Skip to main content

foundry_evm_core/evm/
op.rs

1use alloy_evm::{Evm, EvmEnv, EvmFactory, precompiles::PrecompilesMap};
2use alloy_op_evm::{OpEvm, OpEvmContext, OpEvmFactory, OpTx};
3use foundry_fork_db::DatabaseError;
4use op_alloy_network::Optimism;
5use op_revm::{
6    L1BlockInfo, OpEvm as RevmEvm, OpHaltReason, OpSpecId, OpTransactionError, handler::OpHandler,
7};
8use revm::{
9    context::{
10        BlockEnv, ContextTr, Journal, LocalContextTr,
11        result::{EVMError, HaltReason, ResultAndState},
12    },
13    handler::{EthFrame, EvmTr, FrameResult, Handler, instructions::EthInstructions},
14    inspector::InspectorHandler,
15    interpreter::{
16        FrameInput, GasTracker, InstructionResult, SharedMemory, interpreter::EthInterpreter,
17        interpreter_action::FrameInit,
18    },
19};
20
21use crate::{
22    FoundryChain, FoundryContextExt, FoundryInspectorExt,
23    backend::{DatabaseExt, JournaledState},
24    evm::{FoundryEvmFactory, FoundryEvmNetwork, IntoInstructionResult, NestedEvm, NestedEvmFor},
25};
26
27impl FoundryChain<OpTx> for L1BlockInfo {}
28
29#[derive(Clone, Copy, Debug, Default)]
30pub struct OpEvmNetwork;
31impl FoundryEvmNetwork for OpEvmNetwork {
32    type Network = Optimism;
33    type EvmFactory = OpEvmFactory;
34}
35
36impl IntoInstructionResult for OpHaltReason {
37    fn into_instruction_result(self) -> InstructionResult {
38        match self {
39            Self::Base(eth) => eth.into(),
40            Self::FailedDeposit => InstructionResult::Stop,
41        }
42    }
43}
44
45type OpEvmHandler<'db, I> =
46    OpHandler<OpRevmEvm<'db, I>, EVMError<DatabaseError, OpTransactionError>, EthFrame>;
47
48pub type OpRevmEvm<'db, I> = RevmEvm<
49    OpEvmContext<&'db mut dyn DatabaseExt<OpEvmFactory>>,
50    I,
51    EthInstructions<EthInterpreter, OpEvmContext<&'db mut dyn DatabaseExt<OpEvmFactory>>>,
52    PrecompilesMap,
53>;
54
55impl FoundryEvmFactory for OpEvmFactory {
56    type Chain = L1BlockInfo;
57    type FoundryContext<'db> = OpEvmContext<&'db mut dyn DatabaseExt<Self>>;
58
59    type FoundryEvm<'db, I: FoundryInspectorExt<Self::FoundryContext<'db>>> =
60        OpEvm<&'db mut dyn DatabaseExt<Self>, I, Self::Precompiles>;
61
62    fn create_evm_with_context<DB: alloy_evm::Database>(
63        &self,
64        db: DB,
65        evm_env: EvmEnv<Self::Spec, Self::BlockEnv>,
66        chain_context: Self::Chain,
67    ) -> Self::Evm<DB, revm::inspector::NoOpInspector> {
68        let mut evm = self.create_evm(db, evm_env);
69        evm.ctx_mut().chain = chain_context;
70        evm
71    }
72
73    fn create_foundry_evm_with_inspector<'db, I: FoundryInspectorExt<Self::FoundryContext<'db>>>(
74        &self,
75        db: &'db mut dyn DatabaseExt<Self>,
76        evm_env: EvmEnv<Self::Spec, Self::BlockEnv>,
77        chain_context: Self::Chain,
78        inspector: I,
79    ) -> Self::FoundryEvm<'db, I> {
80        let mut op_evm = Self::default().create_evm_with_inspector(db, evm_env, inspector);
81        op_evm.ctx_mut().chain = chain_context;
82        op_evm.cfg.tx_chain_id_check = true;
83        op_evm.inspector().get_networks().inject_precompiles(op_evm.precompiles_mut());
84        op_evm
85    }
86
87    fn create_foundry_nested_evm<'db>(
88        &self,
89        db: &'db mut dyn DatabaseExt<Self>,
90        evm_env: EvmEnv<Self::Spec, Self::BlockEnv>,
91        chain_context: Self::Chain,
92        inspector: &'db mut dyn FoundryInspectorExt<Self::FoundryContext<'db>>,
93    ) -> NestedEvmFor<'db, Self> {
94        Box::new(
95            self.create_foundry_evm_with_inspector(db, evm_env, chain_context, inspector)
96                .into_inner(),
97        )
98    }
99}
100
101/// Maps an OP [`EVMError`] to the common `EVMError<DatabaseError>` used by [`NestedEvm`].
102fn map_op_error(e: EVMError<DatabaseError, OpTransactionError>) -> EVMError<DatabaseError> {
103    match e {
104        EVMError::Database(db) => EVMError::Database(db),
105        EVMError::Header(h) => EVMError::Header(h),
106        EVMError::Custom(s) => EVMError::Custom(s),
107        EVMError::Transaction(t) => EVMError::Custom(format!("op transaction error: {t}")),
108        EVMError::CustomAny(custom_any_error) => EVMError::CustomAny(custom_any_error),
109    }
110}
111
112impl<'db, I: FoundryInspectorExt<OpEvmContext<&'db mut dyn DatabaseExt<OpEvmFactory>>>> NestedEvm
113    for OpRevmEvm<'db, I>
114{
115    type Spec = OpSpecId;
116    type Block = BlockEnv;
117    type Tx = OpTx;
118    type Chain = L1BlockInfo;
119    type Journal = Journal<&'db mut dyn DatabaseExt<OpEvmFactory>>;
120
121    fn tx_mut(&mut self) -> &mut Self::Tx {
122        self.ctx_mut().tx_mut()
123    }
124
125    fn journal_inner_mut(&mut self) -> &mut JournaledState {
126        &mut self.ctx().journaled_state.inner
127    }
128
129    fn chain_mut(&mut self) -> &mut Self::Chain {
130        &mut self.ctx_mut().chain
131    }
132
133    fn journal_mut(&mut self) -> &mut Self::Journal {
134        &mut self.ctx_mut().journaled_state
135    }
136
137    fn run_execution(&mut self, frame: FrameInput) -> Result<FrameResult, EVMError<DatabaseError>> {
138        let mut handler = OpEvmHandler::<I>::new();
139        let memory =
140            SharedMemory::new_with_buffer(self.ctx_ref().local().shared_memory_buffer().clone());
141        let first_frame_input = FrameInit { depth: 0, memory, frame_input: frame };
142
143        let mut frame_result =
144            handler.inspect_run_exec_loop(self, first_frame_input).map_err(map_op_error)?;
145
146        let mut parent_gas = GasTracker::new(
147            frame_result.gas().limit(),
148            frame_result.gas().remaining(),
149            frame_result.gas().reservoir(),
150        );
151        handler
152            .last_frame_result(self, &mut frame_result, &mut parent_gas)
153            .map_err(map_op_error)?;
154
155        Ok(frame_result)
156    }
157
158    fn transact_raw(&mut self, tx: Self::Tx) -> eyre::Result<ResultAndState<HaltReason>> {
159        self.ctx().set_tx(tx);
160
161        let mut handler = OpEvmHandler::<I>::new();
162        let result = handler.inspect_run(self).map_err(map_op_error)?;
163
164        let result = result.map_haltreason(|h| match h {
165            OpHaltReason::Base(eth) => eth,
166            _ => HaltReason::PrecompileError,
167        });
168
169        Ok(ResultAndState::new(result, self.ctx_ref().journaled_state.inner.state.clone()))
170    }
171
172    fn to_evm_env(&self) -> EvmEnv<Self::Spec, Self::Block> {
173        self.ctx_ref().evm_clone()
174    }
175}