Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions crates/anvil-polkadot/src/api_server/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,10 @@ use std::{
},
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,
Expand Down Expand Up @@ -282,7 +285,12 @@ impl ApiServer {
// to a 1e12.
self.backend.inject_next_fee_multiplier(
latest_block,
FixedU128::from_rational(base_fee.to::<u128>(), 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::<u128>(),
NATIVE_TO_ETH_RATIO as u128 * GAS_SCALE as u128,
),
);
Ok(()).to_rpc_result()
}
Expand Down
8 changes: 5 additions & 3 deletions crates/anvil-polkadot/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"
_ _
Expand Down
12 changes: 9 additions & 3 deletions crates/anvil-polkadot/src/substrate_node/genesis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
17 changes: 14 additions & 3 deletions crates/anvil-polkadot/substrate-runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -289,6 +294,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;
Expand Down Expand Up @@ -318,7 +326,10 @@ impl pallet_revive::Config for Runtime {
type UploadOrigin = EnsureSigned<Self::AccountId>;
type InstantiateOrigin = EnsureSigned<Self::AccountId>;
type Time = Timestamp;
type GasScale = ConstU32<1>;
// GasScale=100_000 with MaxEthExtrinsicWeight=50% gives ~7.5M max gas/tx,
// exactly matching asset-hub-kusama production configuration.
type GasScale = ConstU32<100_000>;
Comment thread
re-gius marked this conversation as resolved.
type MaxEthExtrinsicWeight = MaxEthExtrinsicWeight;
type FeeInfo = FeeInfo<Address, Signature, EthExtraImpl>;
type DebugEnabled = ConstBool<true>;
}
Expand Down
19 changes: 13 additions & 6 deletions crates/anvil-polkadot/tests/it/gas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<u64>()));
let substrate_node_config = SubstrateNodeConfig::new(&anvil_node_config);
Expand Down Expand Up @@ -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::<u64>()));
let substrate_node_config = SubstrateNodeConfig::new(&anvil_node_config);
Expand Down Expand Up @@ -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)
);
}
36 changes: 28 additions & 8 deletions crates/anvil-polkadot/tests/it/standard_rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ async fn test_gas_price() {

let gas_price =
unwrap_response::<U256>(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")]
Expand Down Expand Up @@ -730,13 +730,33 @@ 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::<Vec<_>>();
// 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).
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::<Vec<_>>();
assert_eq!(base_fees, fee_history.base_fee_per_gas);
}

Expand Down
Loading