1use alloy_evm::{
2 Database, Evm, EvmEnv, EvmFactory,
3 precompiles::PrecompilesMap,
4 revm::{
5 Context, ExecuteEvm, InspectEvm, Inspector, SystemCallEvm,
6 context::result::{EVMError, ResultAndState},
7 inspector::NoOpInspector,
8 },
9};
10use alloy_primitives::{Address, Bytes, Log, TxKind};
11use reth_revm::{InspectSystemCallEvm, MainContext, context::result::ExecutionResult};
12use std::ops::{Deref, DerefMut};
13use tempo_chainspec::hardfork::TempoHardfork;
14use tempo_revm::{TempoHaltReason, TempoInvalidTransaction, TempoTxEnv, evm::TempoContext};
15
16use crate::TempoBlockEnv;
17
18#[derive(Debug, Default, Clone, Copy)]
19#[non_exhaustive]
20pub struct TempoEvmFactory;
21
22impl EvmFactory for TempoEvmFactory {
23 type Evm<DB: Database, I: Inspector<Self::Context<DB>>> = TempoEvm<DB, I>;
24 type Context<DB: Database> = TempoContext<DB>;
25 type Tx = TempoTxEnv;
26 type Error<DBError: std::error::Error + Send + Sync + 'static> =
27 EVMError<DBError, TempoInvalidTransaction>;
28 type HaltReason = TempoHaltReason;
29 type Spec = TempoHardfork;
30 type BlockEnv = TempoBlockEnv;
31 type Precompiles = PrecompilesMap;
32
33 fn create_evm<DB: Database>(
34 &self,
35 db: DB,
36 input: EvmEnv<Self::Spec, Self::BlockEnv>,
37 ) -> Self::Evm<DB, NoOpInspector> {
38 TempoEvm::new(db, input)
39 }
40
41 fn create_evm_with_inspector<DB: Database, I: Inspector<Self::Context<DB>>>(
42 &self,
43 db: DB,
44 input: EvmEnv<Self::Spec, Self::BlockEnv>,
45 inspector: I,
46 ) -> Self::Evm<DB, I> {
47 TempoEvm::new(db, input).with_inspector(inspector)
48 }
49}
50
51#[expect(missing_debug_implementations)]
57pub struct TempoEvm<DB: Database, I = NoOpInspector> {
58 inner: tempo_revm::TempoEvm<DB, I>,
59 inspect: bool,
60}
61
62impl<DB: Database> TempoEvm<DB> {
63 pub fn new(db: DB, input: EvmEnv<TempoHardfork, TempoBlockEnv>) -> Self {
65 let ctx = Context::mainnet()
66 .with_db(db)
67 .with_block(input.block_env)
68 .with_cfg(input.cfg_env)
69 .with_tx(Default::default());
70
71 Self {
72 inner: tempo_revm::TempoEvm::new(ctx, NoOpInspector {}),
73 inspect: false,
74 }
75 }
76}
77
78impl<DB: Database, I> TempoEvm<DB, I> {
79 pub const fn ctx(&self) -> &TempoContext<DB> {
81 &self.inner.inner.ctx
82 }
83
84 pub fn ctx_mut(&mut self) -> &mut TempoContext<DB> {
86 &mut self.inner.inner.ctx
87 }
88
89 pub fn with_inspector<OINSP>(self, inspector: OINSP) -> TempoEvm<DB, OINSP> {
91 TempoEvm {
92 inner: self.inner.with_inspector(inspector),
93 inspect: true,
94 }
95 }
96
97 pub fn take_revert_logs(&mut self) -> Vec<Log> {
106 std::mem::take(&mut self.inner.logs)
107 }
108}
109
110impl<DB: Database, I> Deref for TempoEvm<DB, I>
111where
112 DB: Database,
113 I: Inspector<TempoContext<DB>>,
114{
115 type Target = TempoContext<DB>;
116
117 #[inline]
118 fn deref(&self) -> &Self::Target {
119 self.ctx()
120 }
121}
122
123impl<DB: Database, I> DerefMut for TempoEvm<DB, I>
124where
125 DB: Database,
126 I: Inspector<TempoContext<DB>>,
127{
128 #[inline]
129 fn deref_mut(&mut self) -> &mut Self::Target {
130 self.ctx_mut()
131 }
132}
133
134impl<DB, I> Evm for TempoEvm<DB, I>
135where
136 DB: Database,
137 I: Inspector<TempoContext<DB>>,
138{
139 type DB = DB;
140 type Tx = TempoTxEnv;
141 type Error = EVMError<DB::Error, TempoInvalidTransaction>;
142 type HaltReason = TempoHaltReason;
143 type Spec = TempoHardfork;
144 type BlockEnv = TempoBlockEnv;
145 type Precompiles = PrecompilesMap;
146 type Inspector = I;
147
148 fn block(&self) -> &Self::BlockEnv {
149 &self.block
150 }
151
152 fn chain_id(&self) -> u64 {
153 self.cfg.chain_id
154 }
155
156 fn transact_raw(
157 &mut self,
158 tx: Self::Tx,
159 ) -> Result<ResultAndState<Self::HaltReason>, Self::Error> {
160 if tx.is_system_tx {
161 let TxKind::Call(to) = tx.inner.kind else {
162 return Err(TempoInvalidTransaction::SystemTransactionMustBeCall.into());
163 };
164
165 let mut result = if self.inspect {
166 self.inner
167 .inspect_system_call_with_caller(tx.inner.caller, to, tx.inner.data)?
168 } else {
169 self.inner
170 .system_call_with_caller(tx.inner.caller, to, tx.inner.data)?
171 };
172
173 let ExecutionResult::Success {
175 gas_used,
176 gas_refunded,
177 ..
178 } = &mut result.result
179 else {
180 return Err(TempoInvalidTransaction::SystemTransactionFailed(result.result).into());
181 };
182
183 *gas_used = 0;
184 *gas_refunded = 0;
185
186 Ok(result)
187 } else if self.inspect {
188 self.inner.inspect_tx(tx)
189 } else {
190 self.inner.transact(tx)
191 }
192 }
193
194 fn transact_system_call(
195 &mut self,
196 caller: Address,
197 contract: Address,
198 data: Bytes,
199 ) -> Result<ResultAndState<Self::HaltReason>, Self::Error> {
200 self.inner.system_call_with_caller(caller, contract, data)
201 }
202
203 fn finish(self) -> (Self::DB, EvmEnv<Self::Spec, Self::BlockEnv>) {
204 let Context {
205 block: block_env,
206 cfg: cfg_env,
207 journaled_state,
208 ..
209 } = self.inner.inner.ctx;
210
211 (journaled_state.database, EvmEnv { block_env, cfg_env })
212 }
213
214 fn set_inspector_enabled(&mut self, enabled: bool) {
215 self.inspect = enabled;
216 }
217
218 fn components(&self) -> (&Self::DB, &Self::Inspector, &Self::Precompiles) {
219 (
220 &self.inner.inner.ctx.journaled_state.database,
221 &self.inner.inner.inspector,
222 &self.inner.inner.precompiles,
223 )
224 }
225
226 fn components_mut(&mut self) -> (&mut Self::DB, &mut Self::Inspector, &mut Self::Precompiles) {
227 (
228 &mut self.inner.inner.ctx.journaled_state.database,
229 &mut self.inner.inner.inspector,
230 &mut self.inner.inner.precompiles,
231 )
232 }
233}
234
235#[cfg(test)]
236mod tests {
237 use reth_revm::context::BlockEnv;
238 use revm::{context::TxEnv, database::EmptyDB};
239
240 use super::*;
241
242 #[test]
243 fn can_execute_system_tx() {
244 let mut evm = TempoEvm::new(
245 EmptyDB::default(),
246 EvmEnv {
247 block_env: TempoBlockEnv {
248 inner: BlockEnv {
249 basefee: 1,
250 ..Default::default()
251 },
252 ..Default::default()
253 },
254 ..Default::default()
255 },
256 );
257 let result = evm
258 .transact(TempoTxEnv {
259 inner: TxEnv {
260 caller: Address::ZERO,
261 gas_price: 0,
262 gas_limit: 21000,
263 ..Default::default()
264 },
265 is_system_tx: true,
266 ..Default::default()
267 })
268 .unwrap();
269
270 assert!(result.result.is_success());
271 }
272}