1use std::{fmt::Debug, ops::Deref};
2
3use crate::{
4 FoundryBlock, FoundryChain, FoundryContextExt, FoundryInspectorExt, FoundryJournal,
5 FoundryTransaction, 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 ContextTr, JournalTr,
22 result::{EVMError, HaltReason, ResultAndState},
23 },
24 handler::FrameResult,
25 inspector::NoOpInspector,
26 interpreter::{
27 CallInput, CallInputs, CallScheme, CallValue, CreateInputs, FrameInput, InstructionResult,
28 },
29 primitives::hardfork::SpecId,
30};
31use serde::{Deserialize, Serialize};
32use tempo_alloy::TempoNetwork;
33use tempo_evm::evm::TempoEvmFactory;
34use tempo_revm::TempoHaltReason;
35
36pub mod eth;
37#[cfg(feature = "monad")]
38pub mod monad;
39#[cfg(feature = "optimism")]
40pub mod op;
41pub mod tempo;
42
43mod block_context;
44pub use block_context::*;
45
46pub use eth::*;
47#[cfg(feature = "monad")]
48pub use monad::*;
49#[cfg(feature = "optimism")]
50pub use op::*;
51pub use tempo::*;
52
53pub trait FoundryEvmNetwork: Copy + Debug + Default + 'static {
55 type Network: Network<
56 TxEnvelope: Decodable
57 + SignerRecoverable
58 + From<Signed<<Self::Network as Network>::UnsignedTx>>
59 + for<'d> Deserialize<'d>
60 + Serialize
61 + UIfmt,
62 UnsignedTx: SignableTransaction<Signature>,
63 TransactionRequest: FoundryTransactionBuilder<Self::Network>
64 + for<'d> Deserialize<'d>
65 + Serialize,
66 ReceiptResponse: FoundryReceiptResponse,
67 >;
68 type EvmFactory: FoundryEvmFactory<Tx: FromRecoveredTx<<Self::Network as Network>::TxEnvelope>>;
69}
70
71#[derive(Clone, Copy, Debug, Default)]
72pub struct EthEvmNetwork;
73impl FoundryEvmNetwork for EthEvmNetwork {
74 type Network = Ethereum;
75 type EvmFactory = EthEvmFactory;
76}
77
78#[derive(Clone, Copy, Debug, Default)]
79pub struct TempoEvmNetwork;
80impl FoundryEvmNetwork for TempoEvmNetwork {
81 type Network = TempoNetwork;
82 type EvmFactory = TempoEvmFactory;
83}
84
85#[derive(Clone, Copy, Debug, Default)]
86#[cfg(feature = "monad")]
87pub struct MonadEvmNetwork;
88#[cfg(feature = "monad")]
89impl FoundryEvmNetwork for MonadEvmNetwork {
90 type Network = Ethereum;
91 type EvmFactory = alloy_monad_evm::MonadEvmFactory;
92}
93
94pub type EvmFactoryFor<FEN> = <FEN as FoundryEvmNetwork>::EvmFactory;
96pub type FoundryContextFor<'db, FEN> =
97 <EvmFactoryFor<FEN> as FoundryEvmFactory>::FoundryContext<'db>;
98pub type TxEnvFor<FEN> = <EvmFactoryFor<FEN> as EvmFactory>::Tx;
99pub type HaltReasonFor<FEN> = <EvmFactoryFor<FEN> as EvmFactory>::HaltReason;
100pub type SpecFor<FEN> = <EvmFactoryFor<FEN> as EvmFactory>::Spec;
101pub type BlockEnvFor<FEN> = <EvmFactoryFor<FEN> as EvmFactory>::BlockEnv;
102pub type PrecompilesFor<FEN> = <EvmFactoryFor<FEN> as EvmFactory>::Precompiles;
103pub type EvmEnvFor<FEN> = EvmEnv<SpecFor<FEN>, BlockEnvFor<FEN>>;
104pub type NetworkFor<FEN> = <FEN as FoundryEvmNetwork>::Network;
105pub type TxEnvelopeFor<FEN> = <NetworkFor<FEN> as Network>::TxEnvelope;
106pub type TransactionRequestFor<FEN> = <NetworkFor<FEN> as Network>::TransactionRequest;
107pub type TransactionResponseFor<FEN> = <NetworkFor<FEN> as Network>::TransactionResponse;
108pub type BlockResponseFor<FEN> = <NetworkFor<FEN> as Network>::BlockResponse;
109
110pub type ChainFor<FEN> = <EvmFactoryFor<FEN> as FoundryEvmFactory>::Chain;
111
112pub type NestedEvmFor<'db, F> = Box<
114 dyn NestedEvm<
115 Spec = <F as EvmFactory>::Spec,
116 Block = <F as EvmFactory>::BlockEnv,
117 Tx = <F as EvmFactory>::Tx,
118 Chain = <F as FoundryEvmFactory>::Chain,
119 Journal = <<F as FoundryEvmFactory>::FoundryContext<'db> as ContextTr>::Journal,
120 > + 'db,
121>;
122
123pub trait FoundryEvmFactory:
124 EvmFactory<
125 Spec: Into<SpecId> + ExecutionSpec + Default + Copy + Unpin + Send + 'static,
126 BlockEnv: FoundryBlock + ForkBlockEnv + Default + Unpin,
127 Tx: Clone + Debug + FoundryTransaction + FromAnyRpcTransaction + Default + Send + Sync,
128 HaltReason: IntoInstructionResult,
129 Precompiles = PrecompilesMap,
130 > + Clone
131 + Debug
132 + Default
133 + 'static
134{
135 type Chain: FoundryChain<Self::Tx>;
137
138 type FoundryContext<'db>: FoundryContextExt<
140 Block = Self::BlockEnv,
141 Tx = Self::Tx,
142 Spec = Self::Spec,
143 Chain = Self::Chain,
144 Journal: FoundryJournal,
145 Db: DatabaseExt<Self>,
146 >
147 where
148 Self: 'db;
149
150 type FoundryEvm<'db, I: FoundryInspectorExt<Self::FoundryContext<'db>>>: Evm<
152 DB = &'db mut dyn DatabaseExt<Self>,
153 Tx = Self::Tx,
154 BlockEnv = Self::BlockEnv,
155 Spec = Self::Spec,
156 HaltReason = Self::HaltReason,
157 > + Deref<Target = Self::FoundryContext<'db>>
158 where
159 Self: 'db;
160
161 fn create_foundry_evm_with_inspector<'db, I: FoundryInspectorExt<Self::FoundryContext<'db>>>(
163 &self,
164 db: &'db mut dyn DatabaseExt<Self>,
165 evm_env: EvmEnv<Self::Spec, Self::BlockEnv>,
166 chain_context: Self::Chain,
167 inspector: I,
168 ) -> Self::FoundryEvm<'db, I>;
169
170 #[cfg(feature = "monad")]
176 fn try_transact_system_replay<DB, I>(
177 &self,
178 _evm: &mut Self::Evm<DB, I>,
179 _tx: &Self::Tx,
180 ) -> eyre::Result<Option<ResultAndState<Self::HaltReason>>>
181 where
182 DB: alloy_evm::Database,
183 I: revm::inspector::Inspector<Self::Context<DB>>,
184 {
185 Ok(None)
186 }
187
188 fn create_evm_with_context<DB: alloy_evm::Database>(
190 &self,
191 db: DB,
192 evm_env: EvmEnv<Self::Spec, Self::BlockEnv>,
193 chain_context: Self::Chain,
194 ) -> Self::Evm<DB, NoOpInspector>;
195
196 fn create_foundry_nested_evm<'db>(
203 &self,
204 db: &'db mut dyn DatabaseExt<Self>,
205 evm_env: EvmEnv<Self::Spec, Self::BlockEnv>,
206 chain_context: Self::Chain,
207 inspector: &'db mut dyn FoundryInspectorExt<Self::FoundryContext<'db>>,
208 ) -> NestedEvmFor<'db, Self>;
209}
210
211pub trait NestedEvm {
216 type Spec;
218 type Block;
220 type Tx: FoundryTransaction;
222 type Chain: FoundryChain<Self::Tx>;
224 type Journal: FoundryJournal;
226 fn journal_inner_mut(&mut self) -> &mut JournaledState;
228
229 fn tx_mut(&mut self) -> &mut Self::Tx;
231
232 fn chain_mut(&mut self) -> &mut Self::Chain;
234
235 fn journal_mut(&mut self) -> &mut Self::Journal;
237
238 fn run_execution(&mut self, frame: FrameInput) -> Result<FrameResult, EVMError<DatabaseError>>;
240
241 fn transact_raw(&mut self, tx: Self::Tx) -> eyre::Result<ResultAndState<HaltReason>>;
243
244 fn to_evm_env(&self) -> EvmEnv<Self::Spec, Self::Block>;
245}
246
247pub type NestedEvmClosure<'a, F> = &'a mut dyn for<'j> FnMut(
249 &mut dyn NestedEvm<
250 Spec = <F as EvmFactory>::Spec,
251 Block = <F as EvmFactory>::BlockEnv,
252 Tx = <F as EvmFactory>::Tx,
253 Chain = <F as FoundryEvmFactory>::Chain,
254 Journal = <<F as FoundryEvmFactory>::FoundryContext<'j> as ContextTr>::Journal,
255 >,
256)
257 -> Result<(), EVMError<DatabaseError>>;
258
259pub type NestedEvmClosureFor<'a, FEN> = NestedEvmClosure<'a, EvmFactoryFor<FEN>>;
261
262pub fn with_cloned_context<CTX: FoundryContextExt>(
267 ecx: &mut CTX,
268 f: impl FnOnce(
269 &mut CTX::Db,
270 EvmEnv<CTX::Spec, CTX::Block>,
271 JournaledState,
272 )
273 -> Result<(EvmEnv<CTX::Spec, CTX::Block>, JournaledState), EVMError<DatabaseError>>,
274) -> Result<(), EVMError<DatabaseError>> {
275 let evm_env = ecx.evm_clone();
276 let (db, journal_inner) = ecx.db_journal_inner_mut();
277 let journal_inner = journal_inner.clone();
278
279 let (sub_evm_env, sub_inner) = f(db, evm_env, journal_inner)?;
280
281 ecx.set_journal_inner(sub_inner);
283 ecx.set_evm(sub_evm_env);
284
285 Ok(())
286}
287
288pub fn get_create2_factory_call_inputs<T: JournalTr>(
290 salt: U256,
291 inputs: &CreateInputs,
292 deployer: Address,
293 journal: &mut T,
294) -> Result<CallInputs, <T::Database as Database>::Error> {
295 let calldata = [&salt.to_be_bytes::<32>()[..], &inputs.init_code()[..]].concat();
296 let account = journal.load_account_with_code(deployer)?;
297 Ok(CallInputs {
298 caller: inputs.caller(),
299 bytecode_address: deployer,
300 known_bytecode: (account.info.code_hash, account.info.code.clone().unwrap_or_default()),
301 target_address: deployer,
302 scheme: CallScheme::Call,
303 value: CallValue::Transfer(inputs.value()),
304 input: CallInput::Bytes(calldata.into()),
305 gas_limit: inputs.gas_limit(),
306 reservoir: inputs.reservoir(),
307 is_static: false,
308 return_memory_offset: 0..0,
309 charged_new_account_state_gas: false,
310 })
311}
312
313pub trait IntoInstructionResult {
315 fn into_instruction_result(self) -> InstructionResult;
316}
317
318impl IntoInstructionResult for HaltReason {
319 fn into_instruction_result(self) -> InstructionResult {
320 self.into()
321 }
322}
323
324impl IntoInstructionResult for TempoHaltReason {
325 fn into_instruction_result(self) -> InstructionResult {
326 match self {
327 Self::Ethereum(eth) => eth.into(),
328 _ => InstructionResult::PrecompileError,
329 }
330 }
331}