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 self.set_tx(tx);
133
134 let mut handler = TempoEvmHandler::new();
135 let result = handler.inspect_run(self).map_err(map_tempo_error)?;
136
137 Ok(ResultAndState::new(result, self.ctx.journaled_state.inner.state.clone()))
138 }
139
140 fn to_evm_env(&self) -> EvmEnv<Self::Spec, Self::Block> {
141 self.ctx_ref().evm_clone()
142 }
143}
144
145pub(crate) fn map_tempo_error(
150 e: EVMError<DatabaseError, TempoInvalidTransaction>,
151) -> EVMError<DatabaseError> {
152 match e {
153 EVMError::Database(db) => EVMError::Database(db),
154 EVMError::Header(h) => EVMError::Header(h),
155 EVMError::Custom(s) => EVMError::Custom(s),
156 EVMError::CustomAny(custom_any_error) => EVMError::CustomAny(custom_any_error),
157 EVMError::Transaction(t) => match t {
158 TempoInvalidTransaction::EthInvalidTransaction(eth) => EVMError::Transaction(eth),
159 t => EVMError::Custom(format!("tempo transaction error: {t}")),
160 },
161 }
162}
163
164pub(crate) fn initialize_tempo_evm<
173 'db,
174 I: FoundryInspectorExt<TempoContext<&'db mut dyn DatabaseExt<TempoEvmFactory>>>,
175>(
176 evm: &mut TempoEvm<&'db mut dyn DatabaseExt<TempoEvmFactory>, I>,
177 is_forked: bool,
178) {
179 let ctx = evm.ctx_mut();
180 StorageCtx::enter_evm(
181 &mut ctx.journaled_state,
182 &ctx.block,
183 &ctx.cfg,
184 &ctx.tx,
185 StorageActions::disabled(),
186 || {
187 if is_forked {
188 let mut sctx = StorageCtx;
190 let sentinel = Bytecode::new_legacy(Bytes::from_static(SYSTEM_PRECOMPILE_STUB));
191 for addr in TEMPO_PRECOMPILE_ADDRESSES
192 .iter()
193 .copied()
194 .chain(TEMPO_TIP20_TOKENS.iter().copied())
195 {
196 sctx.set_code(addr, sentinel.clone())
197 .expect("failed to warm tempo precompile address");
198 }
199 } else {
200 initialize_tempo_test_genesis_inner(TEST_CONTRACT_ADDRESS, CALLER)
202 .expect("tempo genesis initialization failed");
203 }
204 },
205 );
206}
207
208#[cfg(test)]
209mod tests {
210 use super::*;
211 use crate::backend::Backend;
212 use revm::{
213 context_interface::cfg::GasParams, inspector::NoOpInspector, primitives::hardfork::SpecId,
214 };
215
216 #[test]
217 fn tempo_execution_spec_preserves_network_gas() {
218 let mut db = Backend::<TempoEvmNetwork>::spawn(None).unwrap();
219 let mut evm = TempoEvmFactory::default().create_foundry_evm_with_inspector(
220 &mut db,
221 EvmEnv::default(),
222 NoOpInspector,
223 );
224 for spec in [TempoHardfork::T3, TempoHardfork::T7, TempoHardfork::T14] {
225 TempoEvmFactory::set_execution_spec(evm.ctx_mut(), spec);
226 assert_eq!(evm.cfg.spec, spec);
227 assert_eq!(evm.cfg.gas_params, tempo_gas_params(spec));
228 assert_ne!(evm.cfg.gas_params, GasParams::new_spec(SpecId::OSAKA));
229 }
230 }
231}