From a1c8f524342c17a722356c68d84a54a8c20c5401 Mon Sep 17 00:00:00 2001 From: zerosnacks Date: Tue, 9 Sep 2025 13:43:14 +0200 Subject: [PATCH 1/5] add eth_config rpc method for anvil --- crates/anvil/core/src/eth/mod.rs | 3 ++ crates/anvil/src/eth/api.rs | 28 +++++++++-- crates/anvil/src/eth/backend/mem/mod.rs | 64 +++++++++++++++++++++++-- crates/anvil/src/evm/celo_precompile.rs | 8 +++- 4 files changed, 96 insertions(+), 7 deletions(-) diff --git a/crates/anvil/core/src/eth/mod.rs b/crates/anvil/core/src/eth/mod.rs index 57e31ed246da9..836e4fd27d3f1 100644 --- a/crates/anvil/core/src/eth/mod.rs +++ b/crates/anvil/core/src/eth/mod.rs @@ -264,6 +264,9 @@ pub enum EthRequest { #[serde(rename = "eth_syncing", with = "empty_params")] EthSyncing(()), + #[serde(rename = "eth_config", with = "empty_params")] + EthConfig(()), + /// geth's `debug_getRawTransaction` endpoint #[serde(rename = "debug_getRawTransaction", with = "sequence")] DebugGetRawTransaction(TxHash), diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 89741775e66ab..4675c1b4f94fb 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -34,7 +34,11 @@ use alloy_consensus::{ transaction::{Recovered, eip4844::TxEip4844Variant}, }; use alloy_dyn_abi::TypedData; -use alloy_eips::eip2718::Encodable2718; +use alloy_eips::{ + eip2718::Encodable2718, + eip7840::BlobParams, + eip7910::{EthConfig, EthForkConfig, SystemContract}, +}; use alloy_evm::overrides::{OverrideBlockHashes, apply_state_overrides}; use alloy_network::{ AnyRpcBlock, AnyRpcTransaction, BlockResponse, Ethereum, NetworkWallet, TransactionBuilder, @@ -93,9 +97,9 @@ use revm::{ context_interface::{block::BlobExcessGasAndPrice, result::Output}, database::CacheDB, interpreter::{InstructionResult, return_ok, return_revert}, - primitives::eip7702::PER_EMPTY_ACCOUNT_COST, + primitives::{eip7702::PER_EMPTY_ACCOUNT_COST, hardfork::SpecId}, }; -use std::{sync::Arc, time::Duration}; +use std::{collections::BTreeMap, sync::Arc, time::Duration}; use tokio::{ sync::mpsc::{UnboundedReceiver, unbounded_channel}, try_join, @@ -313,6 +317,7 @@ impl EthApi { EthRequest::EthGetLogs(filter) => self.logs(filter).await.to_rpc_result(), EthRequest::EthGetWork(_) => self.work().to_rpc_result(), EthRequest::EthSyncing(_) => self.syncing().to_rpc_result(), + EthRequest::EthConfig(_) => self.config().to_rpc_result(), EthRequest::EthSubmitWork(nonce, pow, digest) => { self.submit_work(nonce, pow, digest).to_rpc_result() } @@ -1472,6 +1477,23 @@ impl EthApi { Ok(false) } + pub fn config(&self) -> Result { + node_info!("eth_config"); + + Ok(EthConfig { + current: EthForkConfig { + activation_time: 0, + blob_schedule: self.backend.blob_params(), + chain_id: self.backend.env().read().evm_env.cfg_env.chain_id, + fork_id: Bytes::from_static(b"0x"), + precompiles: self.backend.precompiles(), + system_contracts: self.backend.system_contracts(), + }, + next: None, + last: None, + }) + } + /// Used for submitting a proof-of-work solution. /// /// Handler for ETH RPC call: `eth_submitWork` diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 47bebdaf7480f..2c9ffc1765d81 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -27,7 +27,7 @@ use crate::{ pool::transactions::PoolTransaction, sign::build_typed_transaction, }, - evm::celo_precompile, + evm::celo_precompile::{self, CELO_TRANSFER_ADDRESS}, inject_precompiles, mem::{ inspector::AnvilInspector, @@ -41,7 +41,10 @@ use alloy_consensus::{ proofs::{calculate_receipt_root, calculate_transaction_root}, transaction::Recovered, }; -use alloy_eips::{eip1559::BaseFeeParams, eip4844::kzg_to_versioned_hash, eip7840::BlobParams}; +use alloy_eips::{ + eip1559::BaseFeeParams, eip4844::kzg_to_versioned_hash, eip7840::BlobParams, + eip7910::SystemContract, +}; use alloy_evm::{ Database, Evm, eth::EthEvmContext, @@ -114,8 +117,13 @@ use revm::{ result::{ExecutionResult, Output, ResultAndState}, }, database::{CacheDB, WrapDatabaseRef}, + handler::EthPrecompiles, interpreter::InstructionResult, - precompile::secp256r1::{P256VERIFY, P256VERIFY_BASE_GAS_FEE}, + precompile::{ + PrecompileId, PrecompileSpecId, Precompiles, + secp256r1::{P256VERIFY, P256VERIFY_ADDRESS, P256VERIFY_BASE_GAS_FEE}, + u64_to_address, + }, primitives::{KECCAK_EMPTY, hardfork::SpecId}, state::AccountInfo, }; @@ -843,6 +851,56 @@ impl Backend { self.env.read().is_celo } + /// Returns the precompiles for the current spec. + pub fn precompiles(&self) -> BTreeMap { + let spec_id = self.env.read().evm_env.cfg_env.spec; + let precompiles = Precompiles::new(PrecompileSpecId::from_spec_id(spec_id)); + + let mut precompiles_map = BTreeMap::::default(); + for (address, precompile) in precompiles.inner() { + precompiles_map.insert(precompile.id().name().to_string(), *address); + } + + if self.odyssey { + precompiles_map.insert( + PrecompileId::P256Verify.name().to_string(), + u64_to_address(P256VERIFY_ADDRESS), + ); + } + + if self.is_celo() { + precompiles_map.insert( + celo_precompile::PRECOMPILE_ID_CELO_TRANSFER.clone().name().to_string(), + CELO_TRANSFER_ADDRESS, + ); + } + + if let Some(factory) = &self.precompile_factory { + for (precompile, _) in &factory.precompiles() { + precompiles_map.insert(precompile.id().name().to_string(), *precompile.address()); + } + } + + precompiles_map + } + + /// Returns the system contracts for the current spec. + pub fn system_contracts(&self) -> BTreeMap { + let mut system_contracts = BTreeMap::::default(); + + let spec_id = self.env.read().evm_env.cfg_env.spec; + + if spec_id >= SpecId::CANCUN { + system_contracts.extend(SystemContract::cancun()); + } + + if spec_id >= SpecId::PRAGUE { + system_contracts.extend(SystemContract::prague(None)); + } + + system_contracts + } + /// Returns [`BlobParams`] corresponding to the current spec. pub fn blob_params(&self) -> BlobParams { let spec_id = self.env.read().evm_env.cfg_env.spec; diff --git a/crates/anvil/src/evm/celo_precompile.rs b/crates/anvil/src/evm/celo_precompile.rs index e3bdae226e0b2..9c6545367a958 100644 --- a/crates/anvil/src/evm/celo_precompile.rs +++ b/crates/anvil/src/evm/celo_precompile.rs @@ -11,18 +11,24 @@ //! - to address (32 bytes, left-padded) //! - value (32 bytes, big-endian U256) +use std::borrow::Cow; + use alloy_evm::precompiles::{DynPrecompile, PrecompileInput}; use alloy_primitives::{Address, U256, address}; use revm::precompile::{PrecompileError, PrecompileId, PrecompileOutput, PrecompileResult}; pub const CELO_TRANSFER_ADDRESS: Address = address!("0x00000000000000000000000000000000000000fd"); +/// ID for the [`CheatEcrecover::precompile_id`] precompile. +pub static PRECOMPILE_ID_CELO_TRANSFER: PrecompileId = + PrecompileId::Custom(Cow::Borrowed("celo transfer")); + /// Gas cost for Celo transfer precompile const CELO_TRANSFER_GAS_COST: u64 = 9000; /// Returns the celo native transfer pub fn precompile() -> DynPrecompile { - DynPrecompile::new_stateful(PrecompileId::custom("celo transfer"), celo_transfer_precompile) + DynPrecompile::new_stateful(PRECOMPILE_ID_CELO_TRANSFER.clone(), celo_transfer_precompile) } /// Celo transfer precompile implementation. From 7a484420441d1eef2a1ebafbc2241308e3e7cc19 Mon Sep 17 00:00:00 2001 From: zerosnacks Date: Tue, 9 Sep 2025 14:10:42 +0200 Subject: [PATCH 2/5] comment --- crates/anvil/src/eth/api.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 4675c1b4f94fb..60b403eb678c9 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -1477,15 +1477,23 @@ impl EthApi { Ok(false) } + /// Returns the current configuration of the chain. + /// + /// Note: the activation timestamp is always 0 as the configuration is set at genesis. + /// Note: the `fork_id` is always `0x00000000` as this node does not participate in any forking + /// on the network. + /// Note: the `next` and `last` fields are always `null` as this node does not participate in + /// any forking on the network. + /// + /// Handler for ETH RPC call: `eth_config` pub fn config(&self) -> Result { node_info!("eth_config"); - Ok(EthConfig { current: EthForkConfig { activation_time: 0, blob_schedule: self.backend.blob_params(), chain_id: self.backend.env().read().evm_env.cfg_env.chain_id, - fork_id: Bytes::from_static(b"0x"), + fork_id: Bytes::from_static(b"0x00000000"), precompiles: self.backend.precompiles(), system_contracts: self.backend.system_contracts(), }, From 23f6b6a4d6438be10156955ecce6988e3a515b75 Mon Sep 17 00:00:00 2001 From: zerosnacks Date: Tue, 9 Sep 2025 16:12:11 +0200 Subject: [PATCH 3/5] add test suite --- crates/anvil/src/eth/api.rs | 4 +- crates/anvil/tests/it/fork.rs | 285 +++++++++++++++++++++++++++++++++- 2 files changed, 285 insertions(+), 4 deletions(-) diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 60b403eb678c9..7622e6d411e4c 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -1477,7 +1477,7 @@ impl EthApi { Ok(false) } - /// Returns the current configuration of the chain. + /// Returns the current configuration of the chain. This is useful to /// /// Note: the activation timestamp is always 0 as the configuration is set at genesis. /// Note: the `fork_id` is always `0x00000000` as this node does not participate in any forking @@ -1493,7 +1493,7 @@ impl EthApi { activation_time: 0, blob_schedule: self.backend.blob_params(), chain_id: self.backend.env().read().evm_env.cfg_env.chain_id, - fork_id: Bytes::from_static(b"0x00000000"), + fork_id: Bytes::from_static(&[0; 4]), precompiles: self.backend.precompiles(), system_contracts: self.backend.system_contracts(), }, diff --git a/crates/anvil/tests/it/fork.rs b/crates/anvil/tests/it/fork.rs index 8d2813e2a01a2..b5667eab75e8f 100644 --- a/crates/anvil/tests/it/fork.rs +++ b/crates/anvil/tests/it/fork.rs @@ -5,6 +5,10 @@ use crate::{ utils::{http_provider, http_provider_with_signer}, }; use alloy_chains::NamedChain; +use alloy_eips::{ + eip7840::BlobParams, + eip7910::{EthConfig, SystemContract}, +}; use alloy_network::{EthereumWallet, ReceiptResponse, TransactionBuilder, TransactionResponse}; use alloy_primitives::{Address, Bytes, TxHash, TxKind, U64, U256, address, b256, bytes, uint}; use alloy_provider::Provider; @@ -16,12 +20,19 @@ use alloy_rpc_types::{ }; use alloy_serde::WithOtherFields; use alloy_signer_local::PrivateKeySigner; -use anvil::{NodeConfig, NodeHandle, eth::EthApi, spawn}; +use anvil::{EthereumHardfork, NodeConfig, NodeHandle, PrecompileFactory, eth::EthApi, spawn}; use foundry_common::provider::get_http_provider; use foundry_config::Config; use foundry_test_utils::rpc::{self, next_http_rpc_endpoint, next_rpc_endpoint}; use futures::StreamExt; -use std::{sync::Arc, thread::sleep, time::Duration}; +use revm::precompile::{Precompile, PrecompileId, PrecompileOutput, PrecompileResult}; +use std::{ + borrow::Cow, + collections::{BTreeMap, BTreeSet}, + sync::Arc, + thread::sleep, + time::Duration, +}; const BLOCK_NUMBER: u64 = 14_608_400u64; const DEAD_BALANCE_AT_BLOCK_NUMBER: u128 = 12_556_069_338_441_120_059_867u128; @@ -1607,3 +1618,273 @@ async fn test_fork_get_account() { assert_eq!(alice_acc_init, alice_acc_prev_block); } + +fn assert_hardfork_config( + config: &EthConfig, + expected_blob_params: &BlobParams, + expected_precompiles: &[Address], + expected_system_contracts: &BTreeMap, +) { + assert!(config.next.is_none()); + assert!(config.last.is_none()); + + let current = &config.current; + + assert_eq!(current.activation_time, 0); + assert_eq!(current.chain_id, 31337); + assert_eq!(current.fork_id, Bytes::from(vec![0, 0, 0, 0])); + + assert_eq!(¤t.blob_schedule, expected_blob_params); + + assert_eq!( + current.precompiles.values().copied().collect::>(), + expected_precompiles.iter().copied().collect::>(), + ); + + assert_eq!(current.system_contracts, *expected_system_contracts); +} + +#[tokio::test(flavor = "multi_thread")] +async fn test_config_with_cancun_hardfork() { + let (api, _handle) = + spawn(NodeConfig::test().with_hardfork(Some(EthereumHardfork::Cancun.into()))).await; + + let config = api.config().unwrap(); + + let expected_blob_params = BlobParams { + target_blob_count: 3, + max_blob_count: 6, + update_fraction: 3338477, + min_blob_fee: 1, + max_blobs_per_tx: 6, + blob_base_cost: 0, + }; + + let expected_precompiles = [ + address!("0000000000000000000000000000000000000001"), + address!("0000000000000000000000000000000000000002"), + address!("0000000000000000000000000000000000000003"), + address!("0000000000000000000000000000000000000004"), + address!("0000000000000000000000000000000000000005"), + address!("0000000000000000000000000000000000000006"), + address!("0000000000000000000000000000000000000007"), + address!("0000000000000000000000000000000000000008"), + address!("0000000000000000000000000000000000000009"), + address!("000000000000000000000000000000000000000a"), + ]; + + let expected_system_contracts = BTreeMap::from([( + SystemContract::BeaconRoots, + address!("000f3df6d732807ef1319fb7b8bb8522d0beac02"), + )]); + + assert_hardfork_config( + &config, + &expected_blob_params, + &expected_precompiles, + &expected_system_contracts, + ); +} + +#[tokio::test(flavor = "multi_thread")] +async fn test_config_with_prague_hardfork_with_celo() { + let (api, _handle) = spawn( + NodeConfig::test().with_hardfork(Some(EthereumHardfork::Prague.into())).with_celo(true), + ) + .await; + + let config = api.config().unwrap(); + + let expected_blob_params = BlobParams { + target_blob_count: 6, + max_blob_count: 9, + update_fraction: 5007716, + min_blob_fee: 1, + max_blobs_per_tx: 9, + blob_base_cost: 0, + }; + + let expected_precompiles = [ + address!("0000000000000000000000000000000000000001"), + address!("0000000000000000000000000000000000000002"), + address!("0000000000000000000000000000000000000003"), + address!("0000000000000000000000000000000000000004"), + address!("0000000000000000000000000000000000000005"), + address!("0000000000000000000000000000000000000006"), + address!("0000000000000000000000000000000000000007"), + address!("0000000000000000000000000000000000000008"), + address!("0000000000000000000000000000000000000009"), + address!("000000000000000000000000000000000000000a"), + address!("000000000000000000000000000000000000000b"), + address!("000000000000000000000000000000000000000c"), + address!("000000000000000000000000000000000000000d"), + address!("000000000000000000000000000000000000000e"), + address!("000000000000000000000000000000000000000f"), + address!("0000000000000000000000000000000000000010"), + address!("0000000000000000000000000000000000000011"), + address!("00000000000000000000000000000000000000fd"), // `celo transfer` + ]; + + let expected_system_contracts = BTreeMap::from([ + (SystemContract::BeaconRoots, address!("000f3df6d732807ef1319fb7b8bb8522d0beac02")), + ( + SystemContract::ConsolidationRequestPredeploy, + address!("0000bbddc7ce488642fb579f8b00f3a590007251"), + ), + (SystemContract::DepositContract, address!("00000000219ab540356cbb839cbe05303d7705fa")), + (SystemContract::HistoryStorage, address!("0000f90827f1c53a10cb7a02335b175320002935")), + ( + SystemContract::WithdrawalRequestPredeploy, + address!("00000961ef480eb55e80d19ad83579a64c007002"), + ), + ]); + + assert_hardfork_config( + &config, + &expected_blob_params, + &expected_precompiles, + &expected_system_contracts, + ); +} + +#[tokio::test(flavor = "multi_thread")] +async fn test_config_with_osaka_hardfork() { + let (api, _handle) = + spawn(NodeConfig::test().with_hardfork(Some(EthereumHardfork::Osaka.into()))).await; + + let config = api.config().unwrap(); + + let expected_blob_params = BlobParams { + target_blob_count: 6, + max_blob_count: 9, + update_fraction: 5007716, + min_blob_fee: 1, + max_blobs_per_tx: 6, + blob_base_cost: 8192, + }; + + let expected_precompiles = [ + address!("0000000000000000000000000000000000000001"), + address!("0000000000000000000000000000000000000002"), + address!("0000000000000000000000000000000000000003"), + address!("0000000000000000000000000000000000000004"), + address!("0000000000000000000000000000000000000005"), + address!("0000000000000000000000000000000000000006"), + address!("0000000000000000000000000000000000000007"), + address!("0000000000000000000000000000000000000008"), + address!("0000000000000000000000000000000000000009"), + address!("000000000000000000000000000000000000000a"), + address!("000000000000000000000000000000000000000b"), + address!("000000000000000000000000000000000000000c"), + address!("000000000000000000000000000000000000000d"), + address!("000000000000000000000000000000000000000e"), + address!("000000000000000000000000000000000000000f"), + address!("0000000000000000000000000000000000000010"), + address!("0000000000000000000000000000000000000011"), + address!("0000000000000000000000000000000000000100"), + ]; + + let expected_system_contracts = BTreeMap::from([ + (SystemContract::BeaconRoots, address!("000f3df6d732807ef1319fb7b8bb8522d0beac02")), + ( + SystemContract::ConsolidationRequestPredeploy, + address!("0000bbddc7ce488642fb579f8b00f3a590007251"), + ), + (SystemContract::DepositContract, address!("00000000219ab540356cbb839cbe05303d7705fa")), + (SystemContract::HistoryStorage, address!("0000f90827f1c53a10cb7a02335b175320002935")), + ( + SystemContract::WithdrawalRequestPredeploy, + address!("00000961ef480eb55e80d19ad83579a64c007002"), + ), + ]); + + assert_hardfork_config( + &config, + &expected_blob_params, + &expected_precompiles, + &expected_system_contracts, + ); +} + +#[tokio::test(flavor = "multi_thread")] +async fn test_config_with_osaka_hardfork_with_precompile_factory() { + fn custom_echo_precompile(input: &[u8], _gas_limit: u64) -> PrecompileResult { + Ok(PrecompileOutput { bytes: Bytes::copy_from_slice(input), gas_used: 0, reverted: false }) + } + + #[derive(Debug)] + struct CustomPrecompileFactory; + + impl PrecompileFactory for CustomPrecompileFactory { + fn precompiles(&self) -> Vec<(Precompile, u64)> { + vec![( + Precompile::from(( + PrecompileId::Custom(Cow::Borrowed("custom_echo")), + address!("0x0000000000000000000000000000000000000071"), + custom_echo_precompile as fn(&[u8], u64) -> PrecompileResult, + )), + 1000, + )] + } + } + + let (api, _handle) = spawn( + NodeConfig::test() + .with_hardfork(Some(EthereumHardfork::Osaka.into())) + .with_precompile_factory(CustomPrecompileFactory), + ) + .await; + + let config = api.config().unwrap(); + + let expected_blob_params = BlobParams { + target_blob_count: 6, + max_blob_count: 9, + update_fraction: 5007716, + min_blob_fee: 1, + max_blobs_per_tx: 6, + blob_base_cost: 8192, + }; + + let expected_precompiles = [ + address!("0000000000000000000000000000000000000001"), + address!("0000000000000000000000000000000000000002"), + address!("0000000000000000000000000000000000000003"), + address!("0000000000000000000000000000000000000004"), + address!("0000000000000000000000000000000000000005"), + address!("0000000000000000000000000000000000000006"), + address!("0000000000000000000000000000000000000007"), + address!("0000000000000000000000000000000000000008"), + address!("0000000000000000000000000000000000000009"), + address!("000000000000000000000000000000000000000a"), + address!("000000000000000000000000000000000000000b"), + address!("000000000000000000000000000000000000000c"), + address!("000000000000000000000000000000000000000d"), + address!("000000000000000000000000000000000000000e"), + address!("000000000000000000000000000000000000000f"), + address!("0000000000000000000000000000000000000010"), + address!("0000000000000000000000000000000000000011"), + address!("0000000000000000000000000000000000000071"), // `custom_echo` + address!("0000000000000000000000000000000000000100"), + ]; + let expected_system_contracts = BTreeMap::from([ + (SystemContract::BeaconRoots, address!("000f3df6d732807ef1319fb7b8bb8522d0beac02")), + ( + SystemContract::ConsolidationRequestPredeploy, + address!("0000bbddc7ce488642fb579f8b00f3a590007251"), + ), + (SystemContract::DepositContract, address!("00000000219ab540356cbb839cbe05303d7705fa")), + (SystemContract::HistoryStorage, address!("0000f90827f1c53a10cb7a02335b175320002935")), + ( + SystemContract::WithdrawalRequestPredeploy, + address!("00000961ef480eb55e80d19ad83579a64c007002"), + ), + ]); + + assert_hardfork_config( + &config, + &expected_blob_params, + &expected_precompiles, + &expected_system_contracts, + ); +} From ad02f1c5b566f73078374244c7ccecbfb8b45a38 Mon Sep 17 00:00:00 2001 From: zerosnacks Date: Tue, 9 Sep 2025 16:22:04 +0200 Subject: [PATCH 4/5] nits --- crates/anvil/src/eth/api.rs | 10 +++++----- crates/anvil/tests/it/fork.rs | 4 ++++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 7622e6d411e4c..deeab711981c4 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -36,8 +36,7 @@ use alloy_consensus::{ use alloy_dyn_abi::TypedData; use alloy_eips::{ eip2718::Encodable2718, - eip7840::BlobParams, - eip7910::{EthConfig, EthForkConfig, SystemContract}, + eip7910::{EthConfig, EthForkConfig}, }; use alloy_evm::overrides::{OverrideBlockHashes, apply_state_overrides}; use alloy_network::{ @@ -97,9 +96,9 @@ use revm::{ context_interface::{block::BlobExcessGasAndPrice, result::Output}, database::CacheDB, interpreter::{InstructionResult, return_ok, return_revert}, - primitives::{eip7702::PER_EMPTY_ACCOUNT_COST, hardfork::SpecId}, + primitives::eip7702::PER_EMPTY_ACCOUNT_COST, }; -use std::{collections::BTreeMap, sync::Arc, time::Duration}; +use std::{sync::Arc, time::Duration}; use tokio::{ sync::mpsc::{UnboundedReceiver, unbounded_channel}, try_join, @@ -1477,7 +1476,8 @@ impl EthApi { Ok(false) } - /// Returns the current configuration of the chain. This is useful to + /// Returns the current configuration of the chain. + /// This is useful for finding out what precompiles and system contracts are available. /// /// Note: the activation timestamp is always 0 as the configuration is set at genesis. /// Note: the `fork_id` is always `0x00000000` as this node does not participate in any forking diff --git a/crates/anvil/tests/it/fork.rs b/crates/anvil/tests/it/fork.rs index b5667eab75e8f..da5274d94f0f5 100644 --- a/crates/anvil/tests/it/fork.rs +++ b/crates/anvil/tests/it/fork.rs @@ -1660,6 +1660,7 @@ async fn test_config_with_cancun_hardfork() { blob_base_cost: 0, }; + // <= Cancun precompiles let expected_precompiles = [ address!("0000000000000000000000000000000000000001"), address!("0000000000000000000000000000000000000002"), @@ -1704,6 +1705,7 @@ async fn test_config_with_prague_hardfork_with_celo() { blob_base_cost: 0, }; + // <= Prague + Celo precompiles let expected_precompiles = [ address!("0000000000000000000000000000000000000001"), address!("0000000000000000000000000000000000000002"), @@ -1763,6 +1765,7 @@ async fn test_config_with_osaka_hardfork() { blob_base_cost: 8192, }; + // <= Osaka precompiles let expected_precompiles = [ address!("0000000000000000000000000000000000000001"), address!("0000000000000000000000000000000000000002"), @@ -1846,6 +1849,7 @@ async fn test_config_with_osaka_hardfork_with_precompile_factory() { blob_base_cost: 8192, }; + // <= Osaka precompiles + custom precompile let expected_precompiles = [ address!("0000000000000000000000000000000000000001"), address!("0000000000000000000000000000000000000002"), From c74d665bcfa5af8e809988c466e6d86d161a2135 Mon Sep 17 00:00:00 2001 From: zerosnacks Date: Tue, 9 Sep 2025 16:44:14 +0200 Subject: [PATCH 5/5] fix clippy & docs --- crates/anvil/src/eth/backend/mem/mod.rs | 1 - crates/anvil/src/evm/celo_precompile.rs | 7 ++++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 2c9ffc1765d81..9b9f067ffb3fa 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -117,7 +117,6 @@ use revm::{ result::{ExecutionResult, Output, ResultAndState}, }, database::{CacheDB, WrapDatabaseRef}, - handler::EthPrecompiles, interpreter::InstructionResult, precompile::{ PrecompileId, PrecompileSpecId, Precompiles, diff --git a/crates/anvil/src/evm/celo_precompile.rs b/crates/anvil/src/evm/celo_precompile.rs index 9c6545367a958..6744aea8b701f 100644 --- a/crates/anvil/src/evm/celo_precompile.rs +++ b/crates/anvil/src/evm/celo_precompile.rs @@ -17,16 +17,17 @@ use alloy_evm::precompiles::{DynPrecompile, PrecompileInput}; use alloy_primitives::{Address, U256, address}; use revm::precompile::{PrecompileError, PrecompileId, PrecompileOutput, PrecompileResult}; +/// Address of the Celo transfer precompile. pub const CELO_TRANSFER_ADDRESS: Address = address!("0x00000000000000000000000000000000000000fd"); -/// ID for the [`CheatEcrecover::precompile_id`] precompile. +/// ID for the [Celo transfer precompile](CELO_TRANSFER_ADDRESS). pub static PRECOMPILE_ID_CELO_TRANSFER: PrecompileId = PrecompileId::Custom(Cow::Borrowed("celo transfer")); -/// Gas cost for Celo transfer precompile +/// Gas cost for Celo transfer precompile. const CELO_TRANSFER_GAS_COST: u64 = 9000; -/// Returns the celo native transfer +/// Returns the Celo native transfer. pub fn precompile() -> DynPrecompile { DynPrecompile::new_stateful(PRECOMPILE_ID_CELO_TRANSFER.clone(), celo_transfer_precompile) }