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