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
101fn 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}