Skip to main content

foundry_evm_core/evm/
mod.rs

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