Skip to main content

foundry_evm_core/evm/
mod.rs

1use std::{
2    fmt::Debug,
3    ops::{Deref, DerefMut},
4};
5
6use crate::{
7    FoundryBlock, FoundryContextExt, FoundryInspectorExt, FoundryTransaction,
8    FromAnyRpcTransaction,
9    backend::{DatabaseExt, JournaledState},
10    constants::{CALLER, TEST_CONTRACT_ADDRESS},
11    tempo::{TEMPO_PRECOMPILE_ADDRESSES, TEMPO_TIP20_TOKENS, initialize_tempo_genesis_inner},
12};
13use alloy_consensus::{SignableTransaction, Signed, transaction::SignerRecoverable};
14use alloy_evm::{
15    EthEvmFactory, Evm, EvmEnv, EvmFactory, FromRecoveredTx, eth::EthEvmContext,
16    precompiles::PrecompilesMap,
17};
18use alloy_network::{Ethereum, Network};
19use alloy_op_evm::{OpEvmFactory, OpTx};
20use alloy_primitives::{Address, Bytes, Signature, U256};
21use alloy_rlp::Decodable;
22use foundry_common::{FoundryReceiptResponse, FoundryTransactionBuilder, fmt::UIfmt};
23use foundry_config::FromEvmVersion;
24use foundry_fork_db::{DatabaseError, ForkBlockEnv};
25use op_alloy_network::Optimism;
26use op_revm::{
27    L1BlockInfo, OpEvm, OpHaltReason, OpSpecId, OpTransaction, handler::OpHandler,
28    precompiles::OpPrecompiles, transaction::error::OpTransactionError,
29};
30use revm::{
31    Context, Database, Journal, MainContext,
32    context::{
33        BlockEnv, CfgEnv, ContextTr, Evm as RevmEvm, JournalTr, LocalContextTr, TxEnv,
34        result::{
35            EVMError, ExecResultAndState, ExecutionResult, HaltReason, InvalidTransaction,
36            ResultAndState,
37        },
38    },
39    handler::{
40        EthFrame, EvmTr, FrameResult, Handler, MainnetHandler, instructions::EthInstructions,
41    },
42    inspector::{InspectorEvmTr, InspectorHandler},
43    interpreter::{
44        CallInput, CallInputs, CallScheme, CallValue, CreateInputs, FrameInput, InstructionResult,
45        SharedMemory, interpreter::EthInterpreter, interpreter_action::FrameInit,
46    },
47    primitives::hardfork::SpecId,
48    state::Bytecode,
49};
50use serde::{Deserialize, Serialize};
51use tempo_alloy::TempoNetwork;
52use tempo_chainspec::hardfork::TempoHardfork;
53use tempo_evm::evm::TempoEvmFactory;
54use tempo_precompiles::storage::StorageCtx;
55use tempo_revm::{
56    TempoBlockEnv, TempoHaltReason, TempoInvalidTransaction, TempoTxEnv, evm::TempoContext,
57    gas_params::tempo_gas_params, handler::TempoEvmHandler,
58};
59
60// Modified revm's OpContext with `OpTx`
61pub type OpContext<DB> = Context<BlockEnv, OpTx, CfgEnv<OpSpecId>, DB, Journal<DB>, L1BlockInfo>;
62
63pub mod eth;
64pub mod op;
65pub mod tempo;
66
67pub use eth::*;
68pub use op::*;
69pub use tempo::*;
70
71/// Foundry's supertrait associating [Network] with [FoundryEvmFactory]
72pub trait FoundryEvmNetwork: Copy + Debug + Default + 'static {
73    type Network: Network<
74            TxEnvelope: Decodable
75                            + SignerRecoverable
76                            + From<Signed<<Self::Network as Network>::UnsignedTx>>
77                            + for<'d> Deserialize<'d>
78                            + Serialize
79                            + UIfmt,
80            UnsignedTx: SignableTransaction<Signature>,
81            TransactionRequest: FoundryTransactionBuilder<Self::Network>
82                                    + for<'d> Deserialize<'d>
83                                    + Serialize,
84            ReceiptResponse: FoundryReceiptResponse,
85        >;
86    type EvmFactory: FoundryEvmFactory<Tx: FromRecoveredTx<<Self::Network as Network>::TxEnvelope>>;
87}
88
89#[derive(Clone, Copy, Debug, Default)]
90pub struct EthEvmNetwork;
91impl FoundryEvmNetwork for EthEvmNetwork {
92    type Network = Ethereum;
93    type EvmFactory = EthEvmFactory;
94}
95
96#[derive(Clone, Copy, Debug, Default)]
97pub struct TempoEvmNetwork;
98impl FoundryEvmNetwork for TempoEvmNetwork {
99    type Network = TempoNetwork;
100    type EvmFactory = TempoEvmFactory;
101}
102
103#[derive(Clone, Copy, Debug, Default)]
104pub struct OpEvmNetwork;
105impl FoundryEvmNetwork for OpEvmNetwork {
106    type Network = Optimism;
107    type EvmFactory = OpEvmFactory;
108}
109
110/// Convenience type aliases for accessing associated types through [`FoundryEvmNetwork`].
111pub type EvmFactoryFor<FEN> = <FEN as FoundryEvmNetwork>::EvmFactory;
112pub type FoundryContextFor<'db, FEN> =
113    <EvmFactoryFor<FEN> as FoundryEvmFactory>::FoundryContext<'db>;
114pub type TxEnvFor<FEN> = <EvmFactoryFor<FEN> as EvmFactory>::Tx;
115pub type HaltReasonFor<FEN> = <EvmFactoryFor<FEN> as EvmFactory>::HaltReason;
116pub type SpecFor<FEN> = <EvmFactoryFor<FEN> as EvmFactory>::Spec;
117pub type BlockEnvFor<FEN> = <EvmFactoryFor<FEN> as EvmFactory>::BlockEnv;
118pub type PrecompilesFor<FEN> = <EvmFactoryFor<FEN> as EvmFactory>::Precompiles;
119pub type EvmEnvFor<FEN> = EvmEnv<SpecFor<FEN>, BlockEnvFor<FEN>>;
120
121pub type NetworkFor<FEN> = <FEN as FoundryEvmNetwork>::Network;
122pub type TxEnvelopeFor<FEN> = <NetworkFor<FEN> as Network>::TxEnvelope;
123pub type TransactionRequestFor<FEN> = <NetworkFor<FEN> as Network>::TransactionRequest;
124pub type TransactionResponseFor<FEN> = <NetworkFor<FEN> as Network>::TransactionResponse;
125pub type BlockResponseFor<FEN> = <NetworkFor<FEN> as Network>::BlockResponse;
126
127pub trait FoundryEvmFactory:
128    EvmFactory<
129        Spec: Into<SpecId> + FromEvmVersion + Default + Copy + Unpin + Send + 'static,
130        BlockEnv: FoundryBlock + ForkBlockEnv + Default + Unpin,
131        Tx: Clone + Debug + FoundryTransaction + FromAnyRpcTransaction + Default + Send + Sync,
132        HaltReason: IntoInstructionResult,
133        Precompiles = PrecompilesMap,
134    > + Clone
135    + Debug
136    + Default
137    + 'static
138{
139    /// Foundry Context abstraction
140    type FoundryContext<'db>: FoundryContextExt<
141            Block = Self::BlockEnv,
142            Tx = Self::Tx,
143            Spec = Self::Spec,
144            Db: DatabaseExt<Self>,
145        >
146    where
147        Self: 'db;
148
149    /// The Foundry-wrapped EVM type produced by this factory.
150    type FoundryEvm<'db, I: FoundryInspectorExt<Self::FoundryContext<'db>>>: Evm<
151            DB = &'db mut dyn DatabaseExt<Self>,
152            Tx = Self::Tx,
153            BlockEnv = Self::BlockEnv,
154            Spec = Self::Spec,
155            HaltReason = Self::HaltReason,
156        > + Deref<Target = Self::FoundryContext<'db>>
157    where
158        Self: 'db;
159
160    /// Creates a Foundry-wrapped EVM with the given inspector.
161    fn create_foundry_evm_with_inspector<'db, I: FoundryInspectorExt<Self::FoundryContext<'db>>>(
162        &self,
163        db: &'db mut dyn DatabaseExt<Self>,
164        evm_env: EvmEnv<Self::Spec, Self::BlockEnv>,
165        inspector: I,
166    ) -> Self::FoundryEvm<'db, I>;
167
168    /// Creates a Foundry-wrapped EVM with a dynamic inspector, returning a boxed [`NestedEvm`].
169    ///
170    /// This helper exists because `&mut dyn FoundryInspectorExt<FoundryContext>` cannot satisfy
171    /// the generic `I: FoundryInspectorExt<Self::FoundryContext<'db>>` bound when the context
172    /// type is only known through an associated type.  Each concrete factory implements this
173    /// directly, side-stepping the higher-kinded lifetime issue.
174    fn create_foundry_nested_evm<'db>(
175        &self,
176        db: &'db mut dyn DatabaseExt<Self>,
177        evm_env: EvmEnv<Self::Spec, Self::BlockEnv>,
178        inspector: &'db mut dyn FoundryInspectorExt<Self::FoundryContext<'db>>,
179    ) -> Box<dyn NestedEvm<Spec = Self::Spec, Block = Self::BlockEnv, Tx = Self::Tx> + 'db>;
180}
181
182/// Object-safe trait exposing the operations that cheatcode nested EVM closures need.
183///
184/// This abstracts over the concrete EVM type (`FoundryEvm`, future `TempoEvm`, etc.)
185/// so that cheatcode impls can build and run nested EVMs without knowing the concrete type.
186pub trait NestedEvm {
187    /// The spec type.
188    type Spec;
189    /// The block environment type.
190    type Block;
191    /// The transaction environment type.
192    type Tx;
193
194    /// Returns a mutable reference to the journal inner state (`JournaledState`).
195    fn journal_inner_mut(&mut self) -> &mut JournaledState;
196
197    /// Runs a single execution frame (create or call) through the EVM handler loop.
198    fn run_execution(&mut self, frame: FrameInput) -> Result<FrameResult, EVMError<DatabaseError>>;
199
200    /// Executes a full transaction with the given tx env.
201    fn transact_raw(
202        &mut self,
203        tx: Self::Tx,
204    ) -> Result<ResultAndState<HaltReason>, EVMError<DatabaseError>>;
205
206    fn to_evm_env(&self) -> EvmEnv<Self::Spec, Self::Block>;
207}
208
209/// Closure type used by `CheatcodesExecutor` methods that run nested EVM operations.
210pub type NestedEvmClosure<'a, Spec, Block, Tx> =
211    &'a mut dyn FnMut(
212        &mut dyn NestedEvm<Spec = Spec, Block = Block, Tx = Tx>,
213    ) -> Result<(), EVMError<DatabaseError>>;
214
215/// Clones the current context (env + journal), passes the database, cloned env,
216/// and cloned journal inner to the callback. The callback builds whatever EVM it
217/// needs, runs its operations, and returns `(result, modified_env, modified_journal)`.
218/// Modified state is written back after the callback returns.
219pub fn with_cloned_context<CTX: FoundryContextExt>(
220    ecx: &mut CTX,
221    f: impl FnOnce(
222        &mut CTX::Db,
223        EvmEnv<CTX::Spec, CTX::Block>,
224        JournaledState,
225    )
226        -> Result<(EvmEnv<CTX::Spec, CTX::Block>, JournaledState), EVMError<DatabaseError>>,
227) -> Result<(), EVMError<DatabaseError>> {
228    let evm_env = ecx.evm_clone();
229
230    let (db, journal_inner) = ecx.db_journal_inner_mut();
231    let journal_inner_clone = journal_inner.clone();
232
233    let (sub_evm_env, sub_inner) = f(db, evm_env, journal_inner_clone)?;
234
235    // Write back modified state. The db borrow was released when f returned.
236    ecx.set_journal_inner(sub_inner);
237    ecx.set_evm(sub_evm_env);
238
239    Ok(())
240}
241
242/// Get the call inputs for the CREATE2 factory.
243pub fn get_create2_factory_call_inputs<T: JournalTr>(
244    salt: U256,
245    inputs: &CreateInputs,
246    deployer: Address,
247    journal: &mut T,
248) -> Result<CallInputs, <T::Database as Database>::Error> {
249    let calldata = [&salt.to_be_bytes::<32>()[..], &inputs.init_code()[..]].concat();
250    let account = journal.load_account_with_code(deployer)?;
251    Ok(CallInputs {
252        caller: inputs.caller(),
253        bytecode_address: deployer,
254        known_bytecode: (account.info.code_hash, account.info.code.clone().unwrap_or_default()),
255        target_address: deployer,
256        scheme: CallScheme::Call,
257        value: CallValue::Transfer(inputs.value()),
258        input: CallInput::Bytes(calldata.into()),
259        gas_limit: inputs.gas_limit(),
260        reservoir: inputs.reservoir(),
261        is_static: false,
262        return_memory_offset: 0..0,
263    })
264}
265
266/// Converts a network-specific halt reason into an [`InstructionResult`].
267pub trait IntoInstructionResult {
268    fn into_instruction_result(self) -> InstructionResult;
269}
270
271impl IntoInstructionResult for HaltReason {
272    fn into_instruction_result(self) -> InstructionResult {
273        self.into()
274    }
275}
276
277impl IntoInstructionResult for OpHaltReason {
278    fn into_instruction_result(self) -> InstructionResult {
279        match self {
280            Self::Base(eth) => eth.into(),
281            Self::FailedDeposit => InstructionResult::Stop,
282        }
283    }
284}
285
286impl IntoInstructionResult for TempoHaltReason {
287    fn into_instruction_result(self) -> InstructionResult {
288        match self {
289            Self::Ethereum(eth) => eth.into(),
290            _ => InstructionResult::PrecompileError,
291        }
292    }
293}