1use alloy_evm::{Evm, EvmEnv, EvmFactory};
2use alloy_primitives::Bytes;
3use foundry_evm_hardforks::TempoHardfork;
4use foundry_fork_db::DatabaseError;
5use revm::{
6 context::{
7 ContextTr, LocalContextTr,
8 result::{EVMError, HaltReason, ResultAndState},
9 },
10 handler::{EvmTr, FrameResult, Handler},
11 inspector::InspectorHandler,
12 interpreter::{FrameInput, SharedMemory, interpreter_action::FrameInit},
13 state::Bytecode,
14};
15use tempo_evm::{TempoBlockEnv, TempoEvmFactory, TempoHaltReason, evm::TempoEvm};
16use tempo_precompiles::storage::StorageCtx;
17use tempo_revm::{
18 TempoInvalidTransaction, TempoTxEnv, evm::TempoContext, gas_params::tempo_gas_params,
19 handler::TempoEvmHandler,
20};
21
22use crate::{
23 FoundryContextExt, FoundryInspectorExt,
24 backend::{DatabaseExt, JournaledState},
25 constants::{CALLER, TEST_CONTRACT_ADDRESS},
26 evm::{FoundryEvmFactory, NestedEvm},
27 tempo::{TEMPO_PRECOMPILE_ADDRESSES, TEMPO_TIP20_TOKENS, initialize_tempo_genesis_inner},
28};
29
30pub type TempoRevmEvm<'db, I> = tempo_revm::TempoEvm<&'db mut dyn DatabaseExt<TempoEvmFactory>, I>;
32
33pub(crate) fn initialize_tempo_evm<
42 'db,
43 I: FoundryInspectorExt<TempoContext<&'db mut dyn DatabaseExt<TempoEvmFactory>>>,
44>(
45 evm: &mut TempoEvm<&'db mut dyn DatabaseExt<TempoEvmFactory>, I>,
46 is_forked: bool,
47) {
48 let ctx = evm.ctx_mut();
49 StorageCtx::enter_evm(&mut ctx.journaled_state, &ctx.block, &ctx.cfg, &ctx.tx, || {
50 if is_forked {
51 let mut sctx = StorageCtx;
53 let sentinel = Bytecode::new_legacy(Bytes::from_static(&[0xef]));
54 for addr in TEMPO_PRECOMPILE_ADDRESSES.iter().chain(TEMPO_TIP20_TOKENS.iter()) {
55 sctx.set_code(*addr, sentinel.clone())
56 .expect("failed to warm tempo precompile address");
57 }
58 } else {
59 initialize_tempo_genesis_inner(TEST_CONTRACT_ADDRESS, CALLER)
61 .expect("tempo genesis initialization failed");
62 }
63 });
64}
65
66impl FoundryEvmFactory for TempoEvmFactory {
67 type FoundryContext<'db> = TempoContext<&'db mut dyn DatabaseExt<Self>>;
68
69 type FoundryEvm<'db, I: FoundryInspectorExt<Self::FoundryContext<'db>>> =
70 TempoEvm<&'db mut dyn DatabaseExt<Self>, I>;
71
72 fn create_foundry_evm_with_inspector<'db, I: FoundryInspectorExt<Self::FoundryContext<'db>>>(
73 &self,
74 db: &'db mut dyn DatabaseExt<Self>,
75 evm_env: EvmEnv<Self::Spec, Self::BlockEnv>,
76 inspector: I,
77 ) -> Self::FoundryEvm<'db, I> {
78 let is_forked = db.is_forked_mode();
79 let spec = *evm_env.spec_id();
80 let mut tempo_evm = Self::default().create_evm_with_inspector(db, evm_env, inspector);
81 tempo_evm.cfg.gas_params = tempo_gas_params(spec);
82 tempo_evm.cfg.tx_chain_id_check = true;
83 if tempo_evm.cfg.tx_gas_limit_cap.is_none() {
84 tempo_evm.cfg.tx_gas_limit_cap = spec.tx_gas_limit_cap();
85 }
86
87 let networks = tempo_evm.inspector().get_networks();
88 networks.inject_precompiles(tempo_evm.precompiles_mut());
89
90 initialize_tempo_evm(&mut tempo_evm, is_forked);
91 tempo_evm
92 }
93
94 fn create_foundry_nested_evm<'db>(
95 &self,
96 db: &'db mut dyn DatabaseExt<Self>,
97 evm_env: EvmEnv<Self::Spec, Self::BlockEnv>,
98 inspector: &'db mut dyn FoundryInspectorExt<Self::FoundryContext<'db>>,
99 ) -> Box<dyn NestedEvm<Spec = TempoHardfork, Block = TempoBlockEnv, Tx = TempoTxEnv> + 'db>
100 {
101 Box::new(self.create_foundry_evm_with_inspector(db, evm_env, inspector).into_inner())
102 }
103}
104
105pub(crate) fn map_tempo_error(
110 e: EVMError<DatabaseError, TempoInvalidTransaction>,
111) -> EVMError<DatabaseError> {
112 match e {
113 EVMError::Database(db) => EVMError::Database(db),
114 EVMError::Header(h) => EVMError::Header(h),
115 EVMError::Custom(s) => EVMError::Custom(s),
116 EVMError::CustomAny(custom_any_error) => EVMError::CustomAny(custom_any_error),
117 EVMError::Transaction(t) => match t {
118 TempoInvalidTransaction::EthInvalidTransaction(eth) => EVMError::Transaction(eth),
119 t => EVMError::Custom(format!("tempo transaction error: {t}")),
120 },
121 }
122}
123
124impl<'db, I: FoundryInspectorExt<TempoContext<&'db mut dyn DatabaseExt<TempoEvmFactory>>>> NestedEvm
125 for TempoRevmEvm<'db, I>
126{
127 type Spec = TempoHardfork;
128 type Block = TempoBlockEnv;
129 type Tx = TempoTxEnv;
130
131 fn journal_inner_mut(&mut self) -> &mut JournaledState {
132 &mut self.ctx_mut().journaled_state.inner
133 }
134
135 fn run_execution(&mut self, frame: FrameInput) -> Result<FrameResult, EVMError<DatabaseError>> {
136 let mut handler = TempoEvmHandler::new();
137
138 let memory =
139 SharedMemory::new_with_buffer(self.ctx_ref().local().shared_memory_buffer().clone());
140 let first_frame_input = FrameInit { depth: 0, memory, frame_input: frame };
141
142 let mut frame_result =
143 handler.inspect_run_exec_loop(self, first_frame_input).map_err(map_tempo_error)?;
144
145 handler.last_frame_result(self, &mut frame_result).map_err(map_tempo_error)?;
146
147 Ok(frame_result)
148 }
149
150 fn transact_raw(&mut self, tx: Self::Tx) -> Result<ResultAndState, EVMError<DatabaseError>> {
151 self.set_tx(tx);
152
153 let mut handler = TempoEvmHandler::new();
154 let result = handler.inspect_run(self).map_err(map_tempo_error)?;
155
156 let result = result.map_haltreason(|h| match h {
157 TempoHaltReason::Ethereum(eth) => eth,
158 _ => HaltReason::PrecompileError,
159 });
160
161 Ok(ResultAndState::new(result, self.ctx.journaled_state.inner.state.clone()))
162 }
163
164 fn to_evm_env(&self) -> EvmEnv<Self::Spec, Self::Block> {
165 self.ctx_ref().evm_clone()
166 }
167}