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