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::{extend_tempo_precompiles, storage::StorageCtx};
17use tempo_revm::{
18 TempoInvalidTransaction, TempoTxEnv, evm::TempoContext, gas_params::tempo_gas_params,
19 handler::TempoEvmHandler,
20};
21
22use crate::{
23 FoundryContextExt, FoundryInspectorExt,
24 backend::{DatabaseExt, JournaledState},
25 constants::{CALLER, TEST_CONTRACT_ADDRESS},
26 evm::{FoundryEvmFactory, NestedEvm},
27 tempo::{TEMPO_PRECOMPILE_ADDRESSES, TEMPO_TIP20_TOKENS, initialize_tempo_test_genesis_inner},
28};
29
30pub type TempoRevmEvm<'db, I> = tempo_revm::TempoEvm<&'db mut dyn DatabaseExt<TempoEvmFactory>, I>;
32
33pub(crate) fn initialize_tempo_evm<
42 'db,
43 I: FoundryInspectorExt<TempoContext<&'db mut dyn DatabaseExt<TempoEvmFactory>>>,
44>(
45 evm: &mut TempoEvm<&'db mut dyn DatabaseExt<TempoEvmFactory>, I>,
46 is_forked: bool,
47) {
48 let ctx = evm.ctx_mut();
49 StorageCtx::enter_evm(&mut ctx.journaled_state, &ctx.block, &ctx.cfg, &ctx.tx, || {
50 if is_forked {
51 let mut sctx = StorageCtx;
53 let sentinel = Bytecode::new_legacy(Bytes::from_static(&[0xef]));
54 for addr in
55 TEMPO_PRECOMPILE_ADDRESSES.iter().copied().chain(TEMPO_TIP20_TOKENS.iter().copied())
56 {
57 sctx.set_code(addr, sentinel.clone())
58 .expect("failed to warm tempo precompile address");
59 }
60 } else {
61 initialize_tempo_test_genesis_inner(TEST_CONTRACT_ADDRESS, CALLER)
63 .expect("tempo genesis initialization failed");
64 }
65 });
66}
67
68impl FoundryEvmFactory for TempoEvmFactory {
69 type FoundryContext<'db> = TempoContext<&'db mut dyn DatabaseExt<Self>>;
70
71 type FoundryEvm<'db, I: FoundryInspectorExt<Self::FoundryContext<'db>>> =
72 TempoEvm<&'db mut dyn DatabaseExt<Self>, I>;
73
74 fn create_foundry_evm_with_inspector<'db, I: FoundryInspectorExt<Self::FoundryContext<'db>>>(
75 &self,
76 db: &'db mut dyn DatabaseExt<Self>,
77 evm_env: EvmEnv<Self::Spec, Self::BlockEnv>,
78 inspector: I,
79 ) -> Self::FoundryEvm<'db, I> {
80 let is_forked = db.is_forked_mode();
81 let spec = *evm_env.spec_id();
82 let mut tempo_evm = Self::default().create_evm_with_inspector(db, evm_env, inspector);
83 tempo_evm.cfg.gas_params = tempo_gas_params(spec);
84 tempo_evm.cfg.tx_chain_id_check = true;
85 if tempo_evm.cfg.tx_gas_limit_cap.is_none() {
86 tempo_evm.cfg.tx_gas_limit_cap = spec.tx_gas_limit_cap();
87 }
88
89 let networks = tempo_evm.inspector().get_networks();
90 networks.inject_precompiles(tempo_evm.precompiles_mut());
91 let cfg = tempo_evm.cfg.clone();
92 extend_tempo_precompiles(tempo_evm.precompiles_mut(), &cfg);
93
94 initialize_tempo_evm(&mut tempo_evm, is_forked);
95 tempo_evm
96 }
97
98 fn create_foundry_nested_evm<'db>(
99 &self,
100 db: &'db mut dyn DatabaseExt<Self>,
101 evm_env: EvmEnv<Self::Spec, Self::BlockEnv>,
102 inspector: &'db mut dyn FoundryInspectorExt<Self::FoundryContext<'db>>,
103 ) -> Box<dyn NestedEvm<Spec = TempoHardfork, Block = TempoBlockEnv, Tx = TempoTxEnv> + 'db>
104 {
105 Box::new(self.create_foundry_evm_with_inspector(db, evm_env, inspector).into_inner())
106 }
107}
108
109pub(crate) fn map_tempo_error(
114 e: EVMError<DatabaseError, TempoInvalidTransaction>,
115) -> EVMError<DatabaseError> {
116 match e {
117 EVMError::Database(db) => EVMError::Database(db),
118 EVMError::Header(h) => EVMError::Header(h),
119 EVMError::Custom(s) => EVMError::Custom(s),
120 EVMError::CustomAny(custom_any_error) => EVMError::CustomAny(custom_any_error),
121 EVMError::Transaction(t) => match t {
122 TempoInvalidTransaction::EthInvalidTransaction(eth) => EVMError::Transaction(eth),
123 t => EVMError::Custom(format!("tempo transaction error: {t}")),
124 },
125 }
126}
127
128impl<'db, I: FoundryInspectorExt<TempoContext<&'db mut dyn DatabaseExt<TempoEvmFactory>>>> NestedEvm
129 for TempoRevmEvm<'db, I>
130{
131 type Spec = TempoHardfork;
132 type Block = TempoBlockEnv;
133 type Tx = TempoTxEnv;
134
135 fn journal_inner_mut(&mut self) -> &mut JournaledState {
136 &mut self.ctx_mut().journaled_state.inner
137 }
138
139 fn run_execution(&mut self, frame: FrameInput) -> Result<FrameResult, EVMError<DatabaseError>> {
140 let mut handler = TempoEvmHandler::new();
141 let reservoir = frame.reservoir();
142
143 let memory =
144 SharedMemory::new_with_buffer(self.ctx_ref().local().shared_memory_buffer().clone());
145 let first_frame_input = FrameInit { depth: 0, memory, frame_input: frame };
146
147 let mut frame_result =
148 handler.inspect_run_exec_loop(self, first_frame_input).map_err(map_tempo_error)?;
149
150 handler.last_frame_result(self, reservoir, &mut frame_result).map_err(map_tempo_error)?;
151
152 Ok(frame_result)
153 }
154
155 fn transact_raw(&mut self, tx: Self::Tx) -> Result<ResultAndState, EVMError<DatabaseError>> {
156 self.set_tx(tx);
157
158 let mut handler = TempoEvmHandler::new();
159 let result = handler.inspect_run(self).map_err(map_tempo_error)?;
160
161 let result = result.map_haltreason(|h| match h {
162 TempoHaltReason::Ethereum(eth) => eth,
163 _ => HaltReason::PrecompileError,
164 });
165
166 Ok(ResultAndState::new(result, self.ctx.journaled_state.inner.state.clone()))
167 }
168
169 fn to_evm_env(&self) -> EvmEnv<Self::Spec, Self::Block> {
170 self.ctx_ref().evm_clone()
171 }
172}