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 Journal,
8 result::{EVMError, ResultAndState},
9 },
10 handler::{EvmTr, FrameResult},
11 inspector::InspectorHandler,
12 interpreter::FrameInput,
13 state::Bytecode,
14};
15use tempo_alloy::TempoNetwork;
16use tempo_evm::{TempoBlockEnv, TempoEvmFactory, evm::TempoEvm};
17use tempo_precompiles::{
18 extend_tempo_precompiles,
19 storage::{StorageActions, StorageCtx},
20};
21use tempo_revm::{
22 TempoInvalidTransaction, TempoTxEnv, evm::TempoContext, gas_params::tempo_gas_params,
23 handler::TempoEvmHandler,
24};
25
26use crate::{
27 FoundryContextExt, FoundryInspectorExt,
28 backend::{DatabaseExt, JournaledState},
29 constants::{CALLER, SYSTEM_PRECOMPILE_STUB, TEST_CONTRACT_ADDRESS},
30 evm::{FoundryEvmFactory, FoundryEvmNetwork, NestedEvm, NestedEvmFor, run_inspected_frame},
31 tempo::{TEMPO_PRECOMPILE_ADDRESSES, TEMPO_TIP20_TOKENS, initialize_tempo_test_genesis_inner},
32};
33
34#[derive(Clone, Copy, Debug, Default)]
35pub struct TempoEvmNetwork;
36impl FoundryEvmNetwork for TempoEvmNetwork {
37 type Network = TempoNetwork;
38 type EvmFactory = TempoEvmFactory;
39}
40
41pub type TempoRevmEvm<'db, I> = tempo_revm::TempoEvm<&'db mut dyn DatabaseExt<TempoEvmFactory>, I>;
43
44impl FoundryEvmFactory for TempoEvmFactory {
45 type Chain = ();
46 type FoundryContext<'db> = TempoContext<&'db mut dyn DatabaseExt<Self>>;
47
48 type FoundryEvm<'db, I: FoundryInspectorExt<Self::FoundryContext<'db>>> =
49 TempoEvm<&'db mut dyn DatabaseExt<Self>, I>;
50
51 fn create_foundry_evm_with_inspector<'db, I: FoundryInspectorExt<Self::FoundryContext<'db>>>(
52 &self,
53 db: &'db mut dyn DatabaseExt<Self>,
54 evm_env: EvmEnv<Self::Spec, Self::BlockEnv>,
55 inspector: I,
56 ) -> Self::FoundryEvm<'db, I> {
57 let is_forked = db.is_forked_mode();
58 let spec = *evm_env.spec_id();
59 let mut tempo_evm = Self::default().create_evm_with_inspector(db, evm_env, inspector);
60 Self::set_execution_spec(tempo_evm.ctx_mut(), spec);
61 tempo_evm.cfg.tx_chain_id_check = true;
62 if tempo_evm.cfg.tx_gas_limit_cap.is_none() {
63 tempo_evm.cfg.tx_gas_limit_cap = spec.tx_gas_limit_cap();
64 }
65
66 let cfg = tempo_evm.cfg.clone();
68 let non_creditable_slots = tempo_evm.non_creditable_slots();
69 extend_tempo_precompiles(
70 tempo_evm.precompiles_mut(),
71 &cfg,
72 StorageActions::disabled(),
73 non_creditable_slots,
74 );
75
76 initialize_tempo_evm(&mut tempo_evm, is_forked);
77 tempo_evm
78 }
79
80 fn create_nested_evm_with_inspector<'db, I>(
81 &self,
82 db: &'db mut dyn DatabaseExt<Self>,
83 evm_env: EvmEnv<Self::Spec, Self::BlockEnv>,
84 inspector: I,
85 ) -> NestedEvmFor<'db, Self>
86 where
87 I: FoundryInspectorExt<Self::FoundryContext<'db>> + 'db,
88 {
89 Box::new(self.create_foundry_evm_with_inspector(db, evm_env, inspector).into_inner())
90 }
91
92 fn set_execution_spec(context: &mut Self::FoundryContext<'_>, spec: Self::Spec) {
93 context.cfg.spec = spec;
94 context.cfg.set_gas_params(tempo_gas_params(spec));
95 }
96}
97
98impl<'db, I: FoundryInspectorExt<TempoContext<&'db mut dyn DatabaseExt<TempoEvmFactory>>>> NestedEvm
99 for TempoRevmEvm<'db, I>
100{
101 type Spec = TempoHardfork;
102 type Block = TempoBlockEnv;
103 type Tx = TempoTxEnv;
104 type Chain = ();
105 type Journal = Journal<&'db mut dyn DatabaseExt<TempoEvmFactory>>;
106
107 fn tx_mut(&mut self) -> &mut Self::Tx {
108 self.ctx_mut().tx_mut()
109 }
110
111 fn journal_inner_mut(&mut self) -> &mut JournaledState {
112 &mut self.ctx_mut().journaled_state.inner
113 }
114
115 fn chain_mut(&mut self) -> &mut Self::Chain {
116 &mut self.ctx_mut().chain
117 }
118
119 fn precompiles_mut(&mut self) -> &mut alloy_evm::precompiles::PrecompilesMap {
120 &mut self.precompiles
121 }
122
123 fn journal_mut(&mut self) -> &mut Self::Journal {
124 &mut self.ctx_mut().journaled_state
125 }
126
127 fn run_execution(&mut self, frame: FrameInput) -> Result<FrameResult, EVMError<DatabaseError>> {
128 run_inspected_frame(self, TempoEvmHandler::new(), frame).map_err(map_tempo_error)
129 }
130
131 fn transact_raw(&mut self, tx: Self::Tx) -> eyre::Result<ResultAndState> {
132 if tx.tempo_tx_env.is_some() && self.ctx.journaled_state.inner.depth > 0 {
135 self.ctx.journaled_state.inner.depth -= 1;
136 }
137 self.set_tx(tx);
138
139 let mut handler = TempoEvmHandler::new();
140 let result = handler.inspect_run(self).map_err(map_tempo_error)?;
141
142 Ok(ResultAndState::new(result, self.ctx.journaled_state.inner.state.clone()))
143 }
144
145 fn to_evm_env(&self) -> EvmEnv<Self::Spec, Self::Block> {
146 self.ctx_ref().evm_clone()
147 }
148}
149
150pub(crate) fn map_tempo_error(
155 e: EVMError<DatabaseError, TempoInvalidTransaction>,
156) -> EVMError<DatabaseError> {
157 match e {
158 EVMError::Database(db) => EVMError::Database(db),
159 EVMError::Header(h) => EVMError::Header(h),
160 EVMError::Custom(s) => EVMError::Custom(s),
161 EVMError::CustomAny(custom_any_error) => EVMError::CustomAny(custom_any_error),
162 EVMError::Transaction(t) => match t {
163 TempoInvalidTransaction::EthInvalidTransaction(eth) => EVMError::Transaction(eth),
164 t => EVMError::Custom(format!("tempo transaction error: {t}")),
165 },
166 }
167}
168
169pub(crate) fn initialize_tempo_evm<
178 'db,
179 I: FoundryInspectorExt<TempoContext<&'db mut dyn DatabaseExt<TempoEvmFactory>>>,
180>(
181 evm: &mut TempoEvm<&'db mut dyn DatabaseExt<TempoEvmFactory>, I>,
182 is_forked: bool,
183) {
184 let ctx = evm.ctx_mut();
185 StorageCtx::enter_evm(
186 &mut ctx.journaled_state,
187 &ctx.block,
188 &ctx.cfg,
189 &ctx.tx,
190 StorageActions::disabled(),
191 || {
192 if is_forked {
193 let mut sctx = StorageCtx;
197 let sentinel = Bytecode::new_legacy(Bytes::from_static(SYSTEM_PRECOMPILE_STUB));
198 for addr in TEMPO_PRECOMPILE_ADDRESSES
199 .iter()
200 .copied()
201 .chain(TEMPO_TIP20_TOKENS.iter().copied())
202 {
203 let _ = sctx.set_code(addr, sentinel.clone());
204 }
205 } else {
206 initialize_tempo_test_genesis_inner(TEST_CONTRACT_ADDRESS, CALLER)
208 .expect("tempo genesis initialization failed");
209 }
210 },
211 );
212}
213
214#[cfg(test)]
215mod tests {
216 use super::*;
217 use crate::backend::Backend;
218 use revm::{
219 context_interface::cfg::GasParams, inspector::NoOpInspector, primitives::hardfork::SpecId,
220 };
221
222 #[test]
223 fn tempo_execution_spec_preserves_network_gas() {
224 let mut db = Backend::<TempoEvmNetwork>::spawn(None).unwrap();
225 let mut evm = TempoEvmFactory::default().create_foundry_evm_with_inspector(
226 &mut db,
227 EvmEnv::default(),
228 NoOpInspector,
229 );
230 for spec in [TempoHardfork::T3, TempoHardfork::T7, TempoHardfork::T14] {
231 TempoEvmFactory::set_execution_spec(evm.ctx_mut(), spec);
232 assert_eq!(evm.cfg.spec, spec);
233 assert_eq!(evm.cfg.gas_params, tempo_gas_params(spec));
234 assert_ne!(evm.cfg.gas_params, GasParams::new_spec(SpecId::OSAKA));
235 }
236 }
237}