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, Journal, LocalContextTr,
8 result::{EVMError, HaltReason, ResultAndState},
9 },
10 handler::{EvmTr, FrameResult, Handler},
11 inspector::InspectorHandler,
12 interpreter::{FrameInput, GasTracker, SharedMemory, interpreter_action::FrameInit},
13 state::Bytecode,
14};
15use tempo_evm::{TempoBlockEnv, TempoEvmFactory, TempoHaltReason, evm::TempoEvm};
16use tempo_precompiles::{
17 extend_tempo_precompiles,
18 storage::{StorageActions, StorageCtx},
19};
20use tempo_revm::{
21 TempoInvalidTransaction, TempoTxEnv, evm::TempoContext, gas_params::tempo_gas_params,
22 handler::TempoEvmHandler,
23};
24
25use crate::{
26 FoundryContextExt, FoundryInspectorExt,
27 backend::{DatabaseExt, JournaledState},
28 constants::{CALLER, TEST_CONTRACT_ADDRESS},
29 evm::{FoundryEvmFactory, NestedEvm, NestedEvmFor},
30 tempo::{TEMPO_PRECOMPILE_ADDRESSES, TEMPO_TIP20_TOKENS, initialize_tempo_test_genesis_inner},
31};
32
33pub type TempoRevmEvm<'db, I> = tempo_revm::TempoEvm<&'db mut dyn DatabaseExt<TempoEvmFactory>, I>;
35
36pub(crate) fn initialize_tempo_evm<
45 'db,
46 I: FoundryInspectorExt<TempoContext<&'db mut dyn DatabaseExt<TempoEvmFactory>>>,
47>(
48 evm: &mut TempoEvm<&'db mut dyn DatabaseExt<TempoEvmFactory>, I>,
49 is_forked: bool,
50) {
51 let ctx = evm.ctx_mut();
52 StorageCtx::enter_evm(
53 &mut ctx.journaled_state,
54 &ctx.block,
55 &ctx.cfg,
56 &ctx.tx,
57 StorageActions::disabled(),
58 || {
59 if is_forked {
60 let mut sctx = StorageCtx;
62 let sentinel = Bytecode::new_legacy(Bytes::from_static(&[0xef]));
63 for addr in TEMPO_PRECOMPILE_ADDRESSES
64 .iter()
65 .copied()
66 .chain(TEMPO_TIP20_TOKENS.iter().copied())
67 {
68 sctx.set_code(addr, sentinel.clone())
69 .expect("failed to warm tempo precompile address");
70 }
71 } else {
72 initialize_tempo_test_genesis_inner(TEST_CONTRACT_ADDRESS, CALLER)
74 .expect("tempo genesis initialization failed");
75 }
76 },
77 );
78}
79
80impl FoundryEvmFactory for TempoEvmFactory {
81 type Chain = ();
82 type FoundryContext<'db> = TempoContext<&'db mut dyn DatabaseExt<Self>>;
83
84 type FoundryEvm<'db, I: FoundryInspectorExt<Self::FoundryContext<'db>>> =
85 TempoEvm<&'db mut dyn DatabaseExt<Self>, I>;
86
87 fn create_evm_with_context<DB: alloy_evm::Database>(
88 &self,
89 db: DB,
90 evm_env: EvmEnv<Self::Spec, Self::BlockEnv>,
91 _chain_context: Self::Chain,
92 ) -> Self::Evm<DB, revm::inspector::NoOpInspector> {
93 self.create_evm(db, evm_env)
94 }
95
96 fn create_foundry_evm_with_inspector<'db, I: FoundryInspectorExt<Self::FoundryContext<'db>>>(
97 &self,
98 db: &'db mut dyn DatabaseExt<Self>,
99 evm_env: EvmEnv<Self::Spec, Self::BlockEnv>,
100 _chain_context: Self::Chain,
101 inspector: I,
102 ) -> Self::FoundryEvm<'db, I> {
103 let is_forked = db.is_forked_mode();
104 let spec = *evm_env.spec_id();
105 let mut tempo_evm = Self::default().create_evm_with_inspector(db, evm_env, inspector);
106 tempo_evm.cfg.gas_params = tempo_gas_params(spec);
107 tempo_evm.cfg.tx_chain_id_check = true;
108 if tempo_evm.cfg.tx_gas_limit_cap.is_none() {
109 tempo_evm.cfg.tx_gas_limit_cap = spec.tx_gas_limit_cap();
110 }
111
112 let networks = tempo_evm.inspector().get_networks();
113 networks.inject_precompiles(tempo_evm.precompiles_mut());
114 let cfg = tempo_evm.cfg.clone();
116 let non_creditable_slots = tempo_evm.non_creditable_slots();
117 extend_tempo_precompiles(
118 tempo_evm.precompiles_mut(),
119 &cfg,
120 StorageActions::disabled(),
121 non_creditable_slots,
122 );
123
124 initialize_tempo_evm(&mut tempo_evm, is_forked);
125 tempo_evm
126 }
127
128 fn create_foundry_nested_evm<'db>(
129 &self,
130 db: &'db mut dyn DatabaseExt<Self>,
131 evm_env: EvmEnv<Self::Spec, Self::BlockEnv>,
132 chain_context: Self::Chain,
133 inspector: &'db mut dyn FoundryInspectorExt<Self::FoundryContext<'db>>,
134 ) -> NestedEvmFor<'db, Self> {
135 Box::new(
136 self.create_foundry_evm_with_inspector(db, evm_env, chain_context, inspector)
137 .into_inner(),
138 )
139 }
140}
141
142pub(crate) fn map_tempo_error(
147 e: EVMError<DatabaseError, TempoInvalidTransaction>,
148) -> EVMError<DatabaseError> {
149 match e {
150 EVMError::Database(db) => EVMError::Database(db),
151 EVMError::Header(h) => EVMError::Header(h),
152 EVMError::Custom(s) => EVMError::Custom(s),
153 EVMError::CustomAny(custom_any_error) => EVMError::CustomAny(custom_any_error),
154 EVMError::Transaction(t) => match t {
155 TempoInvalidTransaction::EthInvalidTransaction(eth) => EVMError::Transaction(eth),
156 t => EVMError::Custom(format!("tempo transaction error: {t}")),
157 },
158 }
159}
160
161impl<'db, I: FoundryInspectorExt<TempoContext<&'db mut dyn DatabaseExt<TempoEvmFactory>>>> NestedEvm
162 for TempoRevmEvm<'db, I>
163{
164 type Spec = TempoHardfork;
165 type Block = TempoBlockEnv;
166 type Tx = TempoTxEnv;
167 type Chain = ();
168 type Journal = Journal<&'db mut dyn DatabaseExt<TempoEvmFactory>>;
169
170 fn tx_mut(&mut self) -> &mut Self::Tx {
171 self.ctx_mut().tx_mut()
172 }
173
174 fn journal_inner_mut(&mut self) -> &mut JournaledState {
175 &mut self.ctx_mut().journaled_state.inner
176 }
177
178 fn chain_mut(&mut self) -> &mut Self::Chain {
179 &mut self.ctx_mut().chain
180 }
181
182 fn journal_mut(&mut self) -> &mut Self::Journal {
183 &mut self.ctx_mut().journaled_state
184 }
185
186 fn run_execution(&mut self, frame: FrameInput) -> Result<FrameResult, EVMError<DatabaseError>> {
187 let mut handler = TempoEvmHandler::new();
188 let memory =
189 SharedMemory::new_with_buffer(self.ctx_ref().local().shared_memory_buffer().clone());
190 let first_frame_input = FrameInit { depth: 0, memory, frame_input: frame };
191
192 let mut frame_result =
193 handler.inspect_run_exec_loop(self, first_frame_input).map_err(map_tempo_error)?;
194
195 let mut parent_gas = GasTracker::new(
196 frame_result.gas().limit(),
197 frame_result.gas().remaining(),
198 frame_result.gas().reservoir(),
199 );
200 handler
201 .last_frame_result(self, &mut frame_result, &mut parent_gas)
202 .map_err(map_tempo_error)?;
203
204 Ok(frame_result)
205 }
206
207 fn transact_raw(&mut self, tx: Self::Tx) -> eyre::Result<ResultAndState> {
208 self.set_tx(tx);
209
210 let mut handler = TempoEvmHandler::new();
211 let result = handler.inspect_run(self).map_err(map_tempo_error)?;
212
213 let result = result.map_haltreason(|h| match h {
214 TempoHaltReason::Ethereum(eth) => eth,
215 _ => HaltReason::PrecompileError,
216 });
217
218 Ok(ResultAndState::new(result, self.ctx.journaled_state.inner.state.clone()))
219 }
220
221 fn to_evm_env(&self) -> EvmEnv<Self::Spec, Self::Block> {
222 self.ctx_ref().evm_clone()
223 }
224}