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 tempo_evm.cfg.gas_params = tempo_gas_params(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
93impl<'db, I: FoundryInspectorExt<TempoContext<&'db mut dyn DatabaseExt<TempoEvmFactory>>>> NestedEvm
94 for TempoRevmEvm<'db, I>
95{
96 type Spec = TempoHardfork;
97 type Block = TempoBlockEnv;
98 type Tx = TempoTxEnv;
99 type Chain = ();
100 type Journal = Journal<&'db mut dyn DatabaseExt<TempoEvmFactory>>;
101
102 fn tx_mut(&mut self) -> &mut Self::Tx {
103 self.ctx_mut().tx_mut()
104 }
105
106 fn journal_inner_mut(&mut self) -> &mut JournaledState {
107 &mut self.ctx_mut().journaled_state.inner
108 }
109
110 fn chain_mut(&mut self) -> &mut Self::Chain {
111 &mut self.ctx_mut().chain
112 }
113
114 fn precompiles_mut(&mut self) -> &mut alloy_evm::precompiles::PrecompilesMap {
115 &mut self.precompiles
116 }
117
118 fn journal_mut(&mut self) -> &mut Self::Journal {
119 &mut self.ctx_mut().journaled_state
120 }
121
122 fn run_execution(&mut self, frame: FrameInput) -> Result<FrameResult, EVMError<DatabaseError>> {
123 run_inspected_frame(self, TempoEvmHandler::new(), frame).map_err(map_tempo_error)
124 }
125
126 fn transact_raw(&mut self, tx: Self::Tx) -> eyre::Result<ResultAndState> {
127 self.set_tx(tx);
128
129 let mut handler = TempoEvmHandler::new();
130 let result = handler.inspect_run(self).map_err(map_tempo_error)?;
131
132 Ok(ResultAndState::new(result, self.ctx.journaled_state.inner.state.clone()))
133 }
134
135 fn to_evm_env(&self) -> EvmEnv<Self::Spec, Self::Block> {
136 self.ctx_ref().evm_clone()
137 }
138}
139
140pub(crate) fn map_tempo_error(
145 e: EVMError<DatabaseError, TempoInvalidTransaction>,
146) -> EVMError<DatabaseError> {
147 match e {
148 EVMError::Database(db) => EVMError::Database(db),
149 EVMError::Header(h) => EVMError::Header(h),
150 EVMError::Custom(s) => EVMError::Custom(s),
151 EVMError::CustomAny(custom_any_error) => EVMError::CustomAny(custom_any_error),
152 EVMError::Transaction(t) => match t {
153 TempoInvalidTransaction::EthInvalidTransaction(eth) => EVMError::Transaction(eth),
154 t => EVMError::Custom(format!("tempo transaction error: {t}")),
155 },
156 }
157}
158
159pub(crate) fn initialize_tempo_evm<
168 'db,
169 I: FoundryInspectorExt<TempoContext<&'db mut dyn DatabaseExt<TempoEvmFactory>>>,
170>(
171 evm: &mut TempoEvm<&'db mut dyn DatabaseExt<TempoEvmFactory>, I>,
172 is_forked: bool,
173) {
174 let ctx = evm.ctx_mut();
175 StorageCtx::enter_evm(
176 &mut ctx.journaled_state,
177 &ctx.block,
178 &ctx.cfg,
179 &ctx.tx,
180 StorageActions::disabled(),
181 || {
182 if is_forked {
183 let mut sctx = StorageCtx;
185 let sentinel = Bytecode::new_legacy(Bytes::from_static(SYSTEM_PRECOMPILE_STUB));
186 for addr in TEMPO_PRECOMPILE_ADDRESSES
187 .iter()
188 .copied()
189 .chain(TEMPO_TIP20_TOKENS.iter().copied())
190 {
191 sctx.set_code(addr, sentinel.clone())
192 .expect("failed to warm tempo precompile address");
193 }
194 } else {
195 initialize_tempo_test_genesis_inner(TEST_CONTRACT_ADDRESS, CALLER)
197 .expect("tempo genesis initialization failed");
198 }
199 },
200 );
201}