1use alloy_evm::precompiles::DynPrecompile;
2use alloy_primitives::Address;
3use std::fmt::Debug;
4
5pub trait PrecompileFactory: Send + Sync + Unpin + Debug {
8 fn precompiles(&self) -> Vec<(Address, DynPrecompile)>;
10}
11
12#[cfg(test)]
13mod tests {
14 use std::convert::Infallible;
15
16 use crate::PrecompileFactory;
17 use alloy_evm::{
18 EthEvm, Evm,
19 eth::EthEvmContext,
20 precompiles::{DynPrecompile, PrecompilesMap},
21 };
22 use alloy_op_evm::OpEvm;
23 use alloy_primitives::{Address, Bytes, TxKind, U256, address};
24 use itertools::Itertools;
25 use op_revm::{L1BlockInfo, OpContext, OpSpecId, OpTransaction, precompiles::OpPrecompiles};
26 use revm::{
27 Journal,
28 context::{BlockEnv, CfgEnv, Evm as RevmEvm, JournalTr, LocalContext, TxEnv},
29 database::{EmptyDB, EmptyDBTyped},
30 handler::{EthPrecompiles, instructions::EthInstructions},
31 inspector::NoOpInspector,
32 interpreter::interpreter::EthInterpreter,
33 precompile::{PrecompileOutput, PrecompileSpecId, Precompiles},
34 primitives::hardfork::SpecId,
35 };
36
37 const ETH_PRAGUE_PRECOMPILE: Address = address!("0x0000000000000000000000000000000000000011");
39
40 const ETH_OSAKA_PRECOMPILE: Address = address!("0x0000000000000000000000000000000000000100");
42
43 const OP_ISTHMUS_PRECOMPILE: Address = address!("0x0000000000000000000000000000000000000100");
45
46 const PRECOMPILE_ADDR: Address = address!("0x0000000000000000000000000000000000000071");
48 const PAYLOAD: &[u8] = &[0xde, 0xad, 0xbe, 0xef];
49
50 #[derive(Debug)]
51 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 bytes: Bytes::copy_from_slice(input.data),
61 gas_used: 0,
62 gas_refunded: 0,
63 reverted: false,
64 })
65 }),
66 )]
67 }
68 }
69
70 fn create_eth_evm(
72 spec: SpecId,
73 ) -> (TxEnv, EthEvm<EmptyDBTyped<Infallible>, NoOpInspector, PrecompilesMap>) {
74 let tx_env = TxEnv {
75 kind: TxKind::Call(PRECOMPILE_ADDR),
76 data: PAYLOAD.into(),
77 ..Default::default()
78 };
79
80 let eth_evm_context = EthEvmContext {
81 journaled_state: Journal::new(EmptyDB::default()),
82 block: BlockEnv::default(),
83 cfg: CfgEnv::new_with_spec(spec),
84 tx: tx_env.clone(),
85 chain: (),
86 local: LocalContext::default(),
87 error: Ok(()),
88 };
89
90 let eth_precompiles = EthPrecompiles {
91 precompiles: Precompiles::new(PrecompileSpecId::from_spec_id(spec)),
92 spec,
93 }
94 .precompiles;
95 let eth_evm = EthEvm::new(
96 RevmEvm::new_with_inspector(
97 eth_evm_context,
98 NoOpInspector,
99 EthInstructions::<EthInterpreter, EthEvmContext<EmptyDB>>::new_mainnet_with_spec(
100 spec,
101 ),
102 PrecompilesMap::from_static(eth_precompiles),
103 ),
104 true,
105 );
106
107 (tx_env, eth_evm)
108 }
109
110 fn create_op_evm(
112 spec: SpecId,
113 op_spec: OpSpecId,
114 ) -> (OpTransaction<TxEnv>, OpEvm<EmptyDBTyped<Infallible>, NoOpInspector, PrecompilesMap>)
115 {
116 let tx = OpTransaction::<TxEnv> {
117 base: TxEnv {
118 kind: TxKind::Call(PRECOMPILE_ADDR),
119 data: PAYLOAD.into(),
120 ..Default::default()
121 },
122 ..Default::default()
123 };
124
125 let mut chain = L1BlockInfo::default();
126
127 if op_spec == OpSpecId::ISTHMUS {
128 chain.operator_fee_constant = Some(U256::from(0));
129 chain.operator_fee_scalar = Some(U256::from(0));
130 }
131
132 let op_cfg: CfgEnv<OpSpecId> = CfgEnv::new_with_spec(op_spec);
133 let op_evm_context = OpContext {
134 journaled_state: {
135 let mut journal = Journal::new(EmptyDB::default());
136 journal.set_spec_id(spec);
137 journal
138 },
139 block: BlockEnv::default(),
140 cfg: op_cfg.clone(),
141 tx: tx.clone(),
142 chain,
143 local: LocalContext::default(),
144 error: Ok(()),
145 };
146
147 let op_precompiles = OpPrecompiles::new_with_spec(op_cfg.spec).precompiles();
148 let op_evm = OpEvm::new(
149 op_revm::OpEvm(RevmEvm::new_with_inspector(
150 op_evm_context,
151 NoOpInspector,
152 EthInstructions::<EthInterpreter, OpContext<EmptyDB>>::new_mainnet_with_spec(spec),
153 PrecompilesMap::from_static(op_precompiles),
154 )),
155 true,
156 );
157
158 (tx, op_evm)
159 }
160
161 #[test]
162 fn build_eth_evm_with_extra_precompiles_osaka_spec() {
163 let (tx_env, mut evm) = create_eth_evm(SpecId::OSAKA);
164
165 assert!(evm.precompiles().addresses().contains(Ð_OSAKA_PRECOMPILE));
166 assert!(evm.precompiles().addresses().contains(Ð_PRAGUE_PRECOMPILE));
167 assert!(!evm.precompiles().addresses().contains(&PRECOMPILE_ADDR));
168
169 evm.precompiles_mut().extend_precompiles(CustomPrecompileFactory.precompiles());
170
171 assert!(evm.precompiles().addresses().contains(&PRECOMPILE_ADDR));
172
173 let result = evm.transact(tx_env).unwrap();
174 assert!(result.result.is_success());
175 assert_eq!(result.result.output(), Some(&PAYLOAD.into()));
176 }
177
178 #[test]
179 fn build_eth_evm_with_extra_precompiles_london_spec() {
180 let (tx_env, mut evm) = create_eth_evm(SpecId::LONDON);
181
182 assert!(!evm.precompiles().addresses().contains(Ð_OSAKA_PRECOMPILE));
183 assert!(!evm.precompiles().addresses().contains(Ð_PRAGUE_PRECOMPILE));
184 assert!(!evm.precompiles().addresses().contains(&PRECOMPILE_ADDR));
185
186 evm.precompiles_mut().extend_precompiles(CustomPrecompileFactory.precompiles());
187
188 assert!(evm.precompiles().addresses().contains(&PRECOMPILE_ADDR));
189
190 let result = evm.transact(tx_env).unwrap();
191 assert!(result.result.is_success());
192 assert_eq!(result.result.output(), Some(&PAYLOAD.into()));
193 }
194
195 #[test]
196 fn build_eth_evm_with_extra_precompiles_prague_spec() {
197 let (tx_env, mut evm) = create_eth_evm(SpecId::PRAGUE);
198
199 assert!(!evm.precompiles().addresses().contains(Ð_OSAKA_PRECOMPILE));
200 assert!(evm.precompiles().addresses().contains(Ð_PRAGUE_PRECOMPILE));
201 assert!(!evm.precompiles().addresses().contains(&PRECOMPILE_ADDR));
202
203 evm.precompiles_mut().extend_precompiles(CustomPrecompileFactory.precompiles());
204
205 assert!(evm.precompiles().addresses().contains(&PRECOMPILE_ADDR));
206
207 let result = evm.transact(tx_env).unwrap();
208 assert!(result.result.is_success());
209 assert_eq!(result.result.output(), Some(&PAYLOAD.into()));
210 }
211
212 #[test]
213 fn build_op_evm_with_extra_precompiles_isthmus_spec() {
214 let (tx, mut evm) = create_op_evm(SpecId::OSAKA, OpSpecId::ISTHMUS);
215
216 assert!(evm.precompiles().addresses().contains(&OP_ISTHMUS_PRECOMPILE));
217 assert!(evm.precompiles().addresses().contains(Ð_PRAGUE_PRECOMPILE));
218 assert!(!evm.precompiles().addresses().contains(&PRECOMPILE_ADDR));
219
220 evm.precompiles_mut().extend_precompiles(CustomPrecompileFactory.precompiles());
221
222 assert!(evm.precompiles().addresses().contains(&PRECOMPILE_ADDR));
223
224 let result = evm.transact(tx).unwrap();
225 assert!(result.result.is_success());
226 assert_eq!(result.result.output(), Some(&PAYLOAD.into()));
227 }
228
229 #[test]
230 fn build_op_evm_with_extra_precompiles_bedrock_spec() {
231 let (tx, mut evm) = create_op_evm(SpecId::OSAKA, OpSpecId::BEDROCK);
232
233 assert!(!evm.precompiles().addresses().contains(&OP_ISTHMUS_PRECOMPILE));
234 assert!(!evm.precompiles().addresses().contains(Ð_PRAGUE_PRECOMPILE));
235 assert!(!evm.precompiles().addresses().contains(&PRECOMPILE_ADDR));
236
237 evm.precompiles_mut().extend_precompiles(CustomPrecompileFactory.precompiles());
238
239 assert!(evm.precompiles().addresses().contains(&PRECOMPILE_ADDR));
240
241 let result = evm.transact(tx).unwrap();
242 assert!(result.result.is_success());
243 assert_eq!(result.result.output(), Some(&PAYLOAD.into()));
244 }
245}