1use std::{fmt::Debug, ops::Deref};
2
3use crate::{
4 FoundryBlock, FoundryContextExt, FoundryInspectorExt, FoundryTransaction,
5 FromAnyRpcTransaction,
6 backend::{DatabaseExt, JournaledState},
7};
8use alloy_consensus::{SignableTransaction, Signed, transaction::SignerRecoverable};
9use alloy_evm::{
10 EthEvmFactory, Evm, EvmEnv, EvmFactory, FromRecoveredTx, precompiles::PrecompilesMap,
11};
12use alloy_network::{Ethereum, Network};
13use alloy_primitives::{Address, Signature, U256};
14use alloy_rlp::Decodable;
15use foundry_common::{FoundryReceiptResponse, FoundryTransactionBuilder, fmt::UIfmt};
16use foundry_config::ExecutionSpec;
17use foundry_fork_db::{DatabaseError, ForkBlockEnv};
18use revm::{
19 Database,
20 context::{
21 JournalTr,
22 result::{EVMError, HaltReason, ResultAndState},
23 },
24 handler::FrameResult,
25 interpreter::{
26 CallInput, CallInputs, CallScheme, CallValue, CreateInputs, FrameInput, InstructionResult,
27 },
28 primitives::hardfork::SpecId,
29};
30use serde::{Deserialize, Serialize};
31use tempo_alloy::TempoNetwork;
32use tempo_evm::evm::TempoEvmFactory;
33use tempo_revm::TempoHaltReason;
34
35pub mod eth;
36#[cfg(feature = "optimism")]
37pub mod op;
38pub mod tempo;
39
40pub use eth::*;
41#[cfg(feature = "optimism")]
42pub use op::*;
43pub use tempo::*;
44
45pub trait FoundryEvmNetwork: Copy + Debug + Default + 'static {
47 type Network: Network<
48 TxEnvelope: Decodable
49 + SignerRecoverable
50 + From<Signed<<Self::Network as Network>::UnsignedTx>>
51 + for<'d> Deserialize<'d>
52 + Serialize
53 + UIfmt,
54 UnsignedTx: SignableTransaction<Signature>,
55 TransactionRequest: FoundryTransactionBuilder<Self::Network>
56 + for<'d> Deserialize<'d>
57 + Serialize,
58 ReceiptResponse: FoundryReceiptResponse,
59 >;
60 type EvmFactory: FoundryEvmFactory<Tx: FromRecoveredTx<<Self::Network as Network>::TxEnvelope>>;
61}
62
63#[derive(Clone, Copy, Debug, Default)]
64pub struct EthEvmNetwork;
65impl FoundryEvmNetwork for EthEvmNetwork {
66 type Network = Ethereum;
67 type EvmFactory = EthEvmFactory;
68}
69
70#[derive(Clone, Copy, Debug, Default)]
71pub struct TempoEvmNetwork;
72impl FoundryEvmNetwork for TempoEvmNetwork {
73 type Network = TempoNetwork;
74 type EvmFactory = TempoEvmFactory;
75}
76
77pub type EvmFactoryFor<FEN> = <FEN as FoundryEvmNetwork>::EvmFactory;
79pub type FoundryContextFor<'db, FEN> =
80 <EvmFactoryFor<FEN> as FoundryEvmFactory>::FoundryContext<'db>;
81pub type TxEnvFor<FEN> = <EvmFactoryFor<FEN> as EvmFactory>::Tx;
82pub type HaltReasonFor<FEN> = <EvmFactoryFor<FEN> as EvmFactory>::HaltReason;
83pub type SpecFor<FEN> = <EvmFactoryFor<FEN> as EvmFactory>::Spec;
84pub type BlockEnvFor<FEN> = <EvmFactoryFor<FEN> as EvmFactory>::BlockEnv;
85pub type PrecompilesFor<FEN> = <EvmFactoryFor<FEN> as EvmFactory>::Precompiles;
86pub type EvmEnvFor<FEN> = EvmEnv<SpecFor<FEN>, BlockEnvFor<FEN>>;
87
88pub type NetworkFor<FEN> = <FEN as FoundryEvmNetwork>::Network;
89pub type TxEnvelopeFor<FEN> = <NetworkFor<FEN> as Network>::TxEnvelope;
90pub type TransactionRequestFor<FEN> = <NetworkFor<FEN> as Network>::TransactionRequest;
91pub type TransactionResponseFor<FEN> = <NetworkFor<FEN> as Network>::TransactionResponse;
92pub type BlockResponseFor<FEN> = <NetworkFor<FEN> as Network>::BlockResponse;
93
94pub trait FoundryEvmFactory:
95 EvmFactory<
96 Spec: Into<SpecId> + ExecutionSpec + Default + Copy + Unpin + Send + 'static,
97 BlockEnv: FoundryBlock + ForkBlockEnv + Default + Unpin,
98 Tx: Clone + Debug + FoundryTransaction + FromAnyRpcTransaction + Default + Send + Sync,
99 HaltReason: IntoInstructionResult,
100 Precompiles = PrecompilesMap,
101 > + Clone
102 + Debug
103 + Default
104 + 'static
105{
106 type FoundryContext<'db>: FoundryContextExt<
108 Block = Self::BlockEnv,
109 Tx = Self::Tx,
110 Spec = Self::Spec,
111 Db: DatabaseExt<Self>,
112 >
113 where
114 Self: 'db;
115
116 type FoundryEvm<'db, I: FoundryInspectorExt<Self::FoundryContext<'db>>>: Evm<
118 DB = &'db mut dyn DatabaseExt<Self>,
119 Tx = Self::Tx,
120 BlockEnv = Self::BlockEnv,
121 Spec = Self::Spec,
122 HaltReason = Self::HaltReason,
123 > + Deref<Target = Self::FoundryContext<'db>>
124 where
125 Self: 'db;
126
127 fn create_foundry_evm_with_inspector<'db, I: FoundryInspectorExt<Self::FoundryContext<'db>>>(
129 &self,
130 db: &'db mut dyn DatabaseExt<Self>,
131 evm_env: EvmEnv<Self::Spec, Self::BlockEnv>,
132 inspector: I,
133 ) -> Self::FoundryEvm<'db, I>;
134
135 fn create_foundry_nested_evm<'db>(
142 &self,
143 db: &'db mut dyn DatabaseExt<Self>,
144 evm_env: EvmEnv<Self::Spec, Self::BlockEnv>,
145 inspector: &'db mut dyn FoundryInspectorExt<Self::FoundryContext<'db>>,
146 ) -> Box<dyn NestedEvm<Spec = Self::Spec, Block = Self::BlockEnv, Tx = Self::Tx> + 'db>;
147}
148
149pub trait NestedEvm {
154 type Spec;
156 type Block;
158 type Tx;
160
161 fn journal_inner_mut(&mut self) -> &mut JournaledState;
163
164 fn run_execution(&mut self, frame: FrameInput) -> Result<FrameResult, EVMError<DatabaseError>>;
166
167 fn transact_raw(
169 &mut self,
170 tx: Self::Tx,
171 ) -> Result<ResultAndState<HaltReason>, EVMError<DatabaseError>>;
172
173 fn to_evm_env(&self) -> EvmEnv<Self::Spec, Self::Block>;
174}
175
176pub type NestedEvmClosure<'a, Spec, Block, Tx> =
178 &'a mut dyn FnMut(
179 &mut dyn NestedEvm<Spec = Spec, Block = Block, Tx = Tx>,
180 ) -> Result<(), EVMError<DatabaseError>>;
181
182pub fn with_cloned_context<CTX: FoundryContextExt>(
187 ecx: &mut CTX,
188 f: impl FnOnce(
189 &mut CTX::Db,
190 EvmEnv<CTX::Spec, CTX::Block>,
191 JournaledState,
192 )
193 -> Result<(EvmEnv<CTX::Spec, CTX::Block>, JournaledState), EVMError<DatabaseError>>,
194) -> Result<(), EVMError<DatabaseError>> {
195 let evm_env = ecx.evm_clone();
196
197 let (db, journal_inner) = ecx.db_journal_inner_mut();
198 let journal_inner_clone = journal_inner.clone();
199
200 let (sub_evm_env, sub_inner) = f(db, evm_env, journal_inner_clone)?;
201
202 ecx.set_journal_inner(sub_inner);
204 ecx.set_evm(sub_evm_env);
205
206 Ok(())
207}
208
209pub fn get_create2_factory_call_inputs<T: JournalTr>(
211 salt: U256,
212 inputs: &CreateInputs,
213 deployer: Address,
214 journal: &mut T,
215) -> Result<CallInputs, <T::Database as Database>::Error> {
216 let calldata = [&salt.to_be_bytes::<32>()[..], &inputs.init_code()[..]].concat();
217 let account = journal.load_account_with_code(deployer)?;
218 Ok(CallInputs {
219 caller: inputs.caller(),
220 bytecode_address: deployer,
221 known_bytecode: (account.info.code_hash, account.info.code.clone().unwrap_or_default()),
222 target_address: deployer,
223 scheme: CallScheme::Call,
224 value: CallValue::Transfer(inputs.value()),
225 input: CallInput::Bytes(calldata.into()),
226 gas_limit: inputs.gas_limit(),
227 reservoir: inputs.reservoir(),
228 is_static: false,
229 return_memory_offset: 0..0,
230 })
231}
232
233pub trait IntoInstructionResult {
235 fn into_instruction_result(self) -> InstructionResult;
236}
237
238impl IntoInstructionResult for HaltReason {
239 fn into_instruction_result(self) -> InstructionResult {
240 self.into()
241 }
242}
243
244impl IntoInstructionResult for TempoHaltReason {
245 fn into_instruction_result(self) -> InstructionResult {
246 match self {
247 Self::Ethereum(eth) => eth.into(),
248 _ => InstructionResult::PrecompileError,
249 }
250 }
251}