Skip to main content

foundry_evm_core/
evm.rs

1use std::{
2    marker::PhantomData,
3    ops::{Deref, DerefMut},
4};
5
6use crate::{
7    EthCheatCtx, EthInspectorExt,
8    backend::{DatabaseExt, JournaledState},
9    constants::DEFAULT_CREATE2_DEPLOYER_CODEHASH,
10};
11use alloy_consensus::constants::KECCAK_EMPTY;
12use alloy_evm::{Evm, EvmEnv, EvmFactory, eth::EthEvmContext, precompiles::PrecompilesMap};
13use alloy_primitives::{Address, Bytes, U256};
14use foundry_fork_db::DatabaseError;
15use revm::{
16    Context,
17    context::{
18        BlockEnv, Cfg, CfgEnv, ContextTr, CreateScheme, Evm as RevmEvm, JournalTr, LocalContextTr,
19        TxEnv,
20        result::{EVMError, ExecResultAndState, ExecutionResult, HaltReason, ResultAndState},
21    },
22    handler::{
23        EthFrame, EvmTr, FrameResult, FrameTr, Handler, ItemOrResult, instructions::EthInstructions,
24    },
25    inspector::{InspectorEvmTr, InspectorHandler},
26    interpreter::{
27        CallInput, CallInputs, CallOutcome, CallScheme, CallValue, CreateInputs, CreateOutcome,
28        FrameInput, Gas, InstructionResult, InterpreterResult, SharedMemory,
29        interpreter::EthInterpreter, interpreter_action::FrameInit, return_ok,
30    },
31    primitives::hardfork::SpecId,
32};
33
34pub fn new_eth_evm_with_inspector<'db, I: EthInspectorExt>(
35    db: &'db mut dyn DatabaseExt,
36    evm_env: EvmEnv,
37    tx_env: TxEnv,
38    inspector: I,
39) -> FoundryEvm<'db, I> {
40    let eth_evm =
41        alloy_evm::EthEvmFactory::default().create_evm_with_inspector(db, evm_env, inspector);
42    let mut inner = eth_evm.into_inner();
43    inner.ctx.cfg.tx_chain_id_check = true;
44    inner.ctx.tx = tx_env;
45
46    let mut evm = FoundryEvm { inner };
47    evm.inspector().get_networks().inject_precompiles(evm.precompiles_mut());
48    evm
49}
50
51/// Get the call inputs for the CREATE2 factory.
52fn get_create2_factory_call_inputs(
53    salt: U256,
54    inputs: &CreateInputs,
55    deployer: Address,
56) -> CallInputs {
57    let calldata = [&salt.to_be_bytes::<32>()[..], &inputs.init_code()[..]].concat();
58    CallInputs {
59        caller: inputs.caller(),
60        bytecode_address: deployer,
61        known_bytecode: None,
62        target_address: deployer,
63        scheme: CallScheme::Call,
64        value: CallValue::Transfer(inputs.value()),
65        input: CallInput::Bytes(calldata.into()),
66        gas_limit: inputs.gas_limit(),
67        is_static: false,
68        return_memory_offset: 0..0,
69    }
70}
71
72pub struct FoundryEvm<'db, I: EthInspectorExt> {
73    #[allow(clippy::type_complexity)]
74    inner: RevmEvm<
75        EthEvmContext<&'db mut dyn DatabaseExt>,
76        I,
77        EthInstructions<EthInterpreter, EthEvmContext<&'db mut dyn DatabaseExt>>,
78        PrecompilesMap,
79        EthFrame<EthInterpreter>,
80    >,
81}
82
83impl<'db, I: EthInspectorExt> Evm for FoundryEvm<'db, I> {
84    type Precompiles = PrecompilesMap;
85    type Inspector = I;
86    type DB = &'db mut dyn DatabaseExt;
87    type Error = EVMError<DatabaseError>;
88    type HaltReason = HaltReason;
89    type Spec = SpecId;
90    type Tx = TxEnv;
91    type BlockEnv = BlockEnv;
92
93    fn block(&self) -> &BlockEnv {
94        &self.inner.block
95    }
96
97    fn chain_id(&self) -> u64 {
98        self.inner.ctx.cfg.chain_id
99    }
100
101    fn components(&self) -> (&Self::DB, &Self::Inspector, &Self::Precompiles) {
102        (&self.inner.ctx.journaled_state.database, &self.inner.inspector, &self.inner.precompiles)
103    }
104
105    fn components_mut(&mut self) -> (&mut Self::DB, &mut Self::Inspector, &mut Self::Precompiles) {
106        (
107            &mut self.inner.ctx.journaled_state.database,
108            &mut self.inner.inspector,
109            &mut self.inner.precompiles,
110        )
111    }
112
113    fn set_inspector_enabled(&mut self, _enabled: bool) {
114        unimplemented!("FoundryEvm is always inspecting")
115    }
116
117    fn transact_raw(
118        &mut self,
119        tx: Self::Tx,
120    ) -> Result<ResultAndState<Self::HaltReason>, Self::Error> {
121        self.inner.ctx.tx = tx;
122
123        let mut handler = FoundryHandler::<I>::default();
124        let result = handler.inspect_run(&mut self.inner)?;
125
126        Ok(ResultAndState::new(result, self.inner.ctx.journaled_state.inner.state.clone()))
127    }
128
129    fn transact_system_call(
130        &mut self,
131        _caller: Address,
132        _contract: Address,
133        _data: Bytes,
134    ) -> Result<ExecResultAndState<ExecutionResult>, Self::Error> {
135        unimplemented!()
136    }
137
138    fn finish(self) -> (Self::DB, EvmEnv<Self::Spec>)
139    where
140        Self: Sized,
141    {
142        let Context { block: block_env, cfg: cfg_env, journaled_state, .. } = self.inner.ctx;
143
144        (journaled_state.database, EvmEnv { block_env, cfg_env })
145    }
146}
147
148impl<'db, I: EthInspectorExt> Deref for FoundryEvm<'db, I> {
149    type Target = Context<BlockEnv, TxEnv, CfgEnv, &'db mut dyn DatabaseExt>;
150
151    fn deref(&self) -> &Self::Target {
152        &self.inner.ctx
153    }
154}
155
156impl<I: EthInspectorExt> DerefMut for FoundryEvm<'_, I> {
157    fn deref_mut(&mut self) -> &mut Self::Target {
158        &mut self.inner.ctx
159    }
160}
161
162/// Object-safe trait exposing the operations that cheatcode nested EVM closures need.
163///
164/// This abstracts over the concrete EVM type (`FoundryEvm`, future `TempoEvm`, etc.)
165/// so that cheatcode impls can build and run nested EVMs without knowing the concrete type.
166pub trait NestedEvm {
167    /// The transaction environment type.
168    type Tx;
169    /// The block environment type.
170    type Block;
171    /// The EVM spec (hardfork) type.
172    type Spec;
173
174    /// Returns a mutable reference to the journal inner state (`JournaledState`).
175    fn journal_inner_mut(&mut self) -> &mut JournaledState;
176
177    /// Runs a single execution frame (create or call) through the EVM handler loop.
178    fn run_execution(&mut self, frame: FrameInput) -> Result<FrameResult, EVMError<DatabaseError>>;
179
180    /// Executes a full transaction with the given tx env.
181    fn transact(
182        &mut self,
183        tx: Self::Tx,
184    ) -> Result<ResultAndState<HaltReason>, EVMError<DatabaseError>>;
185
186    /// Returns a snapshot of the current EVM environment (cfg + block).
187    fn to_evm_env(&self) -> EvmEnv<Self::Spec, Self::Block>;
188}
189
190impl<I: EthInspectorExt> NestedEvm for FoundryEvm<'_, I> {
191    type Tx = TxEnv;
192    type Block = BlockEnv;
193    type Spec = SpecId;
194
195    fn journal_inner_mut(&mut self) -> &mut JournaledState {
196        &mut self.inner.ctx.journaled_state.inner
197    }
198
199    fn run_execution(&mut self, frame: FrameInput) -> Result<FrameResult, EVMError<DatabaseError>> {
200        let mut handler = FoundryHandler::<I>::default();
201
202        // Create first frame
203        let memory =
204            SharedMemory::new_with_buffer(self.inner.ctx().local().shared_memory_buffer().clone());
205        let first_frame_input = FrameInit { depth: 0, memory, frame_input: frame };
206
207        // Run execution loop
208        let mut frame_result = handler.inspect_run_exec_loop(&mut self.inner, first_frame_input)?;
209
210        // Handle last frame result
211        handler.last_frame_result(&mut self.inner, &mut frame_result)?;
212
213        Ok(frame_result)
214    }
215
216    fn transact(
217        &mut self,
218        tx: TxEnv,
219    ) -> Result<ResultAndState<HaltReason>, EVMError<DatabaseError>> {
220        Evm::transact_raw(self, tx)
221    }
222
223    fn to_evm_env(&self) -> EvmEnv {
224        EvmEnv { cfg_env: self.inner.ctx.cfg.clone(), block_env: self.inner.ctx.block.clone() }
225    }
226}
227
228/// Closure type used by `CheatcodesExecutor` methods that run nested EVM operations.
229pub type NestedEvmClosure<'a, Block, Tx, Spec> =
230    &'a mut dyn FnMut(
231        &mut dyn NestedEvm<Block = Block, Tx = Tx, Spec = Spec>,
232    ) -> Result<(), EVMError<DatabaseError>>;
233
234/// Clones the current context (env + journal), passes the database, cloned env,
235/// and cloned journal inner to the callback. The callback builds whatever EVM it
236/// needs, runs its operations, and returns `(result, modified_env, modified_journal)`.
237/// Modified state is written back after the callback returns.
238pub fn with_cloned_context<CTX: EthCheatCtx>(
239    ecx: &mut CTX,
240    f: impl FnOnce(
241        &mut dyn DatabaseExt<CTX::Block, CTX::Tx, <CTX::Cfg as Cfg>::Spec>,
242        EvmEnv<<CTX::Cfg as Cfg>::Spec, CTX::Block>,
243        CTX::Tx,
244        JournaledState,
245    ) -> Result<
246        (EvmEnv<<CTX::Cfg as Cfg>::Spec, CTX::Block>, JournaledState),
247        EVMError<DatabaseError>,
248    >,
249) -> Result<(), EVMError<DatabaseError>> {
250    let evm_env = ecx.evm_clone();
251    let tx_env = ecx.tx_clone();
252
253    let (db, journal_inner) = ecx.db_journal_inner_mut();
254    let journal_inner_clone = journal_inner.clone();
255
256    let (sub_evm_env, sub_inner) = f(db, evm_env, tx_env, journal_inner_clone)?;
257
258    // Write back modified state. The db borrow was released when f returned.
259    ecx.set_journal_inner(sub_inner);
260    ecx.set_evm(sub_evm_env);
261
262    Ok(())
263}
264
265pub struct FoundryHandler<'db, I: EthInspectorExt> {
266    create2_overrides: Vec<(usize, CallInputs)>,
267    _phantom: PhantomData<(&'db mut dyn DatabaseExt, I)>,
268}
269
270impl<I: EthInspectorExt> Default for FoundryHandler<'_, I> {
271    fn default() -> Self {
272        Self { create2_overrides: Vec::new(), _phantom: PhantomData }
273    }
274}
275
276// Blanket Handler implementation for FoundryHandler, needed for implementing the InspectorHandler
277// trait.
278impl<'db, I: EthInspectorExt> Handler for FoundryHandler<'db, I> {
279    type Evm = RevmEvm<
280        EthEvmContext<&'db mut dyn DatabaseExt>,
281        I,
282        EthInstructions<EthInterpreter, EthEvmContext<&'db mut dyn DatabaseExt>>,
283        PrecompilesMap,
284        EthFrame<EthInterpreter>,
285    >;
286    type Error = EVMError<DatabaseError>;
287    type HaltReason = HaltReason;
288}
289
290impl<'db, I: EthInspectorExt> FoundryHandler<'db, I> {
291    /// Handles CREATE2 frame initialization, potentially transforming it to use the CREATE2
292    /// factory.
293    fn handle_create_frame(
294        &mut self,
295        evm: &mut <Self as Handler>::Evm,
296        init: &mut FrameInit,
297    ) -> Result<Option<FrameResult>, <Self as Handler>::Error> {
298        if let FrameInput::Create(inputs) = &init.frame_input
299            && let CreateScheme::Create2 { salt } = inputs.scheme()
300        {
301            let (ctx, inspector) = evm.ctx_inspector();
302
303            if inspector.should_use_create2_factory(ctx.journal().depth(), inputs) {
304                let gas_limit = inputs.gas_limit();
305
306                // Get CREATE2 deployer.
307                let create2_deployer = evm.inspector().create2_deployer();
308
309                // Generate call inputs for CREATE2 factory.
310                let call_inputs = get_create2_factory_call_inputs(salt, inputs, create2_deployer);
311
312                // Push data about current override to the stack.
313                self.create2_overrides.push((evm.journal().depth(), call_inputs.clone()));
314
315                // Sanity check that CREATE2 deployer exists.
316                let code_hash = evm.journal_mut().load_account(create2_deployer)?.info.code_hash;
317                if code_hash == KECCAK_EMPTY {
318                    return Ok(Some(FrameResult::Call(CallOutcome {
319                        result: InterpreterResult {
320                            result: InstructionResult::Revert,
321                            output: Bytes::from(
322                                format!("missing CREATE2 deployer: {create2_deployer}")
323                                    .into_bytes(),
324                            ),
325                            gas: Gas::new(gas_limit),
326                        },
327                        memory_offset: 0..0,
328                        was_precompile_called: false,
329                        precompile_call_logs: vec![],
330                    })));
331                } else if code_hash != DEFAULT_CREATE2_DEPLOYER_CODEHASH {
332                    return Ok(Some(FrameResult::Call(CallOutcome {
333                        result: InterpreterResult {
334                            result: InstructionResult::Revert,
335                            output: "invalid CREATE2 deployer bytecode".into(),
336                            gas: Gas::new(gas_limit),
337                        },
338                        memory_offset: 0..0,
339                        was_precompile_called: false,
340                        precompile_call_logs: vec![],
341                    })));
342                }
343
344                // Rewrite the frame init
345                init.frame_input = FrameInput::Call(Box::new(call_inputs));
346            }
347        }
348        Ok(None)
349    }
350
351    /// Transforms CREATE2 factory call results back into CREATE outcomes.
352    fn handle_create2_override(
353        &mut self,
354        evm: &mut <Self as Handler>::Evm,
355        result: FrameResult,
356    ) -> FrameResult {
357        if self.create2_overrides.last().is_some_and(|(depth, _)| *depth == evm.journal().depth()) {
358            let (_, call_inputs) = self.create2_overrides.pop().unwrap();
359            let FrameResult::Call(mut call) = result else {
360                unreachable!("create2 override should be a call frame");
361            };
362
363            // Decode address from output.
364            let address = match call.instruction_result() {
365                return_ok!() => Address::try_from(call.output().as_ref())
366                    .map_err(|_| {
367                        call.result = InterpreterResult {
368                            result: InstructionResult::Revert,
369                            output: "invalid CREATE2 factory output".into(),
370                            gas: Gas::new(call_inputs.gas_limit),
371                        };
372                    })
373                    .ok(),
374                _ => None,
375            };
376
377            FrameResult::Create(CreateOutcome { result: call.result, address })
378        } else {
379            result
380        }
381    }
382}
383
384impl<I: EthInspectorExt> InspectorHandler for FoundryHandler<'_, I> {
385    type IT = EthInterpreter;
386
387    fn inspect_run_exec_loop(
388        &mut self,
389        evm: &mut Self::Evm,
390        first_frame_input: <<Self::Evm as EvmTr>::Frame as FrameTr>::FrameInit,
391    ) -> Result<FrameResult, Self::Error> {
392        let res = evm.inspect_frame_init(first_frame_input)?;
393
394        if let ItemOrResult::Result(frame_result) = res {
395            return Ok(frame_result);
396        }
397
398        loop {
399            let call_or_result = evm.inspect_frame_run()?;
400
401            let result = match call_or_result {
402                ItemOrResult::Item(mut init) => {
403                    // Handle CREATE/CREATE2 frame initialization
404                    if let Some(frame_result) = self.handle_create_frame(evm, &mut init)? {
405                        return Ok(frame_result);
406                    }
407
408                    match evm.inspect_frame_init(init)? {
409                        ItemOrResult::Item(_) => continue,
410                        ItemOrResult::Result(result) => result,
411                    }
412                }
413                ItemOrResult::Result(result) => result,
414            };
415
416            // Handle CREATE2 override transformation if needed
417            let result = self.handle_create2_override(evm, result);
418
419            if let Some(result) = evm.frame_return_result(result)? {
420                return Ok(result);
421            }
422        }
423    }
424}