Skip to main content

anvil/evm/
mod.rs

1use alloy_evm::precompiles::DynPrecompile;
2use alloy_primitives::Address;
3use std::fmt::Debug;
4
5#[cfg(feature = "optimism")]
6mod optimism;
7
8/// Object-safe trait that enables injecting extra precompiles when using
9/// `anvil` as a library.
10pub trait PrecompileFactory: Send + Sync + Unpin + Debug {
11    /// Returns a set of precompiles to extend the EVM with.
12    fn precompiles(&self) -> Vec<(Address, DynPrecompile)>;
13}
14
15#[cfg(test)]
16mod tests {
17    use std::convert::Infallible;
18
19    use crate::PrecompileFactory;
20    use alloy_evm::{
21        EthEvm, Evm,
22        eth::EthEvmContext,
23        precompiles::{DynPrecompile, PrecompilesMap},
24    };
25    use alloy_primitives::{Address, Bytes, TxKind, address};
26    use itertools::Itertools;
27    use revm::{
28        Journal,
29        context::{BlockEnv, CfgEnv, Evm as RevmEvm, JournalTr, LocalContext, TxEnv},
30        database::{EmptyDB, EmptyDBTyped},
31        handler::{EthPrecompiles, instructions::EthInstructions},
32        inspector::NoOpInspector,
33        interpreter::interpreter::EthInterpreter,
34        precompile::{PrecompileOutput, PrecompileSpecId, PrecompileStatus, Precompiles},
35        primitives::hardfork::SpecId,
36    };
37
38    // A precompile activated in the `Prague` spec (BLS12-381 G2 map).
39    pub(super) const ETH_PRAGUE_PRECOMPILE: Address =
40        address!("0x0000000000000000000000000000000000000011");
41
42    // A precompile activated in the `Osaka` spec (EIP-7951).
43    const ETH_OSAKA_PRECOMPILE: Address = address!("0x0000000000000000000000000000000000000100");
44
45    // A custom precompile address and payload for testing.
46    pub(super) const PRECOMPILE_ADDR: Address =
47        address!("0x0000000000000000000000000000000000000071");
48    pub(super) const PAYLOAD: &[u8] = &[0xde, 0xad, 0xbe, 0xef];
49
50    #[derive(Debug)]
51    pub(super) struct CustomPrecompileFactory;
52
53    impl PrecompileFactory for CustomPrecompileFactory {
54        fn precompiles(&self) -> Vec<(Address, DynPrecompile)> {
55            use alloy_evm::precompiles::PrecompileInput;
56            vec![(
57                PRECOMPILE_ADDR,
58                DynPrecompile::from(|input: PrecompileInput<'_>| {
59                    Ok(PrecompileOutput {
60                        status: PrecompileStatus::Success,
61                        bytes: Bytes::copy_from_slice(input.data),
62                        gas_used: 0,
63                        gas_refunded: 0,
64                        state_gas_used: 0,
65                        reservoir: input.reservoir,
66                    })
67                }),
68            )]
69        }
70    }
71
72    /// Creates a new Eth EVM instance.
73    fn create_eth_evm(
74        spec: SpecId,
75    ) -> (TxEnv, EthEvm<EmptyDBTyped<Infallible>, NoOpInspector, PrecompilesMap>) {
76        let tx_env = TxEnv {
77            kind: TxKind::Call(PRECOMPILE_ADDR),
78            data: PAYLOAD.into(),
79            ..Default::default()
80        };
81
82        let eth_evm_context = EthEvmContext {
83            journaled_state: Journal::new(EmptyDB::default()),
84            block: BlockEnv::default(),
85            cfg: CfgEnv::new_with_spec(spec),
86            tx: tx_env.clone(),
87            chain: (),
88            local: LocalContext::default(),
89            error: Ok(()),
90        };
91
92        let eth_precompiles = EthPrecompiles {
93            precompiles: Precompiles::new(PrecompileSpecId::from_spec_id(spec)),
94            spec,
95        }
96        .precompiles;
97        let eth_evm = EthEvm::new(
98            RevmEvm::new_with_inspector(
99                eth_evm_context,
100                NoOpInspector,
101                EthInstructions::<EthInterpreter, EthEvmContext<EmptyDB>>::new_mainnet_with_spec(
102                    spec,
103                ),
104                PrecompilesMap::from_static(eth_precompiles),
105            ),
106            true,
107        );
108
109        (tx_env, eth_evm)
110    }
111
112    #[test]
113    fn build_eth_evm_with_extra_precompiles_osaka_spec() {
114        let (tx_env, mut evm) = create_eth_evm(SpecId::OSAKA);
115
116        assert!(evm.precompiles().addresses().contains(&ETH_OSAKA_PRECOMPILE));
117        assert!(evm.precompiles().addresses().contains(&ETH_PRAGUE_PRECOMPILE));
118        assert!(!evm.precompiles().addresses().contains(&PRECOMPILE_ADDR));
119
120        evm.precompiles_mut().extend_precompiles(CustomPrecompileFactory.precompiles());
121
122        assert!(evm.precompiles().addresses().contains(&PRECOMPILE_ADDR));
123
124        let result = evm.transact(tx_env).unwrap();
125        assert!(result.result.is_success());
126        assert_eq!(result.result.output(), Some(&PAYLOAD.into()));
127    }
128
129    #[test]
130    fn build_eth_evm_with_extra_precompiles_london_spec() {
131        let (tx_env, mut evm) = create_eth_evm(SpecId::LONDON);
132
133        assert!(!evm.precompiles().addresses().contains(&ETH_OSAKA_PRECOMPILE));
134        assert!(!evm.precompiles().addresses().contains(&ETH_PRAGUE_PRECOMPILE));
135        assert!(!evm.precompiles().addresses().contains(&PRECOMPILE_ADDR));
136
137        evm.precompiles_mut().extend_precompiles(CustomPrecompileFactory.precompiles());
138
139        assert!(evm.precompiles().addresses().contains(&PRECOMPILE_ADDR));
140
141        let result = evm.transact(tx_env).unwrap();
142        assert!(result.result.is_success());
143        assert_eq!(result.result.output(), Some(&PAYLOAD.into()));
144    }
145
146    #[test]
147    fn build_eth_evm_with_extra_precompiles_prague_spec() {
148        let (tx_env, mut evm) = create_eth_evm(SpecId::PRAGUE);
149
150        assert!(!evm.precompiles().addresses().contains(&ETH_OSAKA_PRECOMPILE));
151        assert!(evm.precompiles().addresses().contains(&ETH_PRAGUE_PRECOMPILE));
152        assert!(!evm.precompiles().addresses().contains(&PRECOMPILE_ADDR));
153
154        evm.precompiles_mut().extend_precompiles(CustomPrecompileFactory.precompiles());
155
156        assert!(evm.precompiles().addresses().contains(&PRECOMPILE_ADDR));
157
158        let result = evm.transact(tx_env).unwrap();
159        assert!(result.result.is_success());
160        assert_eq!(result.result.output(), Some(&PAYLOAD.into()));
161    }
162}