Skip to content

Commit cba81a6

Browse files
mattsseclaude
andauthored
refactor(anvil)!: simplify PrecompileFactory API to use DynPrecompile (#12411)
* refactor(anvil)!: simplify PrecompileFactory API to use DynPrecompile Changed PrecompileFactory::precompiles() return type from Vec<(Precompile, u64)> to Vec<(Address, DynPrecompile)> for a cleaner and more flexible API. The previous API required wrapping precompile functions in the Precompile type with gas limits. The new API directly uses DynPrecompile, which is more straightforward and allows users to create custom precompiles with arbitrary logic without needing to understand the legacy Precompile wrapper. Updated inject_custom_precompiles to accept the new type and simplified the injection logic to directly apply DynPrecompiles without additional wrapping. Updated all tests and implementations to use the new API with proper type annotations for closure parameters. BREAKING CHANGE: PrecompileFactory::precompiles() now returns Vec<(Address, DynPrecompile)> instead of Vec<(Precompile, u64)> Co-Authored-By: Claude <[email protected]> * touchup --------- Co-authored-by: Claude <[email protected]>
1 parent cb34166 commit cba81a6

File tree

3 files changed

+37
-45
lines changed

3 files changed

+37
-45
lines changed

crates/anvil/src/eth/backend/mem/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -813,8 +813,8 @@ impl Backend {
813813
precompiles_map.extend(self.env.read().networks.precompiles());
814814

815815
if let Some(factory) = &self.precompile_factory {
816-
for (precompile, _) in &factory.precompiles() {
817-
precompiles_map.insert(precompile.id().name().to_string(), *precompile.address());
816+
for (address, precompile) in factory.precompiles() {
817+
precompiles_map.insert(precompile.precompile_id().to_string(), address);
818818
}
819819
}
820820

crates/anvil/src/evm.rs

Lines changed: 24 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,44 @@
11
use alloy_evm::{
22
Database, Evm,
33
eth::EthEvmContext,
4-
precompiles::{DynPrecompile, PrecompileInput, PrecompilesMap},
4+
precompiles::{DynPrecompile, PrecompilesMap},
55
};
6-
6+
use alloy_primitives::Address;
77
use foundry_evm::core::either_evm::EitherEvm;
88
use op_revm::OpContext;
9-
use revm::{Inspector, precompile::Precompile};
9+
use revm::Inspector;
1010
use std::fmt::Debug;
1111

1212
/// Object-safe trait that enables injecting extra precompiles when using
1313
/// `anvil` as a library.
1414
pub trait PrecompileFactory: Send + Sync + Unpin + Debug {
1515
/// Returns a set of precompiles to extend the EVM with.
16-
fn precompiles(&self) -> Vec<(Precompile, u64)>;
16+
fn precompiles(&self) -> Vec<(Address, DynPrecompile)>;
1717
}
1818

1919
/// Inject custom precompiles into the EVM dynamically.
2020
pub fn inject_custom_precompiles<DB, I>(
2121
evm: &mut EitherEvm<DB, I, PrecompilesMap>,
22-
precompiles: Vec<(Precompile, u64)>,
22+
precompiles: Vec<(Address, DynPrecompile)>,
2323
) where
2424
DB: Database,
2525
I: Inspector<EthEvmContext<DB>> + Inspector<OpContext<DB>>,
2626
{
27-
for (precompile, gas) in precompiles {
28-
let addr = *precompile.address();
29-
let func = *precompile.precompile();
30-
evm.precompiles_mut().apply_precompile(&addr, move |_| {
31-
Some(DynPrecompile::from(move |input: PrecompileInput<'_>| func(input.data, gas)))
32-
});
27+
for (addr, precompile) in precompiles {
28+
evm.precompiles_mut().apply_precompile(&addr, move |_| Some(precompile));
3329
}
3430
}
3531

3632
#[cfg(test)]
3733
mod tests {
38-
use std::{borrow::Cow, convert::Infallible};
34+
use std::convert::Infallible;
3935

4036
use crate::{PrecompileFactory, inject_custom_precompiles};
41-
use alloy_evm::{EthEvm, Evm, EvmEnv, eth::EthEvmContext, precompiles::PrecompilesMap};
37+
use alloy_evm::{
38+
EthEvm, Evm, EvmEnv,
39+
eth::EthEvmContext,
40+
precompiles::{DynPrecompile, PrecompilesMap},
41+
};
4242
use alloy_op_evm::OpEvm;
4343
use alloy_primitives::{Address, Bytes, TxKind, U256, address};
4444
use foundry_evm::core::either_evm::EitherEvm;
@@ -52,10 +52,7 @@ mod tests {
5252
handler::{EthPrecompiles, instructions::EthInstructions},
5353
inspector::NoOpInspector,
5454
interpreter::interpreter::EthInterpreter,
55-
precompile::{
56-
Precompile, PrecompileId, PrecompileOutput, PrecompileResult, PrecompileSpecId,
57-
Precompiles,
58-
},
55+
precompile::{PrecompileOutput, PrecompileSpecId, Precompiles},
5956
primitives::hardfork::SpecId,
6057
};
6158

@@ -73,24 +70,21 @@ mod tests {
7370
struct CustomPrecompileFactory;
7471

7572
impl PrecompileFactory for CustomPrecompileFactory {
76-
fn precompiles(&self) -> Vec<(Precompile, u64)> {
73+
fn precompiles(&self) -> Vec<(Address, DynPrecompile)> {
74+
use alloy_evm::precompiles::PrecompileInput;
7775
vec![(
78-
Precompile::from((
79-
PrecompileId::Custom(Cow::Borrowed("custom_echo")),
80-
PRECOMPILE_ADDR,
81-
custom_echo_precompile as fn(&[u8], u64) -> PrecompileResult,
82-
)),
83-
1000,
76+
PRECOMPILE_ADDR,
77+
DynPrecompile::from(|input: PrecompileInput<'_>| {
78+
Ok(PrecompileOutput {
79+
bytes: Bytes::copy_from_slice(input.data),
80+
gas_used: 0,
81+
reverted: false,
82+
})
83+
}),
8484
)]
8585
}
8686
}
8787

88-
/// Custom precompile that echoes the input data.
89-
/// In this example it uses `0xdeadbeef` as the input data, returning it as output.
90-
fn custom_echo_precompile(input: &[u8], _gas_limit: u64) -> PrecompileResult {
91-
Ok(PrecompileOutput { bytes: Bytes::copy_from_slice(input), gas_used: 0, reverted: false })
92-
}
93-
9488
/// Creates a new EVM instance with the custom precompile factory.
9589
fn create_eth_evm(
9690
spec: SpecId,

crates/anvil/tests/it/fork.rs

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,7 @@ use foundry_config::Config;
2626
use foundry_evm_networks::NetworkConfigs;
2727
use foundry_test_utils::rpc::{self, next_http_rpc_endpoint, next_rpc_endpoint};
2828
use futures::StreamExt;
29-
use revm::precompile::{Precompile, PrecompileId, PrecompileOutput, PrecompileResult};
3029
use std::{
31-
borrow::Cow,
3230
collections::{BTreeMap, BTreeSet},
3331
sync::Arc,
3432
thread::sleep,
@@ -1868,22 +1866,22 @@ async fn test_config_with_osaka_hardfork() {
18681866

18691867
#[tokio::test(flavor = "multi_thread")]
18701868
async fn test_config_with_osaka_hardfork_with_precompile_factory() {
1871-
fn custom_echo_precompile(input: &[u8], _gas_limit: u64) -> PrecompileResult {
1872-
Ok(PrecompileOutput { bytes: Bytes::copy_from_slice(input), gas_used: 0, reverted: false })
1873-
}
1874-
18751869
#[derive(Debug)]
18761870
struct CustomPrecompileFactory;
18771871

18781872
impl PrecompileFactory for CustomPrecompileFactory {
1879-
fn precompiles(&self) -> Vec<(Precompile, u64)> {
1873+
fn precompiles(&self) -> Vec<(Address, alloy_evm::precompiles::DynPrecompile)> {
18801874
vec![(
1881-
Precompile::from((
1882-
PrecompileId::Custom(Cow::Borrowed("custom_echo")),
1883-
address!("0x0000000000000000000000000000000000000071"),
1884-
custom_echo_precompile as fn(&[u8], u64) -> PrecompileResult,
1885-
)),
1886-
1000,
1875+
address!("0x0000000000000000000000000000000000000071"),
1876+
alloy_evm::precompiles::DynPrecompile::from(
1877+
|input: alloy_evm::precompiles::PrecompileInput<'_>| {
1878+
Ok(revm::precompile::PrecompileOutput {
1879+
bytes: Bytes::copy_from_slice(input.data),
1880+
gas_used: 0,
1881+
reverted: false,
1882+
})
1883+
},
1884+
),
18871885
)]
18881886
}
18891887
}

0 commit comments

Comments
 (0)