From 21621ddb0c90f9b0f5cd7796e4959f897b9a0bbb Mon Sep 17 00:00:00 2001 From: Sebastian Miasojed Date: Wed, 18 Feb 2026 13:47:36 +0100 Subject: [PATCH 1/4] Set gasscale --- crates/anvil-polkadot/substrate-runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/anvil-polkadot/substrate-runtime/src/lib.rs b/crates/anvil-polkadot/substrate-runtime/src/lib.rs index c954d13093975..bf683a09c4a9f 100644 --- a/crates/anvil-polkadot/substrate-runtime/src/lib.rs +++ b/crates/anvil-polkadot/substrate-runtime/src/lib.rs @@ -312,7 +312,7 @@ impl pallet_revive::Config for Runtime { type UploadOrigin = EnsureSigned; type InstantiateOrigin = EnsureSigned; type Time = Timestamp; - type GasScale = ConstU32<1>; + type GasScale = ConstU32<100_000>; type FeeInfo = FeeInfo; type DebugEnabled = ConstBool; } From 028e1063f2f9c3a5dc5c4f506a15e265ac39b449 Mon Sep 17 00:00:00 2001 From: Sebastian Miasojed Date: Tue, 10 Mar 2026 14:00:25 +0100 Subject: [PATCH 2/4] Update other gas deps --- .../anvil-polkadot/src/api_server/server.rs | 12 +++++- crates/anvil-polkadot/src/config.rs | 8 ++-- .../src/substrate_node/genesis.rs | 12 ++++-- .../substrate-runtime/src/lib.rs | 15 ++++++- crates/anvil-polkadot/tests/it/gas.rs | 19 ++++++--- .../anvil-polkadot/tests/it/standard_rpc.rs | 40 +++++++++++++++---- 6 files changed, 82 insertions(+), 24 deletions(-) diff --git a/crates/anvil-polkadot/src/api_server/server.rs b/crates/anvil-polkadot/src/api_server/server.rs index 4c9fd2734979b..049466a29ca6c 100644 --- a/crates/anvil-polkadot/src/api_server/server.rs +++ b/crates/anvil-polkadot/src/api_server/server.rs @@ -83,7 +83,10 @@ use polkadot_sdk::{ use revm::primitives::hardfork::SpecId; use sqlx::sqlite::SqlitePoolOptions; use std::{collections::BTreeSet, sync::Arc, time::Duration}; -use substrate_runtime::{Balance, constants::NATIVE_TO_ETH_RATIO}; +use substrate_runtime::{ + Balance, + constants::{GAS_SCALE, NATIVE_TO_ETH_RATIO}, +}; use subxt::{ Metadata as SubxtMetadata, OnlineClient, backend::rpc::RpcClient, client::RuntimeVersion as SubxtRuntimeVersion, config::substrate::H256, @@ -183,7 +186,12 @@ impl ApiServer { // to a 1e12. self.backend.inject_next_fee_multiplier( latest_block, - FixedU128::from_rational(base_fee.to::(), NATIVE_TO_ETH_RATIO.into()), + // evm_base_fee = multiplier × NATIVE_TO_ETH_RATIO × GAS_SCALE + // so multiplier = base_fee / (NATIVE_TO_ETH_RATIO × GAS_SCALE) + FixedU128::from_rational( + base_fee.to::(), + NATIVE_TO_ETH_RATIO as u128 * GAS_SCALE as u128, + ), ); Ok(()).to_rpc_result() } diff --git a/crates/anvil-polkadot/src/config.rs b/crates/anvil-polkadot/src/config.rs index 4d2811535fc6f..7a14e747c4754 100644 --- a/crates/anvil-polkadot/src/config.rs +++ b/crates/anvil-polkadot/src/config.rs @@ -47,9 +47,11 @@ pub const DEFAULT_MNEMONIC: &str = "test test test test test test test test test pub const DEFAULT_IPC_ENDPOINT: &str = if cfg!(unix) { "/tmp/anvil.ipc" } else { r"\\.\pipe\anvil.ipc" }; -/// In anvil this is `1_000_000_000`, in 1e18 denomination. However, -/// asset-hub-westend runtime sets it to `1_000_000`. -pub const INITIAL_BASE_FEE: u128 = 1_000_000; +/// Default initial base fee in wei (1e18 denomination). +/// = NATIVE_TO_ETH_RATIO × GAS_SCALE = 1e6 × 100_000 = 100 gwei (multiplier=1.0). +/// Matches asset-hub-kusama at genesis. Must be above the SlowAdjustingFeeUpdate minimum: +/// min_base_fee = 0.1 × NATIVE_TO_ETH_RATIO × GAS_SCALE = 0.1 × 1e6 × 100_000 = 10_000_000_000. +pub const INITIAL_BASE_FEE: u128 = 100_000_000_000; const BANNER: &str = r" _ _ diff --git a/crates/anvil-polkadot/src/substrate_node/genesis.rs b/crates/anvil-polkadot/src/substrate_node/genesis.rs index 59c3bf51058f9..650353fc4ccbb 100644 --- a/crates/anvil-polkadot/src/substrate_node/genesis.rs +++ b/crates/anvil-polkadot/src/substrate_node/genesis.rs @@ -22,7 +22,10 @@ use polkadot_sdk::{ use serde::{Deserialize, Serialize}; use serde_json::{Value, json}; use std::{collections::BTreeMap, marker::PhantomData, sync::Arc}; -use substrate_runtime::{WASM_BINARY, constants::NATIVE_TO_ETH_RATIO}; +use substrate_runtime::{ + WASM_BINARY, + constants::{GAS_SCALE, NATIVE_TO_ETH_RATIO}, +}; use subxt_signer::eth::Keypair; /// Genesis settings @@ -65,7 +68,9 @@ impl<'a> From<&'a AnvilNodeConfig> for GenesisConfig { .expect("Genesis block number overflow"), base_fee_per_gas: FixedU128::from_rational( anvil_config.get_base_fee(), - NATIVE_TO_ETH_RATIO.into(), + // evm_base_fee = multiplier × NATIVE_TO_ETH_RATIO × GAS_SCALE + // so multiplier = base_fee / (NATIVE_TO_ETH_RATIO × GAS_SCALE) + NATIVE_TO_ETH_RATIO as u128 * GAS_SCALE as u128, ), genesis_accounts: anvil_config.genesis_accounts.clone(), genesis_balance: anvil_config.genesis_balance, @@ -280,7 +285,8 @@ mod tests { let timestamp: u64 = 10; let chain_id: u64 = 42; let authority_id: [u8; 32] = [0xEE; 32]; - let base_fee_per_gas = FixedU128::from_rational(6_000_000, NATIVE_TO_ETH_RATIO.into()); + let base_fee_per_gas = + FixedU128::from_rational(6_000_000, NATIVE_TO_ETH_RATIO as u128 * GAS_SCALE as u128); let genesis_config = GenesisConfig { number: block_number, timestamp, diff --git a/crates/anvil-polkadot/substrate-runtime/src/lib.rs b/crates/anvil-polkadot/substrate-runtime/src/lib.rs index bf683a09c4a9f..6ca99f75c50c5 100644 --- a/crates/anvil-polkadot/substrate-runtime/src/lib.rs +++ b/crates/anvil-polkadot/substrate-runtime/src/lib.rs @@ -7,7 +7,7 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); extern crate alloc; -use crate::sp_runtime::ConsensusEngineId; +use crate::sp_runtime::{ConsensusEngineId, FixedU128}; use alloc::{vec, vec::Vec}; use currency::*; use frame_support::weights::{ @@ -43,8 +43,13 @@ pub use polkadot_sdk::parachains_common::Balance; use sp_weights::ConstantMultiplier; pub mod constants { - /// DOT precision (1e12) to ETH precision (1e18) ratio. + /// KSM precision (1e12) to ETH precision (1e18) ratio. + /// 1 KSM = 10^12 planck, 1 ETH = 10^18 wei → ratio = 10^(18-12) = 10^6. pub const NATIVE_TO_ETH_RATIO: u32 = 1_000_000; + /// Gas scale used by pallet-revive. Must match `type GasScale` in the runtime config. + /// Matches asset-hub-kusama. Formula: weight = gas × GAS_SCALE. + /// With MaxEthExtrinsicWeight=50%: max_gas = 50% × 1.5T / 100_000 = 7_500_000. + pub const GAS_SCALE: u32 = 100_000; } pub mod currency { @@ -283,6 +288,9 @@ impl pallet_transaction_payment::Config for Runtime { parameter_types! { pub CodeHashLockupDepositPercent: Perbill = Perbill::from_percent(30); pub storage ChainId: u64 = 420_420_420; + // Allow ETH extrinsics to consume up to 50% of the max normal extrinsic weight. + // This gives ~15M max gas/tx, matching asset-hub-polkadot and asset-hub-kusama. + pub MaxEthExtrinsicWeight: FixedU128 = FixedU128::from_rational(5, 10); } pub struct BlockAuthor; @@ -312,7 +320,10 @@ impl pallet_revive::Config for Runtime { type UploadOrigin = EnsureSigned; type InstantiateOrigin = EnsureSigned; type Time = Timestamp; + // GasScale=100_000 with MaxEthExtrinsicWeight=50% gives ~7.5M max gas/tx, + // exactly matching asset-hub-kusama production configuration. type GasScale = ConstU32<100_000>; + type MaxEthExtrinsicWeight = MaxEthExtrinsicWeight; type FeeInfo = FeeInfo; type DebugEnabled = ConstBool; } diff --git a/crates/anvil-polkadot/tests/it/gas.rs b/crates/anvil-polkadot/tests/it/gas.rs index f30bdcf7df18a..a5297fcaba4de 100644 --- a/crates/anvil-polkadot/tests/it/gas.rs +++ b/crates/anvil-polkadot/tests/it/gas.rs @@ -12,8 +12,9 @@ use std::ops::Not; #[case(false)] #[case(true)] async fn test_set_next_fee_multiplier(#[case] rpc_driven: bool) { - // 1e18 denomination. - let new_base_fee = U256::from(6_000_000); + // 1e18 denomination. Must be above the SlowAdjustingFeeUpdate minimum (10_000_000_000). + // Minimum = 0.1 × NATIVE_TO_ETH_RATIO × GAS_SCALE = 0.1 × 1e6 × 100_000 = 10_000_000_000. + let new_base_fee = U256::from(300_000_000_000_u128); let anvil_node_config = AnvilNodeConfig::test_config() .with_base_fee(rpc_driven.not().then_some(new_base_fee.to::())); let substrate_node_config = SubstrateNodeConfig::new(&anvil_node_config); @@ -89,13 +90,15 @@ async fn test_set_next_fee_multiplier(#[case] rpc_driven: bool) { let block2_hash = node.block_hash_by_number(2).await.unwrap(); let block2 = node.get_block_by_hash(block2_hash).await; - assert_eq!(U256::from_be_bytes(block2.base_fee_per_gas.to_big_endian()), 5999775); + // Fee decreases slightly for an under-full block (one simple transfer vs 7.5M gas capacity). + assert_eq!(U256::from_be_bytes(block2.base_fee_per_gas.to_big_endian()), 299_988_700_000_u128); } #[tokio::test(flavor = "multi_thread")] async fn test_next_fee_multiplier_minimum() { - // 1e18 denomination. - let new_base_fee = U256::from(50_123); + // 1e18 denomination. Must be a multiple of GAS_SCALE (100_000) for precise representation. + // This value is intentionally below the SlowAdjustingFeeUpdate minimum (10_000_000_000). + let new_base_fee = U256::from(100_000_u64); let anvil_node_config = AnvilNodeConfig::test_config().with_base_fee(Some(new_base_fee.to::())); let substrate_node_config = SubstrateNodeConfig::new(&anvil_node_config); @@ -152,5 +155,9 @@ async fn test_next_fee_multiplier_minimum() { unwrap_response::<()>(node.eth_rpc(EthRequest::Mine(None, None)).await.unwrap()).unwrap(); let block2_hash = node.block_hash_by_number(2).await.unwrap(); let block2 = node.get_block_by_hash(block2_hash).await; - assert_eq!(U256::from_be_bytes(block2.base_fee_per_gas.to_big_endian()), U256::from(100_000)); + // SlowAdjustingFeeUpdate minimum = 0.1 × NATIVE_TO_ETH_RATIO × GAS_SCALE = 0.1 × 1e6 × 100_000 + assert_eq!( + U256::from_be_bytes(block2.base_fee_per_gas.to_big_endian()), + U256::from(10_000_000_000_u128) + ); } diff --git a/crates/anvil-polkadot/tests/it/standard_rpc.rs b/crates/anvil-polkadot/tests/it/standard_rpc.rs index 2b61502de83fc..48ba7be650eae 100644 --- a/crates/anvil-polkadot/tests/it/standard_rpc.rs +++ b/crates/anvil-polkadot/tests/it/standard_rpc.rs @@ -179,7 +179,7 @@ async fn test_gas_price() { let gas_price = unwrap_response::(node.eth_rpc(EthRequest::EthGasPrice(())).await.unwrap()).unwrap(); - assert_eq!(gas_price, U256::from(1000000)); + assert_eq!(gas_price, U256::from(anvil_polkadot::config::INITIAL_BASE_FEE)); } #[tokio::test(flavor = "multi_thread")] @@ -667,13 +667,37 @@ async fn test_fee_history() { ) .unwrap(); assert_eq!(fee_history.gas_used_ratio.len(), 10); - // The `SlowAdjustingFeeUpdate` logic decreases the base_fee block by block if the - // activity contained within them is low. - let base_fees = - [1_000_000, 999981, 999962, 999944, 999925, 999907, 999888, 999869, 999851, 999832, 999832] - .into_iter() - .map(pallet_revive::U256::from) - .collect::>(); + // fee_history returns `block_count + 1` base fees per EIP-1559: one per requested block plus a + // predicted fee for the next block. Here 10 blocks → 11 entries. + // + // Initial fee: multiplier=1.0 × NATIVE_TO_ETH_RATIO × GAS_SCALE = 1.0 × 1e6 × 100_000 = 100 gwei. + // + // The `SlowAdjustingFeeUpdate` (from polkadot-runtime-common) lowers the multiplier each block + // when utilisation is below the 25% target. With a single simple transfer per block (~21k gas + // vs 7.5M capacity = ~0.14% fill), the decrease per block is approximately: + // Δ = fee × (1/52500) × (1 - 0.25) ≈ 100_000_000_000 × 0.000019 ≈ 1_900_000 + // (alternating 1_800_000/1_900_000 due to fixed-point rounding in FixedU128 arithmetic). + // + // The last two entries are equal because of a known pallet-revive limitation: `fee_history` + // returns the current stored `NEXT_FEE_MULTIPLIER` as the "predicted" entry rather than + // recomputing it, so the prediction mirrors the fee of the most recent block. + // See: https://github.com/paritytech/polkadot-sdk/issues/10177 + let base_fees = [ + 100_000_000_000_u128, + 99_998_100_000, + 99_996_200_000, + 99_994_400_000, + 99_992_500_000, + 99_990_700_000, + 99_988_800_000, + 99_986_900_000, + 99_985_100_000, + 99_983_200_000, + 99_983_200_000, + ] + .into_iter() + .map(pallet_revive::U256::from) + .collect::>(); assert_eq!(base_fees, fee_history.base_fee_per_gas); } From 1446c4a03c2c88d64743c1394235a46bce4e3129 Mon Sep 17 00:00:00 2001 From: Sebastian Miasojed Date: Tue, 10 Mar 2026 14:10:35 +0100 Subject: [PATCH 3/4] Fmt --- crates/anvil-polkadot/src/api_server/server.rs | 5 ++++- crates/anvil-polkadot/tests/it/standard_rpc.rs | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/crates/anvil-polkadot/src/api_server/server.rs b/crates/anvil-polkadot/src/api_server/server.rs index 63dbfe6f3fa58..c02bb2d244980 100644 --- a/crates/anvil-polkadot/src/api_server/server.rs +++ b/crates/anvil-polkadot/src/api_server/server.rs @@ -98,7 +98,10 @@ use std::{ }, time::Duration, }; -use substrate_runtime::{Balance, constants::{GAS_SCALE, NATIVE_TO_ETH_RATIO}); +use substrate_runtime::{ + Balance, + constants::{GAS_SCALE, NATIVE_TO_ETH_RATIO}, +}; use subxt::{ Metadata as SubxtMetadata, OnlineClient, backend::rpc::RpcClient, client::RuntimeVersion as SubxtRuntimeVersion, config::substrate::H256, diff --git a/crates/anvil-polkadot/tests/it/standard_rpc.rs b/crates/anvil-polkadot/tests/it/standard_rpc.rs index 16c786dbf26a6..113f62c21b5ba 100644 --- a/crates/anvil-polkadot/tests/it/standard_rpc.rs +++ b/crates/anvil-polkadot/tests/it/standard_rpc.rs @@ -733,7 +733,8 @@ async fn test_fee_history() { // fee_history returns `block_count + 1` base fees per EIP-1559: one per requested block plus a // predicted fee for the next block. Here 10 blocks → 11 entries. // - // Initial fee: multiplier=1.0 × NATIVE_TO_ETH_RATIO × GAS_SCALE = 1.0 × 1e6 × 100_000 = 100 gwei. + // Initial fee: multiplier=1.0 × NATIVE_TO_ETH_RATIO × GAS_SCALE = 1.0 × 1e6 × 100_000 = 100 + // gwei. // // The `SlowAdjustingFeeUpdate` (from polkadot-runtime-common) lowers the multiplier each block // when utilisation is below the 25% target. With a single simple transfer per block (~21k gas From 6c1303ec58732e7c8faa5b73dc2d7cb921ed3455 Mon Sep 17 00:00:00 2001 From: Sebastian Miasojed Date: Fri, 20 Mar 2026 15:13:54 +0100 Subject: [PATCH 4/4] Cleanup descriptions --- crates/anvil-polkadot/tests/it/standard_rpc.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/crates/anvil-polkadot/tests/it/standard_rpc.rs b/crates/anvil-polkadot/tests/it/standard_rpc.rs index 113f62c21b5ba..26dd50bd29789 100644 --- a/crates/anvil-polkadot/tests/it/standard_rpc.rs +++ b/crates/anvil-polkadot/tests/it/standard_rpc.rs @@ -741,11 +741,6 @@ async fn test_fee_history() { // vs 7.5M capacity = ~0.14% fill), the decrease per block is approximately: // Δ = fee × (1/52500) × (1 - 0.25) ≈ 100_000_000_000 × 0.000019 ≈ 1_900_000 // (alternating 1_800_000/1_900_000 due to fixed-point rounding in FixedU128 arithmetic). - // - // The last two entries are equal because of a known pallet-revive limitation: `fee_history` - // returns the current stored `NEXT_FEE_MULTIPLIER` as the "predicted" entry rather than - // recomputing it, so the prediction mirrors the fee of the most recent block. - // See: https://github.com/paritytech/polkadot-sdk/issues/10177 let base_fees = [ 100_000_000_000_u128, 99_998_100_000,