1use crate::{TempoBlockEnv, TempoTxEnv, instructions};
2use alloy_evm::{Database, precompiles::PrecompilesMap};
3use alloy_primitives::{Log, U256};
4use revm::{
5 Context, Inspector,
6 context::{CfgEnv, ContextError, Evm, FrameStack},
7 handler::{
8 EthFrame, EthPrecompiles, EvmTr, FrameInitOrResult, FrameTr, ItemOrResult,
9 instructions::EthInstructions,
10 },
11 inspector::InspectorEvmTr,
12 interpreter::interpreter::EthInterpreter,
13};
14use tempo_chainspec::hardfork::TempoHardfork;
15use tempo_precompiles::extend_tempo_precompiles;
16
17pub type TempoContext<DB> = Context<TempoBlockEnv, TempoTxEnv, CfgEnv<TempoHardfork>, DB>;
19
20#[derive(Debug, derive_more::Deref, derive_more::DerefMut)]
22#[expect(clippy::type_complexity)]
23pub struct TempoEvm<DB: Database, I> {
24 #[deref]
26 #[deref_mut]
27 pub inner: Evm<
28 TempoContext<DB>,
29 I,
30 EthInstructions<EthInterpreter, TempoContext<DB>>,
31 PrecompilesMap,
32 EthFrame<EthInterpreter>,
33 >,
34 pub logs: Vec<Log>,
36 pub(crate) collected_fee: U256,
38}
39
40impl<DB: Database, I> TempoEvm<DB, I> {
41 pub fn new(ctx: TempoContext<DB>, inspector: I) -> Self {
43 let mut precompiles = PrecompilesMap::from_static(EthPrecompiles::default().precompiles);
44 extend_tempo_precompiles(&mut precompiles, &ctx.cfg);
45
46 Self::new_inner(Evm {
47 ctx,
48 inspector,
49 instruction: instructions::tempo_instructions(),
50 precompiles,
51 frame_stack: FrameStack::new(),
52 })
53 }
54
55 #[inline]
57 #[expect(clippy::type_complexity)]
58 fn new_inner(
59 inner: Evm<
60 TempoContext<DB>,
61 I,
62 EthInstructions<EthInterpreter, TempoContext<DB>>,
63 PrecompilesMap,
64 EthFrame<EthInterpreter>,
65 >,
66 ) -> Self {
67 Self {
68 inner,
69 logs: Vec::new(),
70 collected_fee: U256::ZERO,
71 }
72 }
73}
74
75impl<DB: Database, I> TempoEvm<DB, I> {
76 pub fn with_inspector<OINSP>(self, inspector: OINSP) -> TempoEvm<DB, OINSP> {
78 TempoEvm::new_inner(self.inner.with_inspector(inspector))
79 }
80
81 pub fn with_precompiles(self, precompiles: PrecompilesMap) -> Self {
83 Self::new_inner(self.inner.with_precompiles(precompiles))
84 }
85
86 pub fn into_inspector(self) -> I {
88 self.inner.into_inspector()
89 }
90
91 #[inline]
93 pub fn take_logs(&mut self) -> Vec<Log> {
94 std::mem::take(&mut self.logs)
95 }
96}
97
98impl<DB, I> EvmTr for TempoEvm<DB, I>
99where
100 DB: Database,
101{
102 type Context = TempoContext<DB>;
103 type Instructions = EthInstructions<EthInterpreter, TempoContext<DB>>;
104 type Precompiles = PrecompilesMap;
105 type Frame = EthFrame<EthInterpreter>;
106
107 fn all(
108 &self,
109 ) -> (
110 &Self::Context,
111 &Self::Instructions,
112 &Self::Precompiles,
113 &FrameStack<Self::Frame>,
114 ) {
115 self.inner.all()
116 }
117
118 fn all_mut(
119 &mut self,
120 ) -> (
121 &mut Self::Context,
122 &mut Self::Instructions,
123 &mut Self::Precompiles,
124 &mut FrameStack<Self::Frame>,
125 ) {
126 self.inner.all_mut()
127 }
128
129 fn frame_stack(&mut self) -> &mut FrameStack<Self::Frame> {
130 &mut self.inner.frame_stack
131 }
132
133 fn frame_init(
134 &mut self,
135 frame_input: <Self::Frame as FrameTr>::FrameInit,
136 ) -> Result<
137 ItemOrResult<&mut Self::Frame, <Self::Frame as FrameTr>::FrameResult>,
138 ContextError<DB::Error>,
139 > {
140 self.inner.frame_init(frame_input)
141 }
142
143 fn frame_run(&mut self) -> Result<FrameInitOrResult<Self::Frame>, ContextError<DB::Error>> {
144 self.inner.frame_run()
145 }
146
147 #[doc = " Returns the result of the frame to the caller. Frame is popped from the frame stack."]
148 #[doc = " Consumes the frame result or returns it if there is more frames to run."]
149 fn frame_return_result(
150 &mut self,
151 result: <Self::Frame as FrameTr>::FrameResult,
152 ) -> Result<Option<<Self::Frame as FrameTr>::FrameResult>, ContextError<DB::Error>> {
153 self.inner.frame_return_result(result)
154 }
155}
156
157impl<DB, I> InspectorEvmTr for TempoEvm<DB, I>
158where
159 DB: Database,
160 I: Inspector<TempoContext<DB>>,
161{
162 type Inspector = I;
163
164 fn all_inspector(
165 &self,
166 ) -> (
167 &Self::Context,
168 &Self::Instructions,
169 &Self::Precompiles,
170 &FrameStack<Self::Frame>,
171 &Self::Inspector,
172 ) {
173 self.inner.all_inspector()
174 }
175
176 fn all_mut_inspector(
177 &mut self,
178 ) -> (
179 &mut Self::Context,
180 &mut Self::Instructions,
181 &mut Self::Precompiles,
182 &mut FrameStack<Self::Frame>,
183 &mut Self::Inspector,
184 ) {
185 self.inner.all_mut_inspector()
186 }
187}
188
189#[cfg(test)]
190mod tests {
191 use alloy_evm::{Evm, EvmFactory};
192 use alloy_primitives::{Address, U256, bytes};
193 use reth_evm::EvmInternals;
194 use revm::{
195 context::{ContextTr, TxEnv},
196 database::{CacheDB, EmptyDB},
197 state::{AccountInfo, Bytecode},
198 };
199 use tempo_contracts::DEFAULT_7702_DELEGATE_ADDRESS;
200 use tempo_evm::TempoEvmFactory;
201 use tempo_precompiles::{
202 storage::{StorageCtx, evm::EvmPrecompileStorageProvider},
203 test_util::TIP20Setup,
204 };
205
206 #[test]
207 fn test_auto_7702_delegation() -> eyre::Result<()> {
208 let db = CacheDB::new(EmptyDB::new());
209 let mut tempo_evm = TempoEvmFactory::default().create_evm(db, Default::default());
210
211 let ctx = tempo_evm.ctx_mut();
213 let mut storage = EvmPrecompileStorageProvider::new_max_gas(
214 EvmInternals::new(&mut ctx.journaled_state, &ctx.block),
215 &ctx.cfg,
216 );
217 StorageCtx::enter(&mut storage, || {
218 TIP20Setup::create("USD", "USD", Address::ZERO)
219 .apply()
220 .unwrap();
221 });
222 drop(storage);
223
224 let caller_0 = Address::random();
225 let tx_env = TxEnv {
226 caller: caller_0,
227 nonce: 0,
228 ..Default::default()
229 };
230 let mut res = tempo_evm.transact_raw(tx_env.into())?;
231 assert!(res.result.is_success());
232
233 let account = res.state.remove(&caller_0).unwrap();
234 assert_eq!(
235 account.info.code.unwrap(),
236 Bytecode::new_eip7702(DEFAULT_7702_DELEGATE_ADDRESS),
237 );
238
239 Ok(())
240 }
241
242 #[test]
243 fn test_access_millis_timestamp() -> eyre::Result<()> {
244 let db = CacheDB::new(EmptyDB::new());
245 let mut tempo_evm = TempoEvmFactory::default().create_evm(db, Default::default());
246 let ctx = tempo_evm.ctx_mut();
247 ctx.block.timestamp = U256::from(1000);
248 ctx.block.timestamp_millis_part = 100;
249 let mut storage = EvmPrecompileStorageProvider::new_max_gas(
250 EvmInternals::new(&mut ctx.journaled_state, &ctx.block),
251 &ctx.cfg,
252 );
253 StorageCtx::enter(&mut storage, || {
254 TIP20Setup::create("USD", "USD", Address::ZERO)
255 .apply()
256 .unwrap();
257 });
258 drop(storage);
259
260 let contract = Address::random();
261
262 ctx.db_mut().insert_account_info(
264 contract,
265 AccountInfo {
266 code: Some(Bytecode::new_raw(bytes!("0x4F5F5260205FF3"))),
268 ..Default::default()
269 },
270 );
271
272 let tx_env = TxEnv {
273 kind: contract.into(),
274 ..Default::default()
275 };
276 let res = tempo_evm.transact_raw(tx_env.into())?;
277 assert!(res.result.is_success());
278 assert_eq!(
279 U256::from_be_slice(res.result.output().unwrap()),
280 U256::from(1000100)
281 );
282
283 Ok(())
284 }
285}