From 23205d784410dbdf628e022f94eccff6142e7449 Mon Sep 17 00:00:00 2001 From: Gregory Edison Date: Fri, 4 Jul 2025 13:01:22 +0200 Subject: [PATCH 1/3] feat: add base fee params and l2 system contract address Signed-off-by: Gregory Edison --- Cargo.lock | 10 +++----- crates/scroll/alloy/evm/src/tx/compression.rs | 3 ++- crates/scroll/alloy/hardforks/src/hardfork.rs | 4 +-- crates/scroll/chainspec/Cargo.toml | 1 + crates/scroll/chainspec/src/constants.rs | 25 +++++++++++++++++++ crates/scroll/chainspec/src/dev.rs | 13 +++++++--- crates/scroll/chainspec/src/genesis.rs | 18 +++++++++++-- crates/scroll/chainspec/src/lib.rs | 16 +++++++----- crates/scroll/chainspec/src/scroll.rs | 9 +++++-- crates/scroll/chainspec/src/scroll_sepolia.rs | 9 +++++-- crates/scroll/evm/Cargo.toml | 4 ++- crates/scroll/hardforks/src/lib.rs | 4 +-- crates/scroll/payload/Cargo.toml | 2 +- crates/scroll/txpool/Cargo.toml | 7 ------ 14 files changed, 89 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6a580f4a03f..ebab65110cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10159,6 +10159,7 @@ dependencies = [ "alloy-genesis", "alloy-primitives", "alloy-serde", + "auto_impl", "derive_more", "once_cell", "reth-chainspec", @@ -10256,9 +10257,11 @@ dependencies = [ "reth-execution-types", "reth-primitives", "reth-primitives-traits", + "reth-revm", "reth-scroll-chainspec", "reth-scroll-forks", "reth-scroll-primitives", + "reth-storage-api", "revm", "revm-primitives", "revm-scroll", @@ -10351,13 +10354,13 @@ dependencies = [ "reth-payload-util", "reth-primitives-traits", "reth-revm", + "reth-scroll-chainspec", "reth-scroll-engine-primitives", "reth-scroll-evm", "reth-scroll-primitives", "reth-storage-api", "reth-transaction-pool", "revm", - "scroll-alloy-evm", "scroll-alloy-hardforks", "thiserror 2.0.12", "tracing", @@ -10427,15 +10430,10 @@ dependencies = [ "alloy-consensus", "alloy-eips", "alloy-primitives", - "alloy-rpc-types-eth", "c-kzg", "derive_more", - "futures-util", - "metrics", "parking_lot", - "reth-chain-state", "reth-chainspec", - "reth-metrics", "reth-primitives-traits", "reth-provider", "reth-revm", diff --git a/crates/scroll/alloy/evm/src/tx/compression.rs b/crates/scroll/alloy/evm/src/tx/compression.rs index f8f151df777..a898cf2e291 100644 --- a/crates/scroll/alloy/evm/src/tx/compression.rs +++ b/crates/scroll/alloy/evm/src/tx/compression.rs @@ -5,7 +5,6 @@ use alloy_eips::{Encodable2718, Typed2718}; use alloy_evm::{IntoTxEnv, RecoveredTx}; use alloy_primitives::{Address, Bytes, TxKind, U256}; use revm::context::TxEnv; -use revm_scroll::l1block::TX_L1_FEE_PRECISION_U256; use scroll_alloy_consensus::{ScrollTxEnvelope, TxL1Message}; pub use zstd_compression::compute_compression_ratio; @@ -13,6 +12,8 @@ pub use zstd_compression::compute_compression_ratio; mod zstd_compression { use super::*; use std::io::Write; + + use revm_scroll::l1block::TX_L1_FEE_PRECISION_U256; use zstd::{ stream::Encoder, zstd_safe::{CParameter, ParamSwitch}, diff --git a/crates/scroll/alloy/hardforks/src/hardfork.rs b/crates/scroll/alloy/hardforks/src/hardfork.rs index c942c840d4d..4b37b508900 100644 --- a/crates/scroll/alloy/hardforks/src/hardfork.rs +++ b/crates/scroll/alloy/hardforks/src/hardfork.rs @@ -37,7 +37,7 @@ impl ScrollHardfork { (Self::Euclid, ForkCondition::Timestamp(1744815600)), (Self::EuclidV2, ForkCondition::Timestamp(1745305200)), // TODO: update - (Self::Feynman, ForkCondition::Timestamp(u64::MAX)), + (Self::Feynman, ForkCondition::Timestamp(6000000000)), ] } @@ -52,7 +52,7 @@ impl ScrollHardfork { (Self::Euclid, ForkCondition::Timestamp(1741680000)), (Self::EuclidV2, ForkCondition::Timestamp(1741852800)), // TODO: update - (Self::Feynman, ForkCondition::Timestamp(u64::MAX)), + (Self::Feynman, ForkCondition::Timestamp(6000000000)), ] } } diff --git a/crates/scroll/chainspec/Cargo.toml b/crates/scroll/chainspec/Cargo.toml index 5df5dc4f988..60298bef3e0 100644 --- a/crates/scroll/chainspec/Cargo.toml +++ b/crates/scroll/chainspec/Cargo.toml @@ -36,6 +36,7 @@ serde_json = { workspace = true, default-features = false } serde = { workspace = true, default-features = false, features = ["derive"] } # misc +auto_impl.workspace = true derive_more = { workspace = true, default-features = false } once_cell = { workspace = true, default-features = false } diff --git a/crates/scroll/chainspec/src/constants.rs b/crates/scroll/chainspec/src/constants.rs index 458e4ad5163..cc8ac8288b7 100644 --- a/crates/scroll/chainspec/src/constants.rs +++ b/crates/scroll/chainspec/src/constants.rs @@ -1,9 +1,14 @@ use crate::genesis::L1Config; +use alloy_eips::eip1559::BaseFeeParams; use alloy_primitives::{address, b256, Address, B256}; /// The transaction fee recipient on the L2. pub const SCROLL_FEE_VAULT_ADDRESS: Address = address!("5300000000000000000000000000000000000005"); +/// The system contract on L2 mainnet. +pub const SCROLL_MAINNET_SYSTEM_CONTRACT_ADDRESS: Address = + address!("331A873a2a85219863d80d248F9e2978fE88D0Ea"); + /// The L1 message queue address for Scroll mainnet. /// . pub const SCROLL_MAINNET_L1_MESSAGE_QUEUE_ADDRESS: Address = @@ -29,6 +34,10 @@ pub const SCROLL_MAINNET_L1_CONFIG: L1Config = L1Config { pub const SCROLL_MAINNET_GENESIS_HASH: B256 = b256!("bbc05efd412b7cd47a2ed0e5ddfcf87af251e414ea4c801d78b6784513180a80"); +/// The system contract on L2 sepolia. +pub const SCROLL_SEPOLIA_SYSTEM_CONTRACT_ADDRESS: Address = + address!("F444cF06A3E3724e20B35c2989d3942ea8b59124"); + /// The L1 message queue address for Scroll sepolia. /// . pub const SCROLL_SEPOLIA_L1_MESSAGE_QUEUE_ADDRESS: Address = @@ -50,6 +59,10 @@ pub const SCROLL_SEPOLIA_L1_CONFIG: L1Config = L1Config { num_l1_messages_per_block: SCROLL_SEPOLIA_MAX_L1_MESSAGES, }; +/// The system contract on devnet. +pub const SCROLL_DEV_SYSTEM_CONTRACT_ADDRESS: Address = + address!("0000000000000000000000000000000000000000"); + /// The L1 message queue address for Scroll dev. pub const SCROLL_DEV_L1_MESSAGE_QUEUE_ADDRESS: Address = address!("0000000000000000000000000000000000000000"); @@ -72,3 +85,15 @@ pub const SCROLL_DEV_L1_CONFIG: L1Config = L1Config { /// The Scroll Sepolia genesis hash pub const SCROLL_SEPOLIA_GENESIS_HASH: B256 = b256!("aa62d1a8b2bffa9e5d2368b63aae0d98d54928bd713125e3fd9e5c896c68592c"); + +/// The base fee params for Feynman. +pub const SCROLL_BASE_FEE_PARAMS_FEYNMAN: BaseFeeParams = BaseFeeParams::new( + SCROLL_EIP1559_BASE_FEE_MAX_CHANGE_DENOMINATOR_FEYNMAN, + SCROLL_EIP1559_DEFAULT_ELASTICITY_MULTIPLIER_FEYNMAN, +); + +/// The scroll EIP1559 max change denominator for Feynman. +pub const SCROLL_EIP1559_BASE_FEE_MAX_CHANGE_DENOMINATOR_FEYNMAN: u128 = 8; + +/// The scroll EIP1559 default elasticity multiplier for Feynman. +pub const SCROLL_EIP1559_DEFAULT_ELASTICITY_MULTIPLIER_FEYNMAN: u128 = 2; diff --git a/crates/scroll/chainspec/src/dev.rs b/crates/scroll/chainspec/src/dev.rs index 5ee23d174b6..98d3eefe2c1 100644 --- a/crates/scroll/chainspec/src/dev.rs +++ b/crates/scroll/chainspec/src/dev.rs @@ -1,14 +1,17 @@ //! Chain specification in dev mode for custom chain. +use crate::{ + constants::SCROLL_BASE_FEE_PARAMS_FEYNMAN, make_genesis_header, LazyLock, ScrollChainConfig, + ScrollChainSpec, +}; use alloc::sync::Arc; use alloy_chains::Chain; use alloy_primitives::U256; -use reth_chainspec::{BaseFeeParams, BaseFeeParamsKind, ChainSpec}; +use reth_chainspec::{BaseFeeParamsKind, ChainSpec, Hardfork}; use reth_primitives_traits::SealedHeader; use reth_scroll_forks::DEV_HARDFORKS; - -use crate::{make_genesis_header, LazyLock, ScrollChainConfig, ScrollChainSpec}; +use scroll_alloy_hardforks::ScrollHardfork; /// Scroll dev testnet specification /// @@ -24,7 +27,9 @@ pub static SCROLL_DEV: LazyLock> = LazyLock::new(|| { genesis, paris_block_and_final_difficulty: Some((0, U256::from(0))), hardforks: DEV_HARDFORKS.clone(), - base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()), + base_fee_params: BaseFeeParamsKind::Variable( + vec![(ScrollHardfork::Feynman.boxed(), SCROLL_BASE_FEE_PARAMS_FEYNMAN)].into(), + ), ..Default::default() }, config: ScrollChainConfig::dev(), diff --git a/crates/scroll/chainspec/src/genesis.rs b/crates/scroll/chainspec/src/genesis.rs index 96771f00931..1939248b555 100644 --- a/crates/scroll/chainspec/src/genesis.rs +++ b/crates/scroll/chainspec/src/genesis.rs @@ -1,7 +1,11 @@ //! Scroll types for genesis data. use crate::{ - constants::{SCROLL_FEE_VAULT_ADDRESS, SCROLL_MAINNET_L1_CONFIG, SCROLL_SEPOLIA_L1_CONFIG}, + constants::{ + SCROLL_DEV_SYSTEM_CONTRACT_ADDRESS, SCROLL_FEE_VAULT_ADDRESS, SCROLL_MAINNET_L1_CONFIG, + SCROLL_MAINNET_SYSTEM_CONTRACT_ADDRESS, SCROLL_SEPOLIA_L1_CONFIG, + SCROLL_SEPOLIA_SYSTEM_CONTRACT_ADDRESS, + }, SCROLL_DEV_L1_CONFIG, }; use alloy_primitives::Address; @@ -108,6 +112,8 @@ pub struct ScrollChainConfig { /// This is an optional field that, when set, specifies where L2 transaction fees /// will be sent or stored. pub fee_vault_address: Option
, + /// The address of the L2 system contract. + pub l2_system_contract_address: Address, /// The L1 configuration. /// This field encapsulates specific settings and parameters required for L1 pub l1_config: L1Config, @@ -124,6 +130,7 @@ impl ScrollChainConfig { pub const fn mainnet() -> Self { Self { fee_vault_address: Some(SCROLL_FEE_VAULT_ADDRESS), + l2_system_contract_address: SCROLL_MAINNET_SYSTEM_CONTRACT_ADDRESS, l1_config: SCROLL_MAINNET_L1_CONFIG, } } @@ -132,13 +139,18 @@ impl ScrollChainConfig { pub const fn sepolia() -> Self { Self { fee_vault_address: Some(SCROLL_FEE_VAULT_ADDRESS), + l2_system_contract_address: SCROLL_SEPOLIA_SYSTEM_CONTRACT_ADDRESS, l1_config: SCROLL_SEPOLIA_L1_CONFIG, } } /// Returns the [`ScrollChainConfig`] for Scroll dev. pub const fn dev() -> Self { - Self { fee_vault_address: Some(SCROLL_FEE_VAULT_ADDRESS), l1_config: SCROLL_DEV_L1_CONFIG } + Self { + fee_vault_address: Some(SCROLL_FEE_VAULT_ADDRESS), + l2_system_contract_address: SCROLL_DEV_SYSTEM_CONTRACT_ADDRESS, + l1_config: SCROLL_DEV_L1_CONFIG, + } } } @@ -202,6 +214,7 @@ mod tests { "feynmanTime": 100, "scroll": { "feeVaultAddress": "0x5300000000000000000000000000000000000005", + "l2SystemContractAddress: "0x331A873a2a85219863d80d248F9e2978fE88D0Ea", "l1Config": { "l1ChainId": 1, "l1MessageQueueAddress": "0x0d7E906BD9cAFa154b048cFa766Cc1E54E39AF9B", @@ -228,6 +241,7 @@ mod tests { }), scroll_chain_config: ScrollChainConfig { fee_vault_address: Some(address!("5300000000000000000000000000000000000005")), + l2_system_contract_address: SCROLL_MAINNET_SYSTEM_CONTRACT_ADDRESS, l1_config: L1Config { l1_chain_id: 1, l1_message_queue_address: address!("0d7E906BD9cAFa154b048cFa766Cc1E54E39AF9B"), diff --git a/crates/scroll/chainspec/src/lib.rs b/crates/scroll/chainspec/src/lib.rs index 776b6b6cde2..2588fa02e9e 100644 --- a/crates/scroll/chainspec/src/lib.rs +++ b/crates/scroll/chainspec/src/lib.rs @@ -34,12 +34,15 @@ extern crate alloc; mod constants; pub use constants::{ - SCROLL_DEV_L1_CONFIG, SCROLL_DEV_L1_MESSAGE_QUEUE_ADDRESS, SCROLL_DEV_L1_PROXY_ADDRESS, - SCROLL_DEV_MAX_L1_MESSAGES, SCROLL_FEE_VAULT_ADDRESS, SCROLL_MAINNET_GENESIS_HASH, - SCROLL_MAINNET_L1_CONFIG, SCROLL_MAINNET_L1_MESSAGE_QUEUE_ADDRESS, - SCROLL_MAINNET_L1_PROXY_ADDRESS, SCROLL_MAINNET_MAX_L1_MESSAGES, SCROLL_SEPOLIA_GENESIS_HASH, - SCROLL_SEPOLIA_L1_CONFIG, SCROLL_SEPOLIA_L1_MESSAGE_QUEUE_ADDRESS, - SCROLL_SEPOLIA_L1_PROXY_ADDRESS, SCROLL_SEPOLIA_MAX_L1_MESSAGES, + SCROLL_BASE_FEE_PARAMS_FEYNMAN, SCROLL_DEV_L1_CONFIG, SCROLL_DEV_L1_MESSAGE_QUEUE_ADDRESS, + SCROLL_DEV_L1_PROXY_ADDRESS, SCROLL_DEV_MAX_L1_MESSAGES, SCROLL_DEV_SYSTEM_CONTRACT_ADDRESS, + SCROLL_EIP1559_BASE_FEE_MAX_CHANGE_DENOMINATOR_FEYNMAN, + SCROLL_EIP1559_DEFAULT_ELASTICITY_MULTIPLIER_FEYNMAN, SCROLL_FEE_VAULT_ADDRESS, + SCROLL_MAINNET_GENESIS_HASH, SCROLL_MAINNET_L1_CONFIG, SCROLL_MAINNET_L1_MESSAGE_QUEUE_ADDRESS, + SCROLL_MAINNET_L1_PROXY_ADDRESS, SCROLL_MAINNET_MAX_L1_MESSAGES, + SCROLL_MAINNET_SYSTEM_CONTRACT_ADDRESS, SCROLL_SEPOLIA_GENESIS_HASH, SCROLL_SEPOLIA_L1_CONFIG, + SCROLL_SEPOLIA_L1_MESSAGE_QUEUE_ADDRESS, SCROLL_SEPOLIA_L1_PROXY_ADDRESS, + SCROLL_SEPOLIA_MAX_L1_MESSAGES, SCROLL_SEPOLIA_SYSTEM_CONTRACT_ADDRESS, }; mod dev; @@ -180,6 +183,7 @@ impl ScrollChainSpecBuilder { } /// Returns the chain configuration. +#[auto_impl::auto_impl(Arc)] pub trait ChainConfig { /// The configuration. type Config; diff --git a/crates/scroll/chainspec/src/scroll.rs b/crates/scroll/chainspec/src/scroll.rs index 067ca0a5e87..01e97feb98e 100644 --- a/crates/scroll/chainspec/src/scroll.rs +++ b/crates/scroll/chainspec/src/scroll.rs @@ -1,14 +1,16 @@ //! Chain specification for the Scroll Mainnet network. use crate::{ - make_genesis_header, LazyLock, ScrollChainConfig, ScrollChainSpec, SCROLL_MAINNET_GENESIS_HASH, + constants::SCROLL_BASE_FEE_PARAMS_FEYNMAN, make_genesis_header, LazyLock, ScrollChainConfig, + ScrollChainSpec, SCROLL_MAINNET_GENESIS_HASH, }; use alloc::sync::Arc; use alloy_chains::{Chain, NamedChain}; -use reth_chainspec::ChainSpec; +use reth_chainspec::{BaseFeeParamsKind, ChainSpec, Hardfork}; use reth_primitives_traits::SealedHeader; use reth_scroll_forks::SCROLL_MAINNET_HARDFORKS; +use scroll_alloy_hardforks::ScrollHardfork; /// The Scroll Mainnet spec pub static SCROLL_MAINNET: LazyLock> = LazyLock::new(|| { @@ -24,6 +26,9 @@ pub static SCROLL_MAINNET: LazyLock> = LazyLock::new(|| { ), genesis, hardforks: SCROLL_MAINNET_HARDFORKS.clone(), + base_fee_params: BaseFeeParamsKind::Variable( + vec![(ScrollHardfork::Feynman.boxed(), SCROLL_BASE_FEE_PARAMS_FEYNMAN)].into(), + ), ..Default::default() }, config: ScrollChainConfig::mainnet(), diff --git a/crates/scroll/chainspec/src/scroll_sepolia.rs b/crates/scroll/chainspec/src/scroll_sepolia.rs index 74caaf7dd24..3c0c949af0f 100644 --- a/crates/scroll/chainspec/src/scroll_sepolia.rs +++ b/crates/scroll/chainspec/src/scroll_sepolia.rs @@ -1,14 +1,16 @@ //! Chain specification for the Scroll Sepolia testnet network. use crate::{ - make_genesis_header, LazyLock, ScrollChainConfig, ScrollChainSpec, SCROLL_SEPOLIA_GENESIS_HASH, + constants::SCROLL_BASE_FEE_PARAMS_FEYNMAN, make_genesis_header, LazyLock, ScrollChainConfig, + ScrollChainSpec, SCROLL_SEPOLIA_GENESIS_HASH, }; use alloc::sync::Arc; use alloy_chains::{Chain, NamedChain}; -use reth_chainspec::ChainSpec; +use reth_chainspec::{BaseFeeParamsKind, ChainSpec, Hardfork}; use reth_primitives_traits::SealedHeader; use reth_scroll_forks::SCROLL_SEPOLIA_HARDFORKS; +use scroll_alloy_hardforks::ScrollHardfork; /// The Scroll Sepolia spec pub static SCROLL_SEPOLIA: LazyLock> = LazyLock::new(|| { @@ -24,6 +26,9 @@ pub static SCROLL_SEPOLIA: LazyLock> = LazyLock::new(|| { ), genesis, hardforks: SCROLL_SEPOLIA_HARDFORKS.clone(), + base_fee_params: BaseFeeParamsKind::Variable( + vec![(ScrollHardfork::Feynman.boxed(), SCROLL_BASE_FEE_PARAMS_FEYNMAN)].into(), + ), ..Default::default() }, config: ScrollChainConfig::sepolia(), diff --git a/crates/scroll/evm/Cargo.toml b/crates/scroll/evm/Cargo.toml index e7f5d9c840b..74300c3570f 100644 --- a/crates/scroll/evm/Cargo.toml +++ b/crates/scroll/evm/Cargo.toml @@ -18,6 +18,7 @@ reth-evm = { workspace = true, features = ["scroll-alloy-traits"] } reth-execution-types.workspace = true reth-primitives = { workspace = true, features = ["serde-bincode-compat"] } reth-primitives-traits.workspace = true +reth-storage-api.workspace = true # revm revm = { workspace = true, features = ["optional_no_base_fee"] } @@ -46,8 +47,9 @@ thiserror.workspace = true tracing.workspace = true [dev-dependencies] -eyre.workspace = true alloy-primitives = { workspace = true, features = ["getrandom"] } +eyre.workspace = true +reth-revm = { workspace = true, features = ["test-utils"] } [features] default = ["std"] diff --git a/crates/scroll/hardforks/src/lib.rs b/crates/scroll/hardforks/src/lib.rs index 597e4b24635..ddff1f316e2 100644 --- a/crates/scroll/hardforks/src/lib.rs +++ b/crates/scroll/hardforks/src/lib.rs @@ -40,7 +40,7 @@ pub static SCROLL_MAINNET_HARDFORKS: LazyLock = LazyLock::new(|| (ScrollHardfork::Euclid.boxed(), ForkCondition::Timestamp(1744815600)), (ScrollHardfork::EuclidV2.boxed(), ForkCondition::Timestamp(1745305200)), // TODO: update - (ScrollHardfork::Feynman.boxed(), ForkCondition::Timestamp(u64::MAX)), + (ScrollHardfork::Feynman.boxed(), ForkCondition::Timestamp(6000000000)), ]) }); @@ -65,7 +65,7 @@ pub static SCROLL_SEPOLIA_HARDFORKS: LazyLock = LazyLock::new(|| (ScrollHardfork::Euclid.boxed(), ForkCondition::Timestamp(1741680000)), (ScrollHardfork::EuclidV2.boxed(), ForkCondition::Timestamp(1741852800)), // TODO: update - (ScrollHardfork::Feynman.boxed(), ForkCondition::Timestamp(u64::MAX)), + (ScrollHardfork::Feynman.boxed(), ForkCondition::Timestamp(6000000000)), ]) }); diff --git a/crates/scroll/payload/Cargo.toml b/crates/scroll/payload/Cargo.toml index 8cf2e616f42..c3eeb840f0b 100644 --- a/crates/scroll/payload/Cargo.toml +++ b/crates/scroll/payload/Cargo.toml @@ -20,7 +20,6 @@ alloy-primitives.workspace = true # scroll-alloy reth-scroll-evm.workspace = true scroll-alloy-hardforks.workspace = true -scroll-alloy-evm.workspace = true # revm revm.workspace = true @@ -40,6 +39,7 @@ reth-transaction-pool.workspace = true reth-payload-util.workspace = true # scroll +reth-scroll-chainspec.workspace = true reth-scroll-primitives.workspace = true reth-scroll-engine-primitives.workspace = true diff --git a/crates/scroll/txpool/Cargo.toml b/crates/scroll/txpool/Cargo.toml index 844dffe7a5f..38b29b76bd7 100644 --- a/crates/scroll/txpool/Cargo.toml +++ b/crates/scroll/txpool/Cargo.toml @@ -16,10 +16,8 @@ workspace = true alloy-consensus.workspace = true alloy-eips.workspace = true alloy-primitives.workspace = true -alloy-rpc-types-eth.workspace = true # reth -reth-chain-state.workspace = true reth-chainspec.workspace = true reth-primitives-traits.workspace = true reth-revm.workspace = true @@ -37,14 +35,9 @@ reth-scroll-primitives.workspace = true # scroll-alloy scroll-alloy-consensus.workspace = true -# metrics -reth-metrics.workspace = true -metrics.workspace = true - # misc c-kzg.workspace = true derive_more.workspace = true -futures-util.workspace = true parking_lot.workspace = true [dev-dependencies] From a35e0ae5e5b41a7504f1a757102dc4cb244d47c5 Mon Sep 17 00:00:00 2001 From: Gregory Edison Date: Fri, 4 Jul 2025 13:04:47 +0200 Subject: [PATCH 2/3] feat: update base fee to Feynman Signed-off-by: Gregory Edison --- crates/scroll/evm/src/base_fee.rs | 255 +++++++++++++++++++++ crates/scroll/evm/src/lib.rs | 10 +- crates/scroll/payload/src/base_fee.rs | 97 -------- crates/scroll/payload/src/builder.rs | 42 ++-- crates/scroll/payload/src/lib.rs | 6 - crates/storage/storage-api/src/base_fee.rs | 57 +++++ crates/storage/storage-api/src/lib.rs | 3 + 7 files changed, 348 insertions(+), 122 deletions(-) create mode 100644 crates/scroll/evm/src/base_fee.rs delete mode 100644 crates/scroll/payload/src/base_fee.rs create mode 100644 crates/storage/storage-api/src/base_fee.rs diff --git a/crates/scroll/evm/src/base_fee.rs b/crates/scroll/evm/src/base_fee.rs new file mode 100644 index 00000000000..7df1be96dee --- /dev/null +++ b/crates/scroll/evm/src/base_fee.rs @@ -0,0 +1,255 @@ +use alloy_consensus::BlockHeader; +use alloy_eips::calc_next_block_base_fee; +use alloy_primitives::U256; +use reth_chainspec::EthChainSpec; +use reth_scroll_chainspec::{ChainConfig, ScrollChainConfig}; +use reth_storage_api::{BaseFeeProvider, StorageProvider}; +use scroll_alloy_evm::curie::L1_GAS_PRICE_ORACLE_ADDRESS; +use scroll_alloy_hardforks::ScrollHardforks; + +/// L1 gas price oracle base fee slot. +pub const L1_BASE_FEE_SLOT: U256 = U256::from_limbs([1, 0, 0, 0]); + +/// Protocol-enforced maximum L2 base fee. +pub const MAX_L2_BASE_FEE: U256 = U256::from_limbs([10_000_000_000, 0, 0, 0]); + +/// The base fee overhead slot. +const L1_BASE_FEE_OVERHEAD_SLOT: U256 = U256::from_limbs([101, 0, 0, 0]); + +/// The default base fee overhead, in case the L2 system contract isn't deployed or +/// initialized. +pub const DEFAULT_L1_BASE_FEE_OVERHEAD: U256 = U256::from_limbs([15_680_000, 0, 0, 0]); + +/// The base fee scalar slot. +const L1_BASE_FEE_SCALAR_SLOT: U256 = U256::from_limbs([102, 0, 0, 0]); + +/// The default scalar applied on the L1 base fee, in case the L2 system contract isn't deployed or +/// initialized. +pub const DEFAULT_L1_BASE_FEE_SCALAR: U256 = U256::from_limbs([34_000_000_000_000, 0, 0, 0]); + +/// The precision of the L1 base fee. +pub const L1_BASE_FEE_PRECISION: U256 = U256::from_limbs([1_000_000_000_000_000_000, 0, 0, 0]); + +/// The initial base fee. +const INITIAL_BASE_FEE: u64 = 10_000_000; + +/// The Scroll base fee provider implementation. +#[derive(Clone, Debug, Default)] +pub struct ScrollBaseFeeProvider(ChainSpec); + +impl ScrollBaseFeeProvider { + /// Returns a new instance of a [`ScrollBaseFeeProvider`]. + pub const fn new(chain_spec: ChainSpec) -> Self { + Self(chain_spec) + } +} + +impl BaseFeeProvider

for ScrollBaseFeeProvider +where + ChainSpec: EthChainSpec + ScrollHardforks + ChainConfig, + P: StorageProvider, +{ + fn next_block_base_fee( + &self, + provider: &mut P, + parent_header: &H, + ts: u64, + ) -> Result { + let chain_spec = &self.0; + + // load system contract into cache. + let system_contract_address = chain_spec.chain_config().l2_system_contract_address; + // query scalar and overhead. + let (mut scalar, mut overhead) = ( + provider.storage(system_contract_address, L1_BASE_FEE_SCALAR_SLOT)?, + provider.storage(system_contract_address, L1_BASE_FEE_OVERHEAD_SLOT)?, + ); + // if any value is 0, use the default values. + (scalar, overhead) = ( + if scalar == U256::ZERO { DEFAULT_L1_BASE_FEE_SCALAR } else { scalar }, + if overhead == U256::ZERO { DEFAULT_L1_BASE_FEE_OVERHEAD } else { overhead }, + ); + + if chain_spec.is_feynman_active_at_timestamp(ts) { + Ok(feynman_base_fee(chain_spec, parent_header, overhead.saturating_to())) + } else { + let parent_l1_base_fee = + provider.storage(L1_GAS_PRICE_ORACLE_ADDRESS, L1_BASE_FEE_SLOT)?; + Ok(pre_feynman_base_fee(parent_l1_base_fee, scalar, overhead).saturating_to()) + } + } +} + +/// Returns the Feynman base fee. +fn feynman_base_fee( + chainspec: ChainSpec, + parent_header: H, + overhead: u64, +) -> u64 { + let eip_1559_base_fee = if chainspec.is_feynman_active_at_timestamp(parent_header.timestamp()) { + // extract the eip 1559 base fee from parent header by subtracting overhead from it. + let parent_eip_1559_base_fee = + parent_header.base_fee_per_gas().expect("Feynman active").saturating_sub(overhead); + let base_fee_params = chainspec.base_fee_params_at_timestamp(parent_header.timestamp()); + calc_next_block_base_fee( + parent_header.gas_used(), + parent_header.gas_limit(), + parent_eip_1559_base_fee, + base_fee_params, + ) + } else { + // this is the first Feynman block. + // if the parent has a base fee, return it. + if let Some(base_fee) = parent_header.base_fee_per_gas() { + base_fee + } else { + INITIAL_BASE_FEE + } + }; + + eip_1559_base_fee.saturating_add(overhead) +} + +/// Returns the pre Feynman base fee. +fn pre_feynman_base_fee(parent_l1_base_fee: U256, scalar: U256, overhead: U256) -> U256 { + // l1 base fee * scalar / precision + overhead. + let mut base_fee = parent_l1_base_fee * scalar / L1_BASE_FEE_PRECISION + overhead; + + if base_fee > MAX_L2_BASE_FEE { + base_fee = MAX_L2_BASE_FEE; + } + + base_fee +} + +#[cfg(test)] +mod tests { + use super::*; + use std::boxed::Box; + + use alloy_consensus::BlockHeader; + use reth_scroll_chainspec::SCROLL_MAINNET; + use revm::database::{states::plain_account::PlainStorage, EmptyDB, State}; + use scroll_alloy_hardforks::ScrollHardfork; + + const CURIE_PARAMS_TEST_CASES: [(u64, u64); 8] = [ + (0u64, 15680000u64), + (1000000000, 15714000), + (2000000000, 15748000), + (100000000000, 19080000), + (111111111111, 19457777), + (2164000000000, 89256000), + (644149677419355, 10000000000), + (0x1c3c0f442u64, 15937691), + ]; + + const OVERWRITTEN_PARAMS_TEST_CASES: [(u64, u64); 7] = [ + (0, 1), + (1000000000, 1), + (2000000000, 1), + (100000000000, 2), + (111111111111, 2), + (2164000000000, 22), + (644149677419355, 6442), + ]; + + const EIP_1559_TEST_CASES: [(u64, u64, u64, u64); 3] = [ + (1000000000, 20000000, 10000000, 1000000000), // usage == target + (1000000001, 20000000, 9000000, 987500001), // usage below target + (1000000001, 20000000, 11000000, 1012500001), // usage above target + ]; + + const CURIE_TIMESTAMP: u64 = 1719994280; + + #[test] + fn test_should_return_correct_base_fee() -> Result<(), Box> { + // init the state db. + let db = EmptyDB::new(); + let mut state = + State::builder().with_database(db).with_bundle_update().without_state_clear().build(); + + // init the provider and parent header. + let base_fee_provider = ScrollBaseFeeProvider::new(SCROLL_MAINNET.clone()); + let parent_header = + alloy_consensus::Header { timestamp: CURIE_TIMESTAMP, ..Default::default() }; + let parent_header_ts = parent_header.timestamp(); + + // helper closure to insert the l1 base fee in state. + let insert_l1_base_fee = |state: &mut State, l1_base_fee: u64| { + let oracle_storage_pre_fork = + PlainStorage::from_iter([(L1_BASE_FEE_SLOT, U256::from(l1_base_fee))]); + state.insert_account_with_storage( + L1_GAS_PRICE_ORACLE_ADDRESS, + Default::default(), + oracle_storage_pre_fork, + ); + }; + + // for each test case, insert the l1 base fee and check the expected value matches. + for (l1_base_fee, expected_base_fee) in CURIE_PARAMS_TEST_CASES { + insert_l1_base_fee(&mut state, l1_base_fee); + + // fetch base fee from db. + let base_fee = base_fee_provider.next_block_base_fee( + &mut state, + &parent_header, + parent_header_ts + 1, + )?; + assert_eq!(base_fee, expected_base_fee); + } + + // insert the base fee params. + let system_contract_storage = PlainStorage::from_iter([ + (L1_BASE_FEE_SCALAR_SLOT, U256::from(10000000)), + (L1_BASE_FEE_OVERHEAD_SLOT, U256::ONE), + ]); + state.insert_account_with_storage( + SCROLL_MAINNET.config.l2_system_contract_address, + Default::default(), + system_contract_storage, + ); + + // for each test case, insert the l1 base fee and check the expected value matches. + for (l1_base_fee, expected_base_fee) in OVERWRITTEN_PARAMS_TEST_CASES { + insert_l1_base_fee(&mut state, l1_base_fee); + + // fetch base fee from db. + let base_fee = base_fee_provider.next_block_base_fee( + &mut state, + &parent_header, + parent_header_ts + 1, + )?; + assert_eq!(base_fee, expected_base_fee); + } + + // update the parent header used to activate Feynman. + let feynman_fork_ts = SCROLL_MAINNET + .hardforks + .get(ScrollHardfork::Feynman) + .unwrap() + .as_timestamp() + .expect("Feynman is timestamp based forked."); + let mut parent_header = + alloy_consensus::Header { timestamp: feynman_fork_ts + 1, ..Default::default() }; + let parent_header_ts = parent_header.timestamp(); + + // for each test case, update the parent header fields and check the expected value matches. + for (parent_base_fee, parent_gas_limit, parent_gas_used, expected_base_fee) in + EIP_1559_TEST_CASES + { + parent_header.base_fee_per_gas = Some(parent_base_fee); + parent_header.gas_limit = parent_gas_limit; + parent_header.gas_used = parent_gas_used; + + // fetch base fee from db. + let base_fee = base_fee_provider.next_block_base_fee( + &mut state, + &parent_header, + parent_header_ts + 1, + )?; + assert_eq!(base_fee, expected_base_fee); + } + + Ok(()) + } +} diff --git a/crates/scroll/evm/src/lib.rs b/crates/scroll/evm/src/lib.rs index 84b1afca19f..380ae641570 100644 --- a/crates/scroll/evm/src/lib.rs +++ b/crates/scroll/evm/src/lib.rs @@ -8,14 +8,20 @@ mod build; mod config; -pub use execute::{ScrollBlockExecutionInput, ScrollExecutorProvider}; mod execute; +pub use execute::{ScrollBlockExecutionInput, ScrollExecutorProvider}; mod l1; pub use l1::RethL1BlockInfo; -pub use receipt::ScrollRethReceiptBuilder; +mod base_fee; +pub use base_fee::{ + ScrollBaseFeeProvider, DEFAULT_L1_BASE_FEE_OVERHEAD, DEFAULT_L1_BASE_FEE_SCALAR, + L1_BASE_FEE_PRECISION, L1_BASE_FEE_SLOT, MAX_L2_BASE_FEE, +}; + mod receipt; +pub use receipt::ScrollRethReceiptBuilder; use crate::build::ScrollBlockAssembler; use alloc::sync::Arc; diff --git a/crates/scroll/payload/src/base_fee.rs b/crates/scroll/payload/src/base_fee.rs deleted file mode 100644 index 978a89662d5..00000000000 --- a/crates/scroll/payload/src/base_fee.rs +++ /dev/null @@ -1,97 +0,0 @@ -use alloy_primitives::U256; -use revm::{database::State, Database}; -use scroll_alloy_evm::curie::L1_GAS_PRICE_ORACLE_ADDRESS; - -/// L1 gas price oracle base fee slot. -pub const L1_BASE_FEE_SLOT: U256 = U256::from_limbs([1, 0, 0, 0]); - -/// Protocol-enforced maximum L2 base fee. -pub const MAX_L2_BASE_FEE: U256 = U256::from_limbs([10_000_000_000, 0, 0, 0]); - -/// The base fee overhead. -pub const L1_BASE_FEE_OVERHEAD: U256 = U256::from_limbs([15_680_000, 0, 0, 0]); - -/// The scalar applied on the L1 base fee. -pub const L1_BASE_FEE_SCALAR: U256 = U256::from_limbs([34_000_000_000_000, 0, 0, 0]); - -/// The precision of the L1 base fee. -pub const L1_BASE_FEE_PRECISION: U256 = U256::from_limbs([1_000_000_000_000_000_000, 0, 0, 0]); - -/// An instance of the trait can return the current base fee for block building. -pub trait PayloadBuildingBaseFeeProvider { - /// The error type. - type Error; - - /// Returns the base fee for block building. - fn payload_building_base_fee(&mut self) -> Result; -} - -impl PayloadBuildingBaseFeeProvider for State -where - DB: Database, -{ - type Error = DB::Error; - - fn payload_building_base_fee(&mut self) -> Result { - // load account into cache. - let _ = self.load_cache_account(L1_GAS_PRICE_ORACLE_ADDRESS)?; - - // query storage. - let parent_l1_base_fee = self.storage(L1_GAS_PRICE_ORACLE_ADDRESS, L1_BASE_FEE_SLOT)?; - - // l1 base fee * scalar / precision + overhead. - let mut base_fee = - parent_l1_base_fee * L1_BASE_FEE_SCALAR / L1_BASE_FEE_PRECISION + L1_BASE_FEE_OVERHEAD; - - if base_fee > MAX_L2_BASE_FEE { - base_fee = MAX_L2_BASE_FEE; - } - - Ok(base_fee) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use std::boxed::Box; - - use reth_revm::db::states::plain_account::PlainStorage; - use revm::database::EmptyDB; - - #[test] - fn test_should_return_correct_base_fee() -> Result<(), Box> { - let db = EmptyDB::new(); - let mut state = - State::builder().with_database(db).with_bundle_update().without_state_clear().build(); - - let test_values = [ - (0u64, 15680000u64), - (1000000000, 15714000), - (2000000000, 15748000), - (100000000000, 19080000), - (111111111111, 19457777), - (2164000000000, 89256000), - (644149677419355, 10000000000), - (0x1c3c0f442u64, 15937691), - ]; - - for (l1_base_fee, expected_base_fee) in test_values { - // insert the l1 base fee. - let oracle_storage_pre_fork = - PlainStorage::from_iter([(U256::from(1), U256::from(l1_base_fee))]); - state.insert_account_with_storage( - L1_GAS_PRICE_ORACLE_ADDRESS, - Default::default(), - oracle_storage_pre_fork, - ); - - // fetch base fee from db. - let base_fee = state.payload_building_base_fee()?; - let expected_base_fee = U256::from(expected_base_fee); - assert_eq!(base_fee, expected_base_fee); - } - - Ok(()) - } -} diff --git a/crates/scroll/payload/src/builder.rs b/crates/scroll/payload/src/builder.rs index c69d2bd7e19..df9045d0e54 100644 --- a/crates/scroll/payload/src/builder.rs +++ b/crates/scroll/payload/src/builder.rs @@ -1,6 +1,6 @@ //! Scroll's payload builder implementation. -use super::{PayloadBuildingBaseFeeProvider, ScrollPayloadBuilderError}; +use super::ScrollPayloadBuilderError; use crate::config::{PayloadBuildingBreaker, ScrollBuilderConfig}; use alloy_consensus::{Transaction, Typed2718}; @@ -26,10 +26,11 @@ use reth_primitives_traits::{ NodePrimitives, RecoveredBlock, SealedHeader, SignedTransaction, TxTy, }; use reth_revm::{cancelled::CancelOnDrop, database::StateProviderDatabase, db::State}; +use reth_scroll_chainspec::{ChainConfig, ScrollChainConfig}; use reth_scroll_engine_primitives::{ScrollBuiltPayload, ScrollPayloadBuilderAttributes}; -use reth_scroll_evm::ScrollNextBlockEnvAttributes; +use reth_scroll_evm::{ScrollBaseFeeProvider, ScrollNextBlockEnvAttributes}; use reth_scroll_primitives::{ScrollPrimitives, ScrollTransactionSigned}; -use reth_storage_api::{StateProvider, StateProviderFactory}; +use reth_storage_api::{BaseFeeProvider, StateProvider, StateProviderFactory}; use reth_transaction_pool::{BestTransactionsAttributes, PoolTransaction, TransactionPool}; use revm::context::{Block, BlockEnv}; use scroll_alloy_hardforks::ScrollHardforks; @@ -98,7 +99,13 @@ impl ScrollPayloadBuilder { impl ScrollPayloadBuilder where Pool: TransactionPool>, - Client: StateProviderFactory + ChainSpecProvider, + Client: StateProviderFactory + + ChainSpecProvider< + ChainSpec: EthChainSpec + + ScrollHardforks + + ChainConfig + + Clone, + >, Evm: ConfigureEvm, { @@ -146,8 +153,13 @@ where /// Implementation of the [`PayloadBuilder`] trait for [`ScrollPayloadBuilder`]. impl PayloadBuilder for ScrollPayloadBuilder where - Client: - StateProviderFactory + ChainSpecProvider + Clone, + Client: StateProviderFactory + + ChainSpecProvider< + ChainSpec: EthChainSpec + + ScrollHardforks + + ChainConfig + + Clone, + > + Clone, Pool: TransactionPool>, Evm: ConfigureEvm, @@ -224,7 +236,7 @@ impl ScrollBuilder<'_, Txs> { Primitives = ScrollPrimitives, NextBlockEnvCtx = ScrollNextBlockEnvAttributes, >, - ChainSpec: EthChainSpec + ScrollHardforks, + ChainSpec: EthChainSpec + ScrollHardforks + ChainConfig + Clone, Txs: PayloadTransactions>, { let Self { best } = self; @@ -344,7 +356,7 @@ impl ScrollPayloadBuilderCtx where Evm: ConfigureEvm, - ChainSpec: EthChainSpec + ScrollHardforks, + ChainSpec: EthChainSpec + ScrollHardforks + ChainConfig + Clone, { /// Returns the parent block the payload will be build on. #[allow(clippy::missing_const_for_fn)] @@ -382,14 +394,10 @@ where builder_config: &ScrollBuilderConfig, ) -> Result + 'a, PayloadBuilderError> { // get the base fee for the attributes. - let base_fee: u64 = if self.chain_spec.is_curie_active_at_block(self.parent().number + 1) { - db.payload_building_base_fee() - .map_err(|err| PayloadBuilderError::Other(Box::new(err)))? - .try_into() - .expect("base fee limited to 10_000_000_000") - } else { - 0 - }; + let mut base_fee_provider = ScrollBaseFeeProvider::new(self.chain_spec.clone(), &mut *db); + let base_fee: u64 = base_fee_provider + .next_block_base_fee(self.parent().header(), self.attributes().timestamp()) + .map_err(|err| PayloadBuilderError::Other(Box::new(err)))?; self.evm_config .builder_for_next_block( @@ -410,7 +418,7 @@ impl ScrollPayloadBuilderCtx where Evm: ConfigureEvm, - ChainSpec: EthChainSpec + ScrollHardforks, + ChainSpec: EthChainSpec + ScrollHardforks + ChainConfig + Clone, { /// Executes all sequencer transactions that are included in the payload attributes. pub fn execute_sequencer_transactions( diff --git a/crates/scroll/payload/src/lib.rs b/crates/scroll/payload/src/lib.rs index 9e2720e7251..5e3ab637e8b 100644 --- a/crates/scroll/payload/src/lib.rs +++ b/crates/scroll/payload/src/lib.rs @@ -1,11 +1,5 @@ //! Engine Payload related types. -pub use base_fee::{ - PayloadBuildingBaseFeeProvider, L1_BASE_FEE_OVERHEAD, L1_BASE_FEE_PRECISION, - L1_BASE_FEE_SCALAR, L1_BASE_FEE_SLOT, MAX_L2_BASE_FEE, -}; -mod base_fee; - pub mod builder; pub use builder::{ScrollPayloadBuilder, ScrollPayloadTransactions}; diff --git a/crates/storage/storage-api/src/base_fee.rs b/crates/storage/storage-api/src/base_fee.rs new file mode 100644 index 00000000000..35415a61371 --- /dev/null +++ b/crates/storage/storage-api/src/base_fee.rs @@ -0,0 +1,57 @@ +use crate::{StateProvider, StateProviderBox}; + +use alloy_consensus::BlockHeader; +use alloy_primitives::{Address, U256}; +use reth_chainspec::EthChainSpec; +use reth_storage_errors::ProviderError; +use revm_database::{Database, State}; + +/// An instance of the trait can return the base fee for the next block. +pub trait BaseFeeProvider { + /// Returns the base fee for the next block. + fn next_block_base_fee( + &self, + provider: &mut P, + parent_header: &H, + ts: u64, + ) -> Result; +} + +impl BaseFeeProvider

for T { + fn next_block_base_fee( + &self, + _provider: &mut P, + parent_header: &H, + ts: u64, + ) -> Result { + Ok(parent_header + .next_block_base_fee(self.base_fee_params_at_timestamp(ts)) + .unwrap_or_default()) + } +} + +/// A storage provider trait that can be implemented on foreign types. +pub trait StorageProvider { + /// The error type. + type Error; + + /// Returns the storage value at the address for the provided key. + fn storage(&mut self, address: Address, key: U256) -> Result; +} + +impl StorageProvider for State { + type Error = DB::Error; + + fn storage(&mut self, address: Address, key: U256) -> Result { + let _ = self.load_cache_account(address)?; + Database::storage(self, address, key) + } +} + +impl StorageProvider for StateProviderBox { + type Error = ProviderError; + + fn storage(&mut self, address: Address, key: U256) -> Result { + Ok(StateProvider::storage(self, address, key.into())?.unwrap_or_default()) + } +} diff --git a/crates/storage/storage-api/src/lib.rs b/crates/storage/storage-api/src/lib.rs index a82f6092494..08a49b6f4fd 100644 --- a/crates/storage/storage-api/src/lib.rs +++ b/crates/storage/storage-api/src/lib.rs @@ -85,6 +85,9 @@ pub use legacy::*; mod primitives; pub use primitives::*; +mod base_fee; +pub use base_fee::*; + mod block_indices; pub use block_indices::*; From 6506202dd2fd165daa15aca8df29a552973bd1a9 Mon Sep 17 00:00:00 2001 From: Gregory Edison Date: Fri, 4 Jul 2025 23:08:13 +0200 Subject: [PATCH 3/3] feat: propagate the base fee provider in the pool Signed-off-by: Gregory Edison --- crates/ethereum/node/tests/e2e/pool.rs | 7 ++- crates/node/builder/src/components/pool.rs | 7 ++- .../scroll/alloy/rpc-types/src/transaction.rs | 2 + crates/scroll/node/src/builder/pool.rs | 11 +++- crates/scroll/payload/src/builder.rs | 4 +- crates/transaction-pool/src/maintain.rs | 55 +++++++++++++------ examples/custom-node-components/src/main.rs | 1 + 7 files changed, 63 insertions(+), 24 deletions(-) diff --git a/crates/ethereum/node/tests/e2e/pool.rs b/crates/ethereum/node/tests/e2e/pool.rs index 9187cb61405..027b59a7ae9 100644 --- a/crates/ethereum/node/tests/e2e/pool.rs +++ b/crates/ethereum/node/tests/e2e/pool.rs @@ -45,7 +45,7 @@ async fn maintain_txpool_stale_eviction() -> eyre::Result<()> { .build(), ); let node_config = NodeConfig::test() - .with_chain(chain_spec) + .with_chain(chain_spec.clone()) .with_unused_ports() .with_rpc(RpcServerArgs::default().with_unused_ports().with_http()); let NodeHandle { node, node_exit_future: _ } = NodeBuilder::new(node_config.clone()) @@ -67,6 +67,7 @@ async fn maintain_txpool_stale_eviction() -> eyre::Result<()> { "txpool maintenance task", reth_transaction_pool::maintain::maintain_transaction_pool_future( node.inner.provider.clone(), + chain_spec, txpool.clone(), node.inner.provider.clone().canonical_state_stream(), executor.clone(), @@ -120,7 +121,7 @@ async fn maintain_txpool_reorg() -> eyre::Result<()> { ); let genesis_hash = chain_spec.genesis_hash(); let node_config = NodeConfig::test() - .with_chain(chain_spec) + .with_chain(chain_spec.clone()) .with_unused_ports() .with_rpc(RpcServerArgs::default().with_unused_ports().with_http()); let NodeHandle { node, node_exit_future: _ } = NodeBuilder::new(node_config.clone()) @@ -139,6 +140,7 @@ async fn maintain_txpool_reorg() -> eyre::Result<()> { "txpool maintenance task", reth_transaction_pool::maintain::maintain_transaction_pool_future( node.inner.provider.clone(), + chain_spec, txpool.clone(), node.inner.provider.clone().canonical_state_stream(), executor.clone(), @@ -269,6 +271,7 @@ async fn maintain_txpool_commit() -> eyre::Result<()> { "txpool maintenance task", reth_transaction_pool::maintain::maintain_transaction_pool_future( node.inner.provider.clone(), + MAINNET.clone(), txpool.clone(), node.inner.provider.clone().canonical_state_stream(), executor.clone(), diff --git a/crates/node/builder/src/components/pool.rs b/crates/node/builder/src/components/pool.rs index 2d431831ee3..c12de25a3a2 100644 --- a/crates/node/builder/src/components/pool.rs +++ b/crates/node/builder/src/components/pool.rs @@ -1,7 +1,10 @@ //! Pool component for the node builder. +use crate::{BuilderContext, FullNodeTypes}; + use alloy_primitives::Address; use reth_chain_state::CanonStateSubscriptions; +use reth_chainspec::ChainSpecProvider; use reth_node_api::TxTy; use reth_transaction_pool::{ blobstore::DiskFileBlobStore, CoinbaseTipOrdering, PoolConfig, PoolTransaction, SubPoolLimit, @@ -9,8 +12,6 @@ use reth_transaction_pool::{ }; use std::{collections::HashSet, future::Future}; -use crate::{BuilderContext, FullNodeTypes}; - /// A type that knows how to build the transaction pool. pub trait PoolBuilder: Send { /// The transaction pool to build. @@ -236,11 +237,13 @@ where { let chain_events = ctx.provider().canonical_state_stream(); let client = ctx.provider().clone(); + let chain_spec = client.chain_spec(); ctx.task_executor().spawn_critical( "txpool maintenance task", reth_transaction_pool::maintain::maintain_transaction_pool_future( client, + chain_spec, pool, chain_events, ctx.task_executor().clone(), diff --git a/crates/scroll/alloy/rpc-types/src/transaction.rs b/crates/scroll/alloy/rpc-types/src/transaction.rs index 9aa28541527..d4c482d3cd4 100644 --- a/crates/scroll/alloy/rpc-types/src/transaction.rs +++ b/crates/scroll/alloy/rpc-types/src/transaction.rs @@ -35,6 +35,8 @@ impl Transaction { // For l1 messages, we set the `gasPrice` field to 0 in rpc 0 } else { + // TODO: should we get the pool base fee in the case where the transaction is a pending + // transaction here? base_fee .map(|base_fee| { tx.effective_tip_per_gas(base_fee).unwrap_or_default() + base_fee as u128 diff --git a/crates/scroll/node/src/builder/pool.rs b/crates/scroll/node/src/builder/pool.rs index 81e2f921503..bc8034ceb62 100644 --- a/crates/scroll/node/src/builder/pool.rs +++ b/crates/scroll/node/src/builder/pool.rs @@ -1,3 +1,4 @@ +use reth_chainspec::EthChainSpec; use reth_node_api::{FullNodeTypes, NodeTypes}; use reth_node_builder::{ components::{PoolBuilder, PoolBuilderConfigOverrides}, @@ -5,6 +6,8 @@ use reth_node_builder::{ }; use reth_provider::CanonStateSubscriptions; +use reth_scroll_chainspec::{ChainConfig, ScrollChainConfig}; +use reth_scroll_evm::ScrollBaseFeeProvider; use reth_scroll_txpool::{ScrollTransactionPool, ScrollTransactionValidator}; use reth_transaction_pool::{ blobstore::DiskFileBlobStore, CoinbaseTipOrdering, EthPoolTransaction, @@ -44,7 +47,11 @@ impl ScrollPoolBuilder { impl PoolBuilder for ScrollPoolBuilder where - Node: FullNodeTypes>, + Node: FullNodeTypes< + Types: NodeTypes< + ChainSpec: EthChainSpec + ScrollHardforks + ChainConfig, + >, + >, T: EthPoolTransaction>, { type Pool = ScrollTransactionPool; @@ -86,6 +93,7 @@ where let client = ctx.provider().clone(); let transactions_backup_config = reth_transaction_pool::maintain::LocalTransactionBackupConfig::with_local_txs_backup(transactions_path); + let base_fee_provider = ScrollBaseFeeProvider::new(ctx.chain_spec()); ctx.task_executor().spawn_critical_with_graceful_shutdown_signal( "local transactions backup task", @@ -103,6 +111,7 @@ where "txpool maintenance task", reth_transaction_pool::maintain::maintain_transaction_pool_future( client, + base_fee_provider, transaction_pool.clone(), chain_events, ctx.task_executor().clone(), diff --git a/crates/scroll/payload/src/builder.rs b/crates/scroll/payload/src/builder.rs index df9045d0e54..3f41c74d4ba 100644 --- a/crates/scroll/payload/src/builder.rs +++ b/crates/scroll/payload/src/builder.rs @@ -394,9 +394,9 @@ where builder_config: &ScrollBuilderConfig, ) -> Result + 'a, PayloadBuilderError> { // get the base fee for the attributes. - let mut base_fee_provider = ScrollBaseFeeProvider::new(self.chain_spec.clone(), &mut *db); + let base_fee_provider = ScrollBaseFeeProvider::new(self.chain_spec.clone()); let base_fee: u64 = base_fee_provider - .next_block_base_fee(self.parent().header(), self.attributes().timestamp()) + .next_block_base_fee(db, self.parent().header(), self.attributes().timestamp()) .map_err(|err| PayloadBuilderError::Other(Box::new(err)))?; self.evm_config diff --git a/crates/transaction-pool/src/maintain.rs b/crates/transaction-pool/src/maintain.rs index 1c6a4a52a89..777999c34e5 100644 --- a/crates/transaction-pool/src/maintain.rs +++ b/crates/transaction-pool/src/maintain.rs @@ -22,7 +22,10 @@ use reth_fs_util::FsPathError; use reth_primitives_traits::{ transaction::signed::SignedTransaction, NodePrimitives, SealedHeader, }; -use reth_storage_api::{errors::provider::ProviderError, BlockReaderIdExt, StateProviderFactory}; +use reth_storage_api::{ + errors::provider::ProviderError, BaseFeeProvider, BlockReaderIdExt, StateProviderBox, + StateProviderFactory, +}; use reth_tasks::TaskSpawner; use std::{ borrow::Borrow, @@ -91,8 +94,9 @@ impl LocalTransactionBackupConfig { } /// Returns a spawnable future for maintaining the state of the transaction pool. -pub fn maintain_transaction_pool_future( +pub fn maintain_transaction_pool_future( client: Client, + base_fee_provider: BaseFee, pool: P, events: St, task_spawner: Tasks, @@ -101,12 +105,14 @@ pub fn maintain_transaction_pool_future( where N: NodePrimitives, Client: StateProviderFactory + BlockReaderIdExt + ChainSpecProvider + Clone + 'static, + BaseFee: BaseFeeProvider + Send + 'static, P: TransactionPoolExt> + 'static, St: Stream> + Send + Unpin + 'static, Tasks: TaskSpawner + 'static, { async move { - maintain_transaction_pool(client, pool, events, task_spawner, config).await; + maintain_transaction_pool(client, base_fee_provider, pool, events, task_spawner, config) + .await; } .boxed() } @@ -114,8 +120,9 @@ where /// Maintains the state of the transaction pool by handling new blocks and reorgs. /// /// This listens for any new blocks and reorgs and updates the transaction pool's state accordingly -pub async fn maintain_transaction_pool( +pub async fn maintain_transaction_pool( client: Client, + base_fee_provider: BaseFee, pool: P, mut events: St, task_spawner: Tasks, @@ -123,6 +130,7 @@ pub async fn maintain_transaction_pool( ) where N: NodePrimitives, Client: StateProviderFactory + BlockReaderIdExt + ChainSpecProvider + Clone + 'static, + BaseFee: BaseFeeProvider + Send + 'static, P: TransactionPoolExt> + 'static, St: Stream> + Send + Unpin + 'static, Tasks: TaskSpawner + 'static, @@ -133,13 +141,13 @@ pub async fn maintain_transaction_pool( if let Ok(Some(latest)) = client.header_by_number_or_tag(BlockNumberOrTag::Latest) { let latest = SealedHeader::seal_slow(latest); let chain_spec = client.chain_spec(); + let base_fee = pool_pending_base_fee(&client, &base_fee_provider, latest.header()); + let info = BlockInfo { block_gas_limit: latest.gas_limit(), last_seen_block_hash: latest.hash(), last_seen_block_number: latest.number(), - pending_basefee: latest - .next_block_base_fee(chain_spec.base_fee_params_at_timestamp(latest.timestamp())) - .unwrap_or_default(), + pending_basefee: base_fee, pending_blob_fee: latest .maybe_next_block_blob_fee(chain_spec.blob_params_at_timestamp(latest.timestamp())), }; @@ -317,12 +325,9 @@ pub async fn maintain_transaction_pool( let chain_spec = client.chain_spec(); // fees for the next block: `new_tip+1` - let pending_block_base_fee = new_tip - .header() - .next_block_base_fee( - chain_spec.base_fee_params_at_timestamp(new_tip.timestamp()), - ) - .unwrap_or_default(); + let pending_block_base_fee = + pool_pending_base_fee(&client, &base_fee_provider, new_tip.header()); + let pending_block_blob_fee = new_tip.header().maybe_next_block_blob_fee( chain_spec.blob_params_at_timestamp(new_tip.timestamp()), ); @@ -423,10 +428,8 @@ pub async fn maintain_transaction_pool( let chain_spec = client.chain_spec(); // fees for the next block: `tip+1` - let pending_block_base_fee = tip - .header() - .next_block_base_fee(chain_spec.base_fee_params_at_timestamp(tip.timestamp())) - .unwrap_or_default(); + let pending_block_base_fee = + pool_pending_base_fee(&client, &base_fee_provider, tip.header()); let pending_block_blob_fee = tip.header().maybe_next_block_blob_fee( chain_spec.blob_params_at_timestamp(tip.timestamp()), ); @@ -496,6 +499,24 @@ pub async fn maintain_transaction_pool( } } +/// Computes the pending base fee for the pool. +fn pool_pending_base_fee< + Client: StateProviderFactory + BlockReaderIdExt, + BaseFee: BaseFeeProvider, + H: BlockHeader, +>( + client: &Client, + base_fee_provider: &BaseFee, + parent_header: &H, +) -> u64 { + let provider = client.state_by_block_id(parent_header.number().into()); + provider + .and_then(|mut p| { + base_fee_provider.next_block_base_fee(&mut p, &parent_header, parent_header.timestamp()) + }) + .unwrap_or_else(|_| parent_header.base_fee_per_gas().unwrap_or_default()) +} + struct FinalizedBlockTracker { last_finalized_block: Option, } diff --git a/examples/custom-node-components/src/main.rs b/examples/custom-node-components/src/main.rs index b6b8fb3cdf2..c4e571762e8 100644 --- a/examples/custom-node-components/src/main.rs +++ b/examples/custom-node-components/src/main.rs @@ -95,6 +95,7 @@ where "txpool maintenance task", reth_ethereum::pool::maintain::maintain_transaction_pool_future( client, + ctx.chain_spec(), pool, chain_events, ctx.task_executor().clone(),