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