anvil/
evm.rs

1use alloy_primitives::Address;
2use foundry_evm::revm::precompile::Precompile;
3use std::{fmt::Debug, sync::Arc};
4
5/// Object-safe trait that enables injecting extra precompiles when using
6/// `anvil` as a library.
7pub trait PrecompileFactory: Send + Sync + Unpin + Debug {
8    /// Returns a set of precompiles to extend the EVM with.
9    fn precompiles(&self) -> Vec<(Address, Precompile)>;
10}
11
12/// Appends a handler register to `evm` that injects the given `precompiles`.
13///
14/// This will add an additional handler that extends the default precompiles with the given set of
15/// precompiles.
16pub fn inject_precompiles<DB: revm::Database, I>(
17    evm: &mut revm::Evm<'_, I, DB>,
18    precompiles: Vec<(Address, Precompile)>,
19) {
20    evm.handler.append_handler_register_box(Box::new(move |handler| {
21        let precompiles = precompiles.clone();
22        let prev = handler.pre_execution.load_precompiles.clone();
23        handler.pre_execution.load_precompiles = Arc::new(move || {
24            let mut cx = prev();
25            cx.extend(precompiles.iter().cloned().map(|(a, b)| (a, b.into())));
26            cx
27        });
28    }));
29}
30
31#[cfg(test)]
32mod tests {
33    use crate::{evm::inject_precompiles, PrecompileFactory};
34    use alloy_primitives::Address;
35    use foundry_evm::revm::primitives::{address, Bytes, Precompile, PrecompileResult, SpecId};
36    use revm::primitives::PrecompileOutput;
37
38    #[test]
39    fn build_evm_with_extra_precompiles() {
40        const PRECOMPILE_ADDR: Address = address!("0x0000000000000000000000000000000000000071");
41
42        fn my_precompile(_bytes: &Bytes, _gas_limit: u64) -> PrecompileResult {
43            Ok(PrecompileOutput { bytes: Bytes::new(), gas_used: 0 })
44        }
45
46        #[derive(Debug)]
47        struct CustomPrecompileFactory;
48
49        impl PrecompileFactory for CustomPrecompileFactory {
50            fn precompiles(&self) -> Vec<(Address, Precompile)> {
51                vec![(PRECOMPILE_ADDR, Precompile::Standard(my_precompile))]
52            }
53        }
54
55        let db = revm::db::EmptyDB::default();
56        let env = Box::<revm::primitives::Env>::default();
57        let spec = SpecId::LATEST;
58        let handler_cfg = revm::primitives::HandlerCfg::new(spec);
59        let inspector = revm::inspectors::NoOpInspector;
60        let context = revm::Context::new(revm::EvmContext::new_with_env(db, env), inspector);
61        let handler = revm::Handler::new(handler_cfg);
62        let mut evm = revm::Evm::new(context, handler);
63        assert!(!evm
64            .handler
65            .pre_execution()
66            .load_precompiles()
67            .addresses()
68            .any(|&addr| addr == PRECOMPILE_ADDR));
69
70        inject_precompiles(&mut evm, CustomPrecompileFactory.precompiles());
71        assert!(evm
72            .handler
73            .pre_execution()
74            .load_precompiles()
75            .addresses()
76            .any(|&addr| addr == PRECOMPILE_ADDR));
77
78        let result = evm.transact().unwrap();
79        assert!(result.result.is_success());
80    }
81}