1use std::ops::{Deref, DerefMut};
2
3use crate::{
4 backend::DatabaseExt, constants::DEFAULT_CREATE2_DEPLOYER_CODEHASH, Env, InspectorExt,
5};
6use alloy_consensus::constants::KECCAK_EMPTY;
7use alloy_evm::{
8 eth::EthEvmContext,
9 precompiles::{DynPrecompile, PrecompilesMap},
10 Evm, EvmEnv,
11};
12use alloy_primitives::{Address, Bytes, U256};
13use foundry_fork_db::DatabaseError;
14use revm::{
15 context::{
16 result::{EVMError, HaltReason, ResultAndState},
17 BlockEnv, CfgEnv, ContextTr, CreateScheme, Evm as RevmEvm, JournalTr, LocalContext, TxEnv,
18 },
19 handler::{
20 instructions::EthInstructions, EthFrame, EthPrecompiles, FrameInitOrResult, FrameResult,
21 Handler, ItemOrResult, MainnetHandler,
22 },
23 inspector::InspectorHandler,
24 interpreter::{
25 interpreter::EthInterpreter, return_ok, CallInput, CallInputs, CallOutcome, CallScheme,
26 CallValue, CreateInputs, CreateOutcome, FrameInput, Gas, InstructionResult,
27 InterpreterResult,
28 },
29 precompile::{secp256r1::P256VERIFY, PrecompileSpecId, Precompiles},
30 primitives::hardfork::SpecId,
31 Context, ExecuteEvm, Journal,
32};
33
34pub fn new_evm_with_inspector<'i, 'db, I: InspectorExt + ?Sized>(
35 db: &'db mut dyn DatabaseExt,
36 env: Env,
37 inspector: &'i mut I,
38) -> FoundryEvm<'db, &'i mut I> {
39 let ctx = EthEvmContext {
40 journaled_state: {
41 let mut journal = Journal::new(db);
42 journal.set_spec_id(env.evm_env.cfg_env.spec);
43 journal
44 },
45 block: env.evm_env.block_env,
46 cfg: env.evm_env.cfg_env,
47 tx: env.tx,
48 chain: (),
49 local: LocalContext::default(),
50 error: Ok(()),
51 };
52 let spec = ctx.cfg.spec;
53
54 let mut evm = FoundryEvm {
55 inner: RevmEvm::new_with_inspector(
56 ctx,
57 inspector,
58 EthInstructions::default(),
59 get_precompiles(spec),
60 ),
61 };
62
63 inject_precompiles(&mut evm);
64
65 evm
66}
67
68pub fn new_evm_with_existing_context<'a>(
69 ctx: EthEvmContext<&'a mut dyn DatabaseExt>,
70 inspector: &'a mut dyn InspectorExt,
71) -> FoundryEvm<'a, &'a mut dyn InspectorExt> {
72 let spec = ctx.cfg.spec;
73
74 let mut evm = FoundryEvm {
75 inner: RevmEvm::new_with_inspector(
76 ctx,
77 inspector,
78 EthInstructions::default(),
79 get_precompiles(spec),
80 ),
81 };
82
83 inject_precompiles(&mut evm);
84
85 evm
86}
87
88fn inject_precompiles(evm: &mut FoundryEvm<'_, impl InspectorExt>) {
90 if evm.inspector().is_odyssey() {
91 evm.precompiles_mut().apply_precompile(P256VERIFY.address(), |_| {
92 Some(DynPrecompile::from(P256VERIFY.precompile()))
93 });
94 }
95}
96
97fn get_precompiles(spec: SpecId) -> PrecompilesMap {
99 PrecompilesMap::from_static(
100 EthPrecompiles {
101 precompiles: Precompiles::new(PrecompileSpecId::from_spec_id(spec)),
102 spec,
103 }
104 .precompiles,
105 )
106}
107
108fn get_create2_factory_call_inputs(
110 salt: U256,
111 inputs: &CreateInputs,
112 deployer: Address,
113) -> CallInputs {
114 let calldata = [&salt.to_be_bytes::<32>()[..], &inputs.init_code[..]].concat();
115 CallInputs {
116 caller: inputs.caller,
117 bytecode_address: deployer,
118 target_address: deployer,
119 scheme: CallScheme::Call,
120 value: CallValue::Transfer(inputs.value),
121 input: CallInput::Bytes(calldata.into()),
122 gas_limit: inputs.gas_limit,
123 is_static: false,
124 return_memory_offset: 0..0,
125 is_eof: false,
126 }
127}
128
129pub struct FoundryEvm<'db, I: InspectorExt> {
130 #[allow(clippy::type_complexity)]
131 pub inner: RevmEvm<
132 EthEvmContext<&'db mut dyn DatabaseExt>,
133 I,
134 EthInstructions<EthInterpreter, EthEvmContext<&'db mut dyn DatabaseExt>>,
135 PrecompilesMap,
136 >,
137}
138
139impl<I: InspectorExt> FoundryEvm<'_, I> {
140 pub fn run_execution(
141 &mut self,
142 frame: FrameInput,
143 ) -> Result<FrameResult, EVMError<DatabaseError>> {
144 let mut handler = FoundryHandler::<_>::default();
145
146 let frame = handler.inspect_first_frame_init(&mut self.inner, frame)?;
148 let frame_result = match frame {
149 ItemOrResult::Item(frame) => handler.inspect_run_exec_loop(&mut self.inner, frame)?,
150 ItemOrResult::Result(result) => result,
151 };
152
153 Ok(frame_result)
154 }
155}
156
157impl<'db, I: InspectorExt> Evm for FoundryEvm<'db, I> {
158 type Precompiles = PrecompilesMap;
159 type Inspector = I;
160 type DB = &'db mut dyn DatabaseExt;
161 type Error = EVMError<DatabaseError>;
162 type HaltReason = HaltReason;
163 type Spec = SpecId;
164 type Tx = TxEnv;
165
166 fn chain_id(&self) -> u64 {
167 self.inner.ctx.cfg.chain_id
168 }
169
170 fn block(&self) -> &BlockEnv {
171 &self.inner.block
172 }
173
174 fn db_mut(&mut self) -> &mut Self::DB {
175 self.inner.db()
176 }
177
178 fn precompiles(&self) -> &Self::Precompiles {
179 &self.inner.precompiles
180 }
181
182 fn precompiles_mut(&mut self) -> &mut Self::Precompiles {
183 &mut self.inner.precompiles
184 }
185
186 fn inspector(&self) -> &Self::Inspector {
187 &self.inner.inspector
188 }
189
190 fn inspector_mut(&mut self) -> &mut Self::Inspector {
191 &mut self.inner.inspector
192 }
193
194 fn set_inspector_enabled(&mut self, _enabled: bool) {
195 unimplemented!("FoundryEvm is always inspecting")
196 }
197
198 fn transact_raw(
199 &mut self,
200 tx: Self::Tx,
201 ) -> Result<ResultAndState<Self::HaltReason>, Self::Error> {
202 let mut handler = FoundryHandler::<_>::default();
203 self.inner.set_tx(tx);
204 handler.inspect_run(&mut self.inner)
205 }
206
207 fn transact_system_call(
208 &mut self,
209 _caller: Address,
210 _contract: Address,
211 _data: Bytes,
212 ) -> Result<ResultAndState<Self::HaltReason>, Self::Error> {
213 unimplemented!()
214 }
215
216 fn finish(self) -> (Self::DB, EvmEnv<Self::Spec>)
217 where
218 Self: Sized,
219 {
220 let Context { block: block_env, cfg: cfg_env, journaled_state, .. } = self.inner.ctx;
221
222 (journaled_state.database, EvmEnv { block_env, cfg_env })
223 }
224}
225
226impl<'db, I: InspectorExt> Deref for FoundryEvm<'db, I> {
227 type Target = Context<BlockEnv, TxEnv, CfgEnv, &'db mut dyn DatabaseExt>;
228
229 fn deref(&self) -> &Self::Target {
230 &self.inner.ctx
231 }
232}
233
234impl<I: InspectorExt> DerefMut for FoundryEvm<'_, I> {
235 fn deref_mut(&mut self) -> &mut Self::Target {
236 &mut self.inner.ctx
237 }
238}
239
240pub struct FoundryHandler<'db, I: InspectorExt> {
241 #[allow(clippy::type_complexity)]
242 inner: MainnetHandler<
243 RevmEvm<
244 EthEvmContext<&'db mut dyn DatabaseExt>,
245 I,
246 EthInstructions<EthInterpreter, EthEvmContext<&'db mut dyn DatabaseExt>>,
247 PrecompilesMap,
248 >,
249 EVMError<DatabaseError>,
250 EthFrame<
251 RevmEvm<
252 EthEvmContext<&'db mut dyn DatabaseExt>,
253 I,
254 EthInstructions<EthInterpreter, EthEvmContext<&'db mut dyn DatabaseExt>>,
255 PrecompilesMap,
256 >,
257 EVMError<DatabaseError>,
258 EthInterpreter,
259 >,
260 >,
261 create2_overrides: Vec<(usize, CallInputs)>,
262}
263
264impl<I: InspectorExt> Default for FoundryHandler<'_, I> {
265 fn default() -> Self {
266 Self { inner: MainnetHandler::default(), create2_overrides: Vec::new() }
267 }
268}
269
270impl<'db, I: InspectorExt> Handler for FoundryHandler<'db, I> {
271 type Evm = RevmEvm<
272 EthEvmContext<&'db mut dyn DatabaseExt>,
273 I,
274 EthInstructions<EthInterpreter, EthEvmContext<&'db mut dyn DatabaseExt>>,
275 PrecompilesMap,
276 >;
277 type Error = EVMError<DatabaseError>;
278 type Frame = EthFrame<
279 RevmEvm<
280 EthEvmContext<&'db mut dyn DatabaseExt>,
281 I,
282 EthInstructions<EthInterpreter, EthEvmContext<&'db mut dyn DatabaseExt>>,
283 PrecompilesMap,
284 >,
285 EVMError<DatabaseError>,
286 EthInterpreter,
287 >;
288 type HaltReason = HaltReason;
289
290 fn frame_return_result(
291 &mut self,
292 frame: &mut Self::Frame,
293 evm: &mut Self::Evm,
294 result: <Self::Frame as revm::handler::Frame>::FrameResult,
295 ) -> Result<(), Self::Error> {
296 let result = if self
297 .create2_overrides
298 .last()
299 .is_some_and(|(depth, _)| *depth == evm.journal().depth)
300 {
301 let (_, call_inputs) = self.create2_overrides.pop().unwrap();
302 let FrameResult::Call(mut result) = result else {
303 unreachable!("create2 override should be a call frame");
304 };
305
306 let address = match result.instruction_result() {
308 return_ok!() => Address::try_from(result.output().as_ref())
309 .map_err(|_| {
310 result.result = InterpreterResult {
311 result: InstructionResult::Revert,
312 output: "invalid CREATE2 factory output".into(),
313 gas: Gas::new(call_inputs.gas_limit),
314 };
315 })
316 .ok(),
317 _ => None,
318 };
319
320 FrameResult::Create(CreateOutcome { result: result.result, address })
321 } else {
322 result
323 };
324
325 self.inner.frame_return_result(frame, evm, result)
326 }
327}
328
329impl<I: InspectorExt> InspectorHandler for FoundryHandler<'_, I> {
330 type IT = EthInterpreter;
331
332 fn inspect_frame_call(
333 &mut self,
334 frame: &mut Self::Frame,
335 evm: &mut Self::Evm,
336 ) -> Result<FrameInitOrResult<Self::Frame>, Self::Error> {
337 let frame_or_result = self.inner.inspect_frame_call(frame, evm)?;
338
339 let ItemOrResult::Item(FrameInput::Create(inputs)) = &frame_or_result else {
340 return Ok(frame_or_result)
341 };
342
343 let CreateScheme::Create2 { salt } = inputs.scheme else { return Ok(frame_or_result) };
344
345 if !evm.inspector.should_use_create2_factory(&mut evm.ctx, inputs) {
346 return Ok(frame_or_result)
347 }
348
349 let gas_limit = inputs.gas_limit;
350
351 let create2_deployer = evm.inspector.create2_deployer();
353
354 let call_inputs = get_create2_factory_call_inputs(salt, inputs, create2_deployer);
356
357 self.create2_overrides.push((evm.journal().depth(), call_inputs.clone()));
359
360 let code_hash = evm.journal().load_account(create2_deployer)?.info.code_hash;
362 if code_hash == KECCAK_EMPTY {
363 return Ok(ItemOrResult::Result(FrameResult::Call(CallOutcome {
364 result: InterpreterResult {
365 result: InstructionResult::Revert,
366 output: Bytes::copy_from_slice(
367 format!("missing CREATE2 deployer: {create2_deployer}").as_bytes(),
368 ),
369 gas: Gas::new(gas_limit),
370 },
371 memory_offset: 0..0,
372 })))
373 } else if code_hash != DEFAULT_CREATE2_DEPLOYER_CODEHASH {
374 return Ok(ItemOrResult::Result(FrameResult::Call(CallOutcome {
375 result: InterpreterResult {
376 result: InstructionResult::Revert,
377 output: "invalid CREATE2 deployer bytecode".into(),
378 gas: Gas::new(gas_limit),
379 },
380 memory_offset: 0..0,
381 })))
382 }
383
384 Ok(ItemOrResult::Item(FrameInput::Call(Box::new(call_inputs))))
386 }
387}