1use alloy_evm::{eth::EthEvmContext, Database, EthEvm, Evm, EvmEnv};
2use alloy_op_evm::OpEvm;
3use alloy_primitives::{Address, Bytes};
4use op_revm::{OpContext, OpHaltReason, OpSpecId, OpTransaction, OpTransactionError};
5use revm::{
6 context::{
7 result::{EVMError, ExecutionResult, HaltReason, ResultAndState},
8 BlockEnv, TxEnv,
9 },
10 handler::PrecompileProvider,
11 interpreter::InterpreterResult,
12 primitives::hardfork::SpecId,
13 DatabaseCommit, Inspector,
14};
15
16type EitherEvmResult<DBError, HaltReason, TxError> =
18 Result<ResultAndState<HaltReason>, EVMError<DBError, TxError>>;
19
20type EitherExecResult<DBError, HaltReason, TxError> =
22 Result<ExecutionResult<HaltReason>, EVMError<DBError, TxError>>;
23
24#[allow(clippy::large_enum_variant)]
37pub enum EitherEvm<DB, I, P>
38where
39 DB: Database,
40{
41 Eth(EthEvm<DB, I, P>),
43 Op(OpEvm<DB, I, P>),
45}
46
47impl<DB, I, P> EitherEvm<DB, I, P>
48where
49 DB: Database,
50 I: Inspector<EthEvmContext<DB>> + Inspector<OpContext<DB>>,
51 P: PrecompileProvider<EthEvmContext<DB>, Output = InterpreterResult>
52 + PrecompileProvider<OpContext<DB>, Output = InterpreterResult>,
53{
54 fn map_eth_result(
56 &self,
57 result: Result<ResultAndState<HaltReason>, EVMError<DB::Error>>,
58 ) -> EitherEvmResult<DB::Error, OpHaltReason, OpTransactionError> {
59 match result {
60 Ok(result) => {
61 Ok(result.map_haltreason(OpHaltReason::Base))
63 }
64 Err(e) => Err(self.map_eth_err(e)),
65 }
66 }
67
68 fn map_exec_result(
70 &self,
71 result: Result<ExecutionResult, EVMError<DB::Error>>,
72 ) -> EitherExecResult<DB::Error, OpHaltReason, OpTransactionError> {
73 match result {
74 Ok(result) => {
75 Ok(result.map_haltreason(OpHaltReason::Base))
77 }
78 Err(e) => Err(self.map_eth_err(e)),
79 }
80 }
81
82 fn map_eth_err(&self, err: EVMError<DB::Error>) -> EVMError<DB::Error, OpTransactionError> {
84 match err {
85 EVMError::Transaction(invalid_tx) => {
86 EVMError::Transaction(OpTransactionError::Base(invalid_tx))
87 }
88 EVMError::Database(e) => EVMError::Database(e),
89 EVMError::Header(e) => EVMError::Header(e),
90 EVMError::Custom(e) => EVMError::Custom(e),
91 }
92 }
93}
94
95impl<DB, I, P> Evm for EitherEvm<DB, I, P>
96where
97 DB: Database,
98 I: Inspector<EthEvmContext<DB>> + Inspector<OpContext<DB>>,
99 P: PrecompileProvider<EthEvmContext<DB>, Output = InterpreterResult>
100 + PrecompileProvider<OpContext<DB>, Output = InterpreterResult>,
101{
102 type DB = DB;
103 type Error = EVMError<DB::Error, OpTransactionError>;
104 type HaltReason = OpHaltReason;
105 type Tx = OpTransaction<TxEnv>;
106 type Inspector = I;
107 type Precompiles = P;
108 type Spec = SpecId;
109
110 fn chain_id(&self) -> u64 {
111 match self {
112 Self::Eth(evm) => evm.chain_id(),
113 Self::Op(evm) => evm.chain_id(),
114 }
115 }
116
117 fn block(&self) -> &BlockEnv {
118 match self {
119 Self::Eth(evm) => evm.block(),
120 Self::Op(evm) => evm.block(),
121 }
122 }
123
124 fn db_mut(&mut self) -> &mut Self::DB {
125 match self {
126 Self::Eth(evm) => evm.db_mut(),
127 Self::Op(evm) => evm.db_mut(),
128 }
129 }
130
131 fn into_db(self) -> Self::DB
132 where
133 Self: Sized,
134 {
135 match self {
136 Self::Eth(evm) => evm.into_db(),
137 Self::Op(evm) => evm.into_db(),
138 }
139 }
140
141 fn finish(self) -> (Self::DB, EvmEnv<Self::Spec>)
142 where
143 Self: Sized,
144 {
145 match self {
146 Self::Eth(evm) => evm.finish(),
147 Self::Op(evm) => {
148 let (db, env) = evm.finish();
149 (db, map_env(env))
150 }
151 }
152 }
153
154 fn precompiles(&self) -> &Self::Precompiles {
155 match self {
156 Self::Eth(evm) => evm.precompiles(),
157 Self::Op(evm) => evm.precompiles(),
158 }
159 }
160
161 fn precompiles_mut(&mut self) -> &mut Self::Precompiles {
162 match self {
163 Self::Eth(evm) => evm.precompiles_mut(),
164 Self::Op(evm) => evm.precompiles_mut(),
165 }
166 }
167
168 fn inspector(&self) -> &Self::Inspector {
169 match self {
170 Self::Eth(evm) => evm.inspector(),
171 Self::Op(evm) => evm.inspector(),
172 }
173 }
174
175 fn inspector_mut(&mut self) -> &mut Self::Inspector {
176 match self {
177 Self::Eth(evm) => evm.inspector_mut(),
178 Self::Op(evm) => evm.inspector_mut(),
179 }
180 }
181
182 fn enable_inspector(&mut self) {
183 match self {
184 Self::Eth(evm) => evm.enable_inspector(),
185 Self::Op(evm) => evm.enable_inspector(),
186 }
187 }
188
189 fn disable_inspector(&mut self) {
190 match self {
191 Self::Eth(evm) => evm.disable_inspector(),
192 Self::Op(evm) => evm.disable_inspector(),
193 }
194 }
195
196 fn set_inspector_enabled(&mut self, enabled: bool) {
197 match self {
198 Self::Eth(evm) => evm.set_inspector_enabled(enabled),
199 Self::Op(evm) => evm.set_inspector_enabled(enabled),
200 }
201 }
202
203 fn into_env(self) -> EvmEnv<Self::Spec>
204 where
205 Self: Sized,
206 {
207 match self {
208 Self::Eth(evm) => evm.into_env(),
209 Self::Op(evm) => map_env(evm.into_env()),
210 }
211 }
212
213 fn transact(
214 &mut self,
215 tx: impl alloy_evm::IntoTxEnv<Self::Tx>,
216 ) -> Result<ResultAndState<Self::HaltReason>, Self::Error> {
217 match self {
218 Self::Eth(evm) => {
219 let eth = evm.transact(tx.into_tx_env().base);
220 self.map_eth_result(eth)
221 }
222 Self::Op(evm) => evm.transact(tx),
223 }
224 }
225
226 fn transact_commit(
227 &mut self,
228 tx: impl alloy_evm::IntoTxEnv<Self::Tx>,
229 ) -> Result<ExecutionResult<Self::HaltReason>, Self::Error>
230 where
231 Self::DB: DatabaseCommit,
232 {
233 match self {
234 Self::Eth(evm) => {
235 let eth = evm.transact_commit(tx.into_tx_env().base);
236 self.map_exec_result(eth)
237 }
238 Self::Op(evm) => evm.transact_commit(tx),
239 }
240 }
241
242 fn transact_raw(
243 &mut self,
244 tx: Self::Tx,
245 ) -> Result<ResultAndState<Self::HaltReason>, Self::Error> {
246 match self {
247 Self::Eth(evm) => {
248 let res = evm.transact_raw(tx.base);
249 self.map_eth_result(res)
250 }
251 Self::Op(evm) => evm.transact_raw(tx),
252 }
253 }
254
255 fn transact_system_call(
256 &mut self,
257 caller: Address,
258 contract: Address,
259 data: Bytes,
260 ) -> Result<ResultAndState<Self::HaltReason>, Self::Error> {
261 match self {
262 Self::Eth(evm) => {
263 let eth = evm.transact_system_call(caller, contract, data);
264 self.map_eth_result(eth)
265 }
266 Self::Op(evm) => evm.transact_system_call(caller, contract, data),
267 }
268 }
269}
270
271fn map_env(env: EvmEnv<OpSpecId>) -> EvmEnv {
273 let eth_spec_id = env.spec_id().into_eth_spec();
274 let cfg = env.cfg_env.with_spec(eth_spec_id);
275 EvmEnv { cfg_env: cfg, block_env: env.block_env }
276}