1use crate::{
6 FoundryBlock, FoundryChain, FoundryContextExt, FoundryInspectorExt, FoundryJournal,
7 FoundryTransaction, FromAnyRpcTransaction,
8 backend::{DatabaseExt, JournaledState},
9 refresh_chain_journal,
10};
11use alloy_consensus::{SignableTransaction, Signed, transaction::SignerRecoverable};
12use alloy_evm::{Evm, EvmEnv, EvmFactory, FromRecoveredTx, precompiles::PrecompilesMap};
13use alloy_network::Network;
14use alloy_primitives::{Address, Signature, U256};
15use alloy_rlp::Decodable;
16use foundry_common::{FoundryReceiptResponse, FoundryTransactionBuilder, fmt::UIfmt};
17use foundry_config::ExecutionSpec;
18use foundry_fork_db::{DatabaseError, ForkBlockEnv};
19use revm::{
20 Database,
21 context::{
22 ContextTr, JournalTr, LocalContextTr,
23 result::{EVMError, HaltReason, ResultAndState},
24 },
25 handler::{EvmTr, FrameResult},
26 inspector::{InspectorEvmTr, InspectorHandler, NoOpInspector},
27 interpreter::{
28 CallInput, CallInputs, CallScheme, CallValue, CreateInputs, FrameInput, GasTracker,
29 InstructionResult, SharedMemory, interpreter::EthInterpreter,
30 interpreter_action::FrameInit,
31 },
32 primitives::hardfork::SpecId,
33 state::{AccountStatus, EvmState},
34};
35use serde::{Deserialize, Serialize};
36use std::{fmt::Debug, ops::DerefMut};
37
38#[cfg(feature = "base")]
39pub mod base;
40pub mod eth;
41#[cfg(feature = "monad")]
42pub mod monad;
43#[cfg(feature = "optimism")]
44pub mod op;
45pub mod tempo;
46
47pub use eth::*;
48pub use tempo::*;
49
50#[cfg(feature = "base")]
51pub use base::*;
52
53#[cfg(feature = "monad")]
54pub use monad::*;
55
56#[cfg(feature = "optimism")]
57pub use op::*;
58
59pub trait FoundryEvmNetwork: Copy + Debug + Default + 'static {
61 type Network: Network<
62 TxEnvelope: Decodable
63 + SignerRecoverable
64 + From<Signed<<Self::Network as Network>::UnsignedTx>>
65 + for<'d> Deserialize<'d>
66 + Serialize
67 + UIfmt,
68 UnsignedTx: SignableTransaction<Signature>,
69 TransactionRequest: FoundryTransactionBuilder<Self::Network>
70 + for<'d> Deserialize<'d>
71 + Serialize,
72 ReceiptResponse: FoundryReceiptResponse,
73 >;
74 type EvmFactory: FoundryEvmFactory<Tx: FromRecoveredTx<<Self::Network as Network>::TxEnvelope>>;
75}
76
77pub trait FoundryEvmFactory:
78 EvmFactory<
79 Spec: Into<SpecId> + ExecutionSpec + Default + Copy + Unpin + Send + 'static,
80 BlockEnv: FoundryBlock + ForkBlockEnv + Default + Unpin,
81 Tx: Clone + Debug + FoundryTransaction + FromAnyRpcTransaction + Default + Send + Sync,
82 HaltReason: IntoInstructionResult,
83 Precompiles = PrecompilesMap,
84 > + Clone
85 + Debug
86 + Default
87 + 'static
88{
89 type Chain: FoundryChain<Self::Tx>;
91
92 type FoundryContext<'db>: FoundryContextExt<
94 Block = Self::BlockEnv,
95 Tx = Self::Tx,
96 Spec = Self::Spec,
97 Chain = Self::Chain,
98 Journal: FoundryJournal,
99 Db: DatabaseExt<Self>,
100 >
101 where
102 Self: 'db;
103
104 type FoundryEvm<'db, I: FoundryInspectorExt<Self::FoundryContext<'db>>>: Evm<
106 DB = &'db mut dyn DatabaseExt<Self>,
107 Tx = Self::Tx,
108 BlockEnv = Self::BlockEnv,
109 Spec = Self::Spec,
110 HaltReason = Self::HaltReason,
111 > + DerefMut<Target = Self::FoundryContext<'db>>
112 where
113 Self: 'db;
114
115 fn create_foundry_evm_with_inspector<'db, I: FoundryInspectorExt<Self::FoundryContext<'db>>>(
120 &self,
121 db: &'db mut dyn DatabaseExt<Self>,
122 evm_env: EvmEnv<Self::Spec, Self::BlockEnv>,
123 inspector: I,
124 ) -> Self::FoundryEvm<'db, I>;
125
126 fn create_nested_evm<'db>(
128 &self,
129 db: &'db mut dyn DatabaseExt<Self>,
130 evm_env: EvmEnv<Self::Spec, Self::BlockEnv>,
131 ) -> NestedEvmFor<'db, Self> {
132 self.create_nested_evm_with_inspector(db, evm_env, NoOpInspector)
133 }
134
135 fn create_nested_evm_with_inspector<'db, I>(
139 &self,
140 db: &'db mut dyn DatabaseExt<Self>,
141 evm_env: EvmEnv<Self::Spec, Self::BlockEnv>,
142 inspector: I,
143 ) -> NestedEvmFor<'db, Self>
144 where
145 I: FoundryInspectorExt<Self::FoundryContext<'db>> + 'db;
146}
147
148pub trait NestedEvm {
153 type Spec;
155 type Block;
157 type Tx: FoundryTransaction;
159 type Chain: FoundryChain<Self::Tx>;
161 type Journal: FoundryJournal;
163 fn journal_inner_mut(&mut self) -> &mut JournaledState;
165
166 fn tx_mut(&mut self) -> &mut Self::Tx;
168
169 fn chain_mut(&mut self) -> &mut Self::Chain;
171
172 fn precompiles_mut(&mut self) -> &mut PrecompilesMap;
174
175 fn journal_mut(&mut self) -> &mut Self::Journal;
177
178 fn run_execution(&mut self, frame: FrameInput) -> Result<FrameResult, EVMError<DatabaseError>>;
180
181 fn transact_raw(&mut self, tx: Self::Tx) -> eyre::Result<ResultAndState<HaltReason>>;
183
184 fn transact_replay(
189 &mut self,
190 tx: Self::Tx,
191 is_system: bool,
192 ) -> eyre::Result<Option<ResultAndState<HaltReason>>> {
193 if is_system {
194 return Ok(None);
195 }
196 self.transact_raw(tx).map(Some)
197 }
198
199 fn to_evm_env(&self) -> EvmEnv<Self::Spec, Self::Block>;
200}
201
202pub trait IntoInstructionResult {
204 fn into_instruction_result(self) -> InstructionResult;
205}
206
207pub type EvmFactoryFor<FEN> = <FEN as FoundryEvmNetwork>::EvmFactory;
209pub type FoundryContextFor<'db, FEN> =
210 <EvmFactoryFor<FEN> as FoundryEvmFactory>::FoundryContext<'db>;
211pub type TxEnvFor<FEN> = <EvmFactoryFor<FEN> as EvmFactory>::Tx;
212pub type HaltReasonFor<FEN> = <EvmFactoryFor<FEN> as EvmFactory>::HaltReason;
213pub type SpecFor<FEN> = <EvmFactoryFor<FEN> as EvmFactory>::Spec;
214pub type BlockEnvFor<FEN> = <EvmFactoryFor<FEN> as EvmFactory>::BlockEnv;
215pub type PrecompilesFor<FEN> = <EvmFactoryFor<FEN> as EvmFactory>::Precompiles;
216pub type EvmEnvFor<FEN> = EvmEnv<SpecFor<FEN>, BlockEnvFor<FEN>>;
217pub type NetworkFor<FEN> = <FEN as FoundryEvmNetwork>::Network;
218pub type TxEnvelopeFor<FEN> = <NetworkFor<FEN> as Network>::TxEnvelope;
219pub type TransactionRequestFor<FEN> = <NetworkFor<FEN> as Network>::TransactionRequest;
220pub type TransactionResponseFor<FEN> = <NetworkFor<FEN> as Network>::TransactionResponse;
221pub type BlockResponseFor<FEN> = <NetworkFor<FEN> as Network>::BlockResponse;
222
223pub type ChainFor<FEN> = <EvmFactoryFor<FEN> as FoundryEvmFactory>::Chain;
224
225pub type NestedEvmFor<'db, F> = Box<
227 dyn NestedEvm<
228 Spec = <F as EvmFactory>::Spec,
229 Block = <F as EvmFactory>::BlockEnv,
230 Tx = <F as EvmFactory>::Tx,
231 Chain = <F as FoundryEvmFactory>::Chain,
232 Journal = <<F as FoundryEvmFactory>::FoundryContext<'db> as ContextTr>::Journal,
233 > + 'db,
234>;
235
236pub type NestedEvmClosure<'a, F> = &'a mut dyn for<'j> FnMut(
238 &mut dyn NestedEvm<
239 Spec = <F as EvmFactory>::Spec,
240 Block = <F as EvmFactory>::BlockEnv,
241 Tx = <F as EvmFactory>::Tx,
242 Chain = <F as FoundryEvmFactory>::Chain,
243 Journal = <<F as FoundryEvmFactory>::FoundryContext<'j> as ContextTr>::Journal,
244 >,
245)
246 -> Result<(), EVMError<DatabaseError>>;
247
248pub type NestedEvmClosureFor<'a, FEN> = NestedEvmClosure<'a, EvmFactoryFor<FEN>>;
250
251pub fn with_inherited_evm<F, I>(
261 ecx: &mut F::FoundryContext<'_>,
262 inspector: I,
263 f: NestedEvmClosure<'_, F>,
264) -> Result<(), EVMError<DatabaseError>>
265where
266 F: FoundryEvmFactory,
267 I: for<'db> FoundryInspectorExt<F::FoundryContext<'db>>,
268{
269 let evm_env = ecx.evm_clone();
270 let chain_context = ecx.chain().clone();
271 #[cfg(feature = "monad")]
272 let mut reserve_balance = FoundryJournal::capture_reserve_balance(ecx.journal());
273 let (evm_env, journaled_state, chain_context) = {
274 let (db, journaled_state) = ecx.db_journal_inner_mut();
275 let journaled_state = journaled_state.clone();
276 let mut evm = F::default().create_nested_evm_with_inspector(db, evm_env, inspector);
277 *evm.chain_mut() = chain_context;
278 *evm.journal_inner_mut() = journaled_state;
279 #[cfg(feature = "monad")]
280 {
281 FoundryJournal::restore_reserve_balance(evm.journal_mut(), reserve_balance);
282 refresh_nested_chain_journal(&mut *evm);
283 }
284 f(&mut *evm)?;
285 #[cfg(feature = "monad")]
286 {
287 reserve_balance = FoundryJournal::capture_reserve_balance(evm.journal_mut());
288 }
289 (evm.to_evm_env(), evm.journal_inner_mut().clone(), evm.chain_mut().clone())
290 };
291 ecx.set_journal_inner(journaled_state);
292 ecx.set_evm(evm_env);
293 *ecx.chain_mut() = chain_context;
294 #[cfg(feature = "monad")]
295 FoundryJournal::restore_reserve_balance(ecx.journal_mut(), reserve_balance);
296 refresh_chain_journal(ecx);
297 Ok(())
298}
299
300pub fn prepare_child_state(journal: &JournaledState) -> EvmState {
306 let mut state = journal.state.clone();
307 for (address, account) in &mut state {
308 if journal.warm_addresses.is_cold(address) {
309 account.mark_cold();
310 }
311 for slot in account.storage.values_mut() {
312 slot.is_cold = true;
313 slot.original_value = slot.present_value;
314 }
315 }
316 state
317}
318
319pub fn merge_child_state(parent: &mut EvmState, child: EvmState) {
326 for (address, mut account) in child {
327 let Some(parent_account) = parent.get_mut(&address) else {
328 parent.insert(address, account);
329 continue;
330 };
331 if account.status.contains(AccountStatus::Cold)
332 && !parent_account.status.contains(AccountStatus::Cold)
333 {
334 account.status -= AccountStatus::Cold;
335 }
336 parent_account.info = account.info;
337 parent_account.status |= account.status;
338 for (key, slot) in account.storage {
339 let Some(parent_slot) = parent_account.storage.get_mut(&key) else {
340 parent_account.storage.insert(key, slot);
341 continue;
342 };
343 parent_slot.present_value = slot.present_value;
344 parent_slot.is_cold &= slot.is_cold;
345 }
346 }
347}
348
349pub(crate) fn run_inspected_frame<H>(
351 evm: &mut H::Evm,
352 mut handler: H,
353 frame_input: FrameInput,
354) -> Result<FrameResult, H::Error>
355where
356 H: InspectorHandler<IT = EthInterpreter>,
357 H::Evm: InspectorEvmTr,
358{
359 let memory =
360 SharedMemory::new_with_buffer(evm.ctx_ref().local().shared_memory_buffer().clone());
361 let first_frame_input = FrameInit { depth: 0, memory, frame_input };
362 let mut frame_result = handler.inspect_run_exec_loop(evm, first_frame_input)?;
363 let mut parent_gas = GasTracker::new(
364 frame_result.gas().limit(),
365 frame_result.gas().remaining(),
366 frame_result.gas().reservoir(),
367 );
368 handler.last_frame_result(evm, &mut frame_result, &mut parent_gas)?;
369 Ok(frame_result)
370}
371
372pub fn get_create2_factory_call_inputs<T: JournalTr>(
374 salt: U256,
375 inputs: &CreateInputs,
376 deployer: Address,
377 journal: &mut T,
378) -> Result<CallInputs, <T::Database as Database>::Error> {
379 let calldata = [&salt.to_be_bytes::<32>()[..], &inputs.init_code()[..]].concat();
380 let account = journal.load_account_with_code(deployer)?;
381 Ok(CallInputs {
382 caller: inputs.caller(),
383 bytecode_address: deployer,
384 known_bytecode: (account.info.code_hash, account.info.code.clone().unwrap_or_default()),
385 target_address: deployer,
386 scheme: CallScheme::Call,
387 value: CallValue::Transfer(inputs.value()),
388 input: CallInput::Bytes(calldata.into()),
389 gas_limit: inputs.gas_limit(),
390 reservoir: inputs.reservoir(),
391 is_static: false,
392 return_memory_offset: 0..0,
393 charged_new_account_state_gas: false,
394 })
395}
396
397#[cfg(test)]
398mod tests {
399 use super::*;
400 use crate::backend::Backend;
401 use alloy_evm::EthEvmFactory;
402 use revm::{
403 context::Transaction,
404 state::{Account, AccountInfo, EvmStorageSlot, TransactionId},
405 };
406
407 #[cfg(feature = "monad")]
408 use alloy_monad_evm::MonadEvmFactory;
409 #[cfg(feature = "monad")]
410 use monad_revm::{MonadHardfork, MonadJournalTr, reserve_balance::tracker::ReserveBalanceInit};
411 #[cfg(feature = "monad")]
412 use revm::context::{BlockEnv, CfgEnv};
413
414 #[test]
415 fn inherited_journal_publishes_only_after_success() {
416 let address = Address::with_last_byte(0x42);
417 for succeeds in [false, true] {
418 let mut db = Backend::<EthEvmNetwork>::spawn(None).unwrap();
419 let mut parent = EthEvmFactory::default().create_foundry_evm_with_inspector(
420 &mut db,
421 EvmEnvFor::<EthEvmNetwork>::default(),
422 NoOpInspector,
423 );
424 parent.journaled_state.inner.depth = 3;
425 parent
426 .journaled_state
427 .inner
428 .state
429 .insert(address, Account::from(AccountInfo::from_balance(U256::from(7))));
430 let caller = parent.tx().caller();
431 let result =
432 with_inherited_evm::<EthEvmFactory, _>(&mut parent, NoOpInspector, &mut |child| {
433 assert_eq!(child.journal_inner_mut().depth, 3);
434 assert_eq!(
435 child.journal_inner_mut().state[&address].info.balance,
436 U256::from(7)
437 );
438 child.journal_inner_mut().depth = 4;
439 child.journal_inner_mut().state.get_mut(&address).unwrap().info.balance =
440 U256::from(9);
441 child.tx_mut().caller = address;
442 if succeeds { Ok(()) } else { Err(EVMError::Custom("abort child".into())) }
443 });
444 assert_eq!(result.is_ok(), succeeds);
445 assert_eq!(parent.journaled_state.inner.depth, if succeeds { 4 } else { 3 });
446 assert_eq!(
447 parent.journaled_state.inner.state[&address].info.balance,
448 U256::from(if succeeds { 9 } else { 7 })
449 );
450 assert_eq!(parent.tx().caller(), caller);
451 }
452 }
453
454 #[cfg(feature = "monad")]
455 #[test]
456 fn inherited_monad_tracker_and_chain_publish_together() {
457 let sender = Address::with_last_byte(0x42);
458 for succeeds in [false, true] {
459 let mut db = Backend::<MonadEvmNetwork>::spawn(None).unwrap();
460 let mut parent = MonadEvmFactory::default().create_foundry_evm_with_inspector(
461 &mut db,
462 EvmEnv::new(CfgEnv::new_with_spec(MonadHardfork::MonadNine), BlockEnv::default()),
463 NoOpInspector,
464 );
465 let account = Account::from(AccountInfo::from_balance(U256::from(12)));
466 let chain = parent.chain().clone();
467 parent.journaled_state.reserve_balance_mut().init(ReserveBalanceInit {
468 chain: &chain,
469 spec: MonadHardfork::MonadNine,
470 sender,
471 effective_gas_price: 0,
472 gas_limit: 0,
473 sender_is_delegated: false,
474 sender_account: Some(&account),
475 });
476 parent.journaled_state.inner.state.insert(sender, account);
477 let tracker = parent.journaled_state.reserve_balance().clone();
478 let result = with_inherited_evm::<MonadEvmFactory, _>(
479 &mut parent,
480 NoOpInspector,
481 &mut |child| {
482 assert_eq!(child.journal_mut().reserve_balance(), &tracker);
483 child.chain_mut().parent_senders_and_authorities.insert(sender);
484 child.journal_inner_mut().state.get_mut(&sender).unwrap().info.balance =
485 U256::from(9);
486 if succeeds { Ok(()) } else { Err(EVMError::Custom("abort child".into())) }
487 },
488 );
489 assert_eq!(result.is_ok(), succeeds);
490 if succeeds {
491 assert!(parent.chain().parent_senders_and_authorities.contains(&sender));
492 assert!(parent.journaled_state.reserve_balance().has_violation());
493 } else {
494 assert_eq!(parent.chain(), &chain);
495 assert_eq!(parent.journaled_state.reserve_balance(), &tracker);
496 assert_eq!(
497 parent.journaled_state.inner.state[&sender].info.balance,
498 U256::from(12)
499 );
500 }
501 }
502 }
503
504 #[test]
505 fn preparation_preserves_creation_and_protocol_warmth() {
506 let address = Address::with_last_byte(0x42);
507 let protocol_address = Address::with_last_byte(0x43);
508 let key = U256::ONE;
509 let mut account = Account::from(AccountInfo::default());
510 account.mark_created_locally();
511 account.mark_touch();
512 account.storage.insert(
513 key,
514 EvmStorageSlot::new_changed(U256::from(3), U256::from(7), TransactionId::ZERO),
515 );
516 let mut journal = JournaledState::default();
517 journal.state.insert(address, account.clone());
518 journal.state.insert(protocol_address, account);
519 journal.warm_addresses.set_coinbase(protocol_address);
520 let before = journal.state.clone();
521
522 let child = prepare_child_state(&journal);
523
524 assert_eq!(journal.state, before);
525 assert!(child[&address].is_created_locally());
526 assert!(child[&address].is_touched());
527 assert!(child[&address].status.contains(AccountStatus::Cold));
528 assert!(!child[&protocol_address].status.contains(AccountStatus::Cold));
529 for account in child.values() {
530 assert_eq!(account.storage[&key].original_value, U256::from(7));
531 assert_eq!(account.storage[&key].present_value, U256::from(7));
532 assert!(account.storage[&key].is_cold);
533 }
534 }
535
536 #[test]
537 fn settlement_preserves_parent_original_values_and_combines_warmth() {
538 let address = Address::with_last_byte(0x42);
539 let key = U256::ONE;
540 for parent_cold in [false, true] {
541 for child_cold in [false, true] {
542 let mut account = Account::from(AccountInfo::default());
543 account.status.set(AccountStatus::Cold, parent_cold);
544 account.mark_created_locally();
545 let mut slot =
546 EvmStorageSlot::new_changed(U256::from(3), U256::from(7), TransactionId::ZERO);
547 slot.is_cold = parent_cold;
548 account.storage.insert(key, slot);
549 let mut parent = EvmState::from_iter([(address, account)]);
550 let mut account = Account::from(AccountInfo::from_balance(U256::from(9)));
551 account.status.set(AccountStatus::Cold, child_cold);
552 account.mark_touch();
553 let mut slot =
554 EvmStorageSlot::new_changed(U256::from(7), U256::from(11), TransactionId::ZERO);
555 slot.is_cold = child_cold;
556 account.storage.insert(key, slot);
557
558 merge_child_state(&mut parent, EvmState::from_iter([(address, account)]));
559
560 let account = &parent[&address];
561 assert!(account.is_created_locally());
562 assert!(account.is_touched());
563 assert_eq!(account.info.balance, U256::from(9));
564 assert_eq!(account.status.contains(AccountStatus::Cold), parent_cold);
566 assert_eq!(account.storage[&key].original_value, U256::from(3));
567 assert_eq!(account.storage[&key].present_value, U256::from(11));
568 assert_eq!(account.storage[&key].is_cold, parent_cold && child_cold);
569 }
570 }
571 }
572}