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::{
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},
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 FoundryContext<'db> = TempoContext<&'db mut dyn DatabaseExt<Self>>;
82
83 type FoundryEvm<'db, I: FoundryInspectorExt<Self::FoundryContext<'db>>> =
84 TempoEvm<&'db mut dyn DatabaseExt<Self>, I>;
85
86 fn create_foundry_evm_with_inspector<'db, I: FoundryInspectorExt<Self::FoundryContext<'db>>>(
87 &self,
88 db: &'db mut dyn DatabaseExt<Self>,
89 evm_env: EvmEnv<Self::Spec, Self::BlockEnv>,
90 inspector: I,
91 ) -> Self::FoundryEvm<'db, I> {
92 let is_forked = db.is_forked_mode();
93 let spec = *evm_env.spec_id();
94 let mut tempo_evm = Self::default().create_evm_with_inspector(db, evm_env, inspector);
95 tempo_evm.cfg.gas_params = tempo_gas_params(spec);
96 tempo_evm.cfg.tx_chain_id_check = true;
97 if tempo_evm.cfg.tx_gas_limit_cap.is_none() {
98 tempo_evm.cfg.tx_gas_limit_cap = spec.tx_gas_limit_cap();
99 }
100
101 let networks = tempo_evm.inspector().get_networks();
102 networks.inject_precompiles(tempo_evm.precompiles_mut());
103 let cfg = tempo_evm.cfg.clone();
105 let non_creditable_slots = tempo_evm.non_creditable_slots();
106 extend_tempo_precompiles(
107 tempo_evm.precompiles_mut(),
108 &cfg,
109 StorageActions::disabled(),
110 non_creditable_slots,
111 );
112
113 initialize_tempo_evm(&mut tempo_evm, is_forked);
114 tempo_evm
115 }
116
117 fn create_foundry_nested_evm<'db>(
118 &self,
119 db: &'db mut dyn DatabaseExt<Self>,
120 evm_env: EvmEnv<Self::Spec, Self::BlockEnv>,
121 inspector: &'db mut dyn FoundryInspectorExt<Self::FoundryContext<'db>>,
122 ) -> Box<dyn NestedEvm<Spec = TempoHardfork, Block = TempoBlockEnv, Tx = TempoTxEnv> + 'db>
123 {
124 Box::new(self.create_foundry_evm_with_inspector(db, evm_env, inspector).into_inner())
125 }
126}
127
128pub(crate) fn map_tempo_error(
133 e: EVMError<DatabaseError, TempoInvalidTransaction>,
134) -> EVMError<DatabaseError> {
135 match e {
136 EVMError::Database(db) => EVMError::Database(db),
137 EVMError::Header(h) => EVMError::Header(h),
138 EVMError::Custom(s) => EVMError::Custom(s),
139 EVMError::CustomAny(custom_any_error) => EVMError::CustomAny(custom_any_error),
140 EVMError::Transaction(t) => match t {
141 TempoInvalidTransaction::EthInvalidTransaction(eth) => EVMError::Transaction(eth),
142 t => EVMError::Custom(format!("tempo transaction error: {t}")),
143 },
144 }
145}
146
147impl<'db, I: FoundryInspectorExt<TempoContext<&'db mut dyn DatabaseExt<TempoEvmFactory>>>> NestedEvm
148 for TempoRevmEvm<'db, I>
149{
150 type Spec = TempoHardfork;
151 type Block = TempoBlockEnv;
152 type Tx = TempoTxEnv;
153
154 fn journal_inner_mut(&mut self) -> &mut JournaledState {
155 &mut self.ctx_mut().journaled_state.inner
156 }
157
158 fn run_execution(&mut self, frame: FrameInput) -> Result<FrameResult, EVMError<DatabaseError>> {
159 let mut handler = TempoEvmHandler::new();
160 let reservoir = frame.reservoir();
161
162 let memory =
163 SharedMemory::new_with_buffer(self.ctx_ref().local().shared_memory_buffer().clone());
164 let first_frame_input = FrameInit { depth: 0, memory, frame_input: frame };
165
166 let mut frame_result =
167 handler.inspect_run_exec_loop(self, first_frame_input).map_err(map_tempo_error)?;
168
169 handler.last_frame_result(self, reservoir, &mut frame_result).map_err(map_tempo_error)?;
170
171 Ok(frame_result)
172 }
173
174 fn transact_raw(&mut self, tx: Self::Tx) -> Result<ResultAndState, EVMError<DatabaseError>> {
175 self.set_tx(tx);
176
177 let mut handler = TempoEvmHandler::new();
178 let result = handler.inspect_run(self).map_err(map_tempo_error)?;
179
180 let result = result.map_haltreason(|h| match h {
181 TempoHaltReason::Ethereum(eth) => eth,
182 _ => HaltReason::PrecompileError,
183 });
184
185 Ok(ResultAndState::new(result, self.ctx.journaled_state.inner.state.clone()))
186 }
187
188 fn to_evm_env(&self) -> EvmEnv<Self::Spec, Self::Block> {
189 self.ctx_ref().evm_clone()
190 }
191}