foundry_evm_core/
either_evm.rs

1use alloy_evm::{Database, EthEvm, Evm, EvmEnv, eth::EthEvmContext};
2use alloy_op_evm::OpEvm;
3use alloy_primitives::{Address, Bytes};
4use op_revm::{OpContext, OpHaltReason, OpSpecId, OpTransaction, OpTransactionError};
5use revm::{
6    DatabaseCommit, Inspector,
7    context::{
8        BlockEnv, TxEnv,
9        result::{EVMError, ExecResultAndState, ExecutionResult, ResultAndState},
10    },
11    handler::PrecompileProvider,
12    interpreter::InterpreterResult,
13    primitives::hardfork::SpecId,
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<ExecResultAndState<ExecutionResult>, EVMError<DB::Error>>,
58    ) -> EitherEvmResult<DB::Error, OpHaltReason, OpTransactionError> {
59        match result {
60            Ok(result) => Ok(ResultAndState {
61                result: result.result.map_haltreason(OpHaltReason::Base),
62                state: result.state,
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 block(&self) -> &BlockEnv {
111        match self {
112            Self::Eth(evm) => evm.block(),
113            Self::Op(evm) => evm.block(),
114        }
115    }
116
117    fn chain_id(&self) -> u64 {
118        match self {
119            Self::Eth(evm) => evm.chain_id(),
120            Self::Op(evm) => evm.chain_id(),
121        }
122    }
123
124    fn components(&self) -> (&Self::DB, &Self::Inspector, &Self::Precompiles) {
125        match self {
126            Self::Eth(evm) => evm.components(),
127            Self::Op(evm) => evm.components(),
128        }
129    }
130
131    fn components_mut(&mut self) -> (&mut Self::DB, &mut Self::Inspector, &mut Self::Precompiles) {
132        match self {
133            Self::Eth(evm) => evm.components_mut(),
134            Self::Op(evm) => evm.components_mut(),
135        }
136    }
137
138    fn db_mut(&mut self) -> &mut Self::DB {
139        match self {
140            Self::Eth(evm) => evm.db_mut(),
141            Self::Op(evm) => evm.db_mut(),
142        }
143    }
144
145    fn into_db(self) -> Self::DB
146    where
147        Self: Sized,
148    {
149        match self {
150            Self::Eth(evm) => evm.into_db(),
151            Self::Op(evm) => evm.into_db(),
152        }
153    }
154
155    fn finish(self) -> (Self::DB, EvmEnv<Self::Spec>)
156    where
157        Self: Sized,
158    {
159        match self {
160            Self::Eth(evm) => evm.finish(),
161            Self::Op(evm) => {
162                let (db, env) = evm.finish();
163                (db, map_env(env))
164            }
165        }
166    }
167
168    fn precompiles(&self) -> &Self::Precompiles {
169        match self {
170            Self::Eth(evm) => evm.precompiles(),
171            Self::Op(evm) => evm.precompiles(),
172        }
173    }
174
175    fn precompiles_mut(&mut self) -> &mut Self::Precompiles {
176        match self {
177            Self::Eth(evm) => evm.precompiles_mut(),
178            Self::Op(evm) => evm.precompiles_mut(),
179        }
180    }
181
182    fn inspector(&self) -> &Self::Inspector {
183        match self {
184            Self::Eth(evm) => evm.inspector(),
185            Self::Op(evm) => evm.inspector(),
186        }
187    }
188
189    fn inspector_mut(&mut self) -> &mut Self::Inspector {
190        match self {
191            Self::Eth(evm) => evm.inspector_mut(),
192            Self::Op(evm) => evm.inspector_mut(),
193        }
194    }
195
196    fn enable_inspector(&mut self) {
197        match self {
198            Self::Eth(evm) => evm.enable_inspector(),
199            Self::Op(evm) => evm.enable_inspector(),
200        }
201    }
202
203    fn disable_inspector(&mut self) {
204        match self {
205            Self::Eth(evm) => evm.disable_inspector(),
206            Self::Op(evm) => evm.disable_inspector(),
207        }
208    }
209
210    fn set_inspector_enabled(&mut self, enabled: bool) {
211        match self {
212            Self::Eth(evm) => evm.set_inspector_enabled(enabled),
213            Self::Op(evm) => evm.set_inspector_enabled(enabled),
214        }
215    }
216
217    fn into_env(self) -> EvmEnv<Self::Spec>
218    where
219        Self: Sized,
220    {
221        match self {
222            Self::Eth(evm) => evm.into_env(),
223            Self::Op(evm) => map_env(evm.into_env()),
224        }
225    }
226
227    fn transact(
228        &mut self,
229        tx: impl alloy_evm::IntoTxEnv<Self::Tx>,
230    ) -> Result<ResultAndState<Self::HaltReason>, Self::Error> {
231        match self {
232            Self::Eth(evm) => {
233                let eth = evm.transact(tx.into_tx_env().base);
234                self.map_eth_result(eth)
235            }
236            Self::Op(evm) => evm.transact(tx),
237        }
238    }
239
240    fn transact_commit(
241        &mut self,
242        tx: impl alloy_evm::IntoTxEnv<Self::Tx>,
243    ) -> Result<ExecutionResult<Self::HaltReason>, Self::Error>
244    where
245        Self::DB: DatabaseCommit,
246    {
247        match self {
248            Self::Eth(evm) => {
249                let eth = evm.transact_commit(tx.into_tx_env().base);
250                self.map_exec_result(eth)
251            }
252            Self::Op(evm) => evm.transact_commit(tx),
253        }
254    }
255
256    fn transact_raw(
257        &mut self,
258        tx: Self::Tx,
259    ) -> Result<ResultAndState<Self::HaltReason>, Self::Error> {
260        match self {
261            Self::Eth(evm) => {
262                let res = evm.transact_raw(tx.base);
263                self.map_eth_result(res)
264            }
265            Self::Op(evm) => evm.transact_raw(tx),
266        }
267    }
268
269    fn transact_system_call(
270        &mut self,
271        caller: Address,
272        contract: Address,
273        data: Bytes,
274    ) -> Result<ResultAndState<Self::HaltReason>, Self::Error> {
275        match self {
276            Self::Eth(evm) => {
277                let eth = evm.transact_system_call(caller, contract, data);
278                self.map_eth_result(eth)
279            }
280            Self::Op(evm) => evm.transact_system_call(caller, contract, data),
281        }
282    }
283}
284
285/// Maps [`EvmEnv<OpSpecId>`] to [`EvmEnv`].
286fn map_env(env: EvmEnv<OpSpecId>) -> EvmEnv {
287    let eth_spec_id = env.spec_id().into_eth_spec();
288    let cfg = env.cfg_env.with_spec(eth_spec_id);
289    EvmEnv { cfg_env: cfg, block_env: env.block_env }
290}