foundry_evm_core/
either_evm.rs

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
16/// Alias for result type returned by [`Evm::transact`] methods.
17type EitherEvmResult<DBError, HaltReason, TxError> =
18    Result<ResultAndState<HaltReason>, EVMError<DBError, TxError>>;
19
20/// Alias for result type returned by [`Evm::transact_commit`] methods.
21type EitherExecResult<DBError, HaltReason, TxError> =
22    Result<ExecutionResult<HaltReason>, EVMError<DBError, TxError>>;
23
24/// [`EitherEvm`] delegates its calls to one of the two evm implementations; either [`EthEvm`] or
25/// [`OpEvm`].
26///
27/// Calls are delegated to [`OpEvm`] only if optimism is enabled.
28///
29/// The call delegation is handled via its own implementation of the [`Evm`] trait.
30///
31/// The [`Evm::transact`] and other such calls work over the [`OpTransaction<TxEnv>`] type.
32///
33/// However, the [`Evm::HaltReason`] and [`Evm::Error`] leverage the optimism [`OpHaltReason`] and
34/// [`OpTransactionError`] as these are supersets of the eth types. This makes it easier to map eth
35/// types to op types and also prevents ignoring of any error that maybe thrown by [`OpEvm`].
36#[allow(clippy::large_enum_variant)]
37pub enum EitherEvm<DB, I, P>
38where
39    DB: Database,
40{
41    /// [`EthEvm`] implementation.
42    Eth(EthEvm<DB, I, P>),
43    /// [`OpEvm`] implementation.
44    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    /// Converts the [`EthEvm::transact`] result to [`EitherEvmResult`].
55    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                // Map the halt reason
62                Ok(result.map_haltreason(OpHaltReason::Base))
63            }
64            Err(e) => Err(self.map_eth_err(e)),
65        }
66    }
67
68    /// Converts the [`EthEvm::transact_commit`] result to [`EitherExecResult`].
69    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                // Map the halt reason
76                Ok(result.map_haltreason(OpHaltReason::Base))
77            }
78            Err(e) => Err(self.map_eth_err(e)),
79        }
80    }
81
82    /// Maps [`EVMError<DBError>`] to [`EVMError<DBError, OpTransactionError>`].
83    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
271/// Maps [`EvmEnv<OpSpecId>`] to [`EvmEnv`].
272fn 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}