1use crate::inspectors::{
6 Cheatcodes, CmpOperands, EdgeCoverage, EdgeIndexMap, InspectorData, InspectorStack,
7 cheatcodes::BroadcastableTransactions,
8};
9use alloy_dyn_abi::{DynSolValue, FunctionExt, JsonAbiExt};
10use alloy_eips::eip4788::{BEACON_ROOTS_ADDRESS, SYSTEM_ADDRESS};
11use alloy_evm::Evm;
12use alloy_json_abi::Function;
13use alloy_primitives::{
14 Address, B256, Bytes, Log, TxKind, U256, keccak256,
15 map::{AddressHashMap, HashMap},
16};
17use alloy_sol_types::{SolCall, sol};
18use eyre::WrapErr;
19use foundry_evm_core::{
20 EvmEnv, FoundryBlock, FoundryChain, FoundryTransaction,
21 backend::{
22 Backend, BackendError, BackendResult, CowBackend, DatabaseError, DatabaseExt,
23 GLOBAL_FAIL_SLOT,
24 },
25 constants::{
26 CALLER, CHEATCODE_ADDRESS, CHEATCODE_CONTRACT_HASH, DEFAULT_CREATE2_DEPLOYER,
27 DEFAULT_CREATE2_DEPLOYER_CODE, DEFAULT_CREATE2_DEPLOYER_DEPLOYER,
28 },
29 decode::{RevertDecoder, SkipReason},
30 eip2935::{
31 HISTORY_STORAGE_ADDRESS, HISTORY_STORAGE_CODE, history_storage_slot, history_storage_value,
32 history_window_start,
33 },
34 evm::{
35 ChainFor, EthEvmNetwork, EvmEnvFor, FoundryEvmFactory, FoundryEvmNetwork,
36 IntoInstructionResult, SpecFor, TxEnvFor,
37 },
38 utils::StateChangeset,
39};
40use foundry_evm_coverage::HitMaps;
41use foundry_evm_fuzz::ObservedCall;
42use foundry_evm_networks::NetworkConfigs;
43use foundry_evm_traces::{SparsedTraceArena, TraceRequirements};
44use revm::{
45 bytecode::Bytecode,
46 context::{Block, Cfg, ContextTr, Transaction},
47 context_interface::{
48 cfg::gas_params::Eip2780TxInfo,
49 result::{ExecutionResult, Output, ResultAndState},
50 transaction::SignedAuthorization,
51 },
52 database::{Database, DatabaseCommit, DatabaseRef},
53 interpreter::{InstructionResult, return_ok},
54 primitives::hardfork::SpecId,
55};
56use sancov::SancovGuard;
57use std::{
58 borrow::Cow,
59 sync::{
60 Arc,
61 atomic::{AtomicBool, Ordering},
62 },
63 time::{Duration, Instant},
64};
65
66#[cfg(feature = "monad")]
67use foundry_common::{SYSTEM_TRANSACTION_TYPE, is_known_system_sender};
68#[cfg(feature = "monad")]
69use foundry_evm_core::{
70 evm::{MonadEvmNetwork, try_transact_monad_system_replay},
71 refresh_chain_journal,
72};
73
74mod builder;
75pub use builder::ExecutorBuilder;
76
77mod campaign;
78
79pub mod fuzz;
80pub use fuzz::FuzzedExecutor;
81
82pub mod invariant;
83pub use invariant::InvariantExecutor;
84
85mod corpus;
86mod corpus_io;
87mod sancov;
88mod showmap;
89mod trace;
90
91pub use corpus::{DynamicTargetCtx, StatelessReplayTarget, persist_corpus_seed};
92pub use corpus_io::{
93 CorpusDirEntry, canonical_replay_dirs, parse_corpus_filename, read_corpus_dir, read_corpus_tree,
94};
95pub use showmap::{
96 InvariantReplayOptions, MinimizationReplayInput, ReplayFailure, ReplayObservation,
97 ShowmapDomain, ShowmapOpts, ShowmapReplayTarget, ShowmapStats, replay_corpus_to_showmap,
98 replay_sequence_for_minimization,
99};
100pub use trace::{TracingExecutor, TracingFork};
101
102const DURATION_BETWEEN_METRICS_REPORT: Duration = Duration::from_secs(5);
103
104sol! {
105 interface ITest {
106 function setUp() external;
107 function failed() external view returns (bool failed);
108
109 #[derive(Default)]
110 function beforeTestSetup(bytes4 testSelector) public view returns (bytes[] memory beforeTestCalldata);
111 }
112}
113
114#[derive(Clone, Debug)]
126pub struct Executor<FEN: FoundryEvmNetwork> {
127 backend: Arc<Backend<FEN>>,
136 evm_env: EvmEnvFor<FEN>,
138 tx_env: TxEnvFor<FEN>,
140 inspector: InspectorStack<FEN>,
142 gas_limit: u64,
144 legacy_assertions: bool,
146}
147
148#[cfg(feature = "monad")]
149impl Executor<MonadEvmNetwork> {
150 #[instrument(name = "transact_system_replay", level = "debug", skip_all)]
152 pub fn try_transact_system_replay_with_env_and_context(
153 &mut self,
154 mut evm_env: EvmEnvFor<MonadEvmNetwork>,
155 mut tx_env: TxEnvFor<MonadEvmNetwork>,
156 chain_context: ChainFor<MonadEvmNetwork>,
157 ) -> eyre::Result<Option<RawCallResult<MonadEvmNetwork>>> {
158 let mut stack = self.inspector().clone();
159 let mut backend = CowBackend::new_borrowed(self.backend());
160 let Some(result) = backend.try_inspect_system_replay_with_context(
161 &mut evm_env,
162 &mut tx_env,
163 chain_context,
164 &mut stack,
165 )?
166 else {
167 return Ok(None);
168 };
169 let has_state_snapshot_failure = backend.has_state_snapshot_failure();
170 let fork_block_number = backend.active_fork_block_number();
171 let mut result = convert_executed_result(
172 evm_env,
173 tx_env,
174 stack,
175 result,
176 &backend,
177 has_state_snapshot_failure,
178 fork_block_number,
179 )?;
180 self.commit(&mut result);
181 Ok(Some(result))
182 }
183
184 #[instrument(name = "transact_monad_block_replay", level = "debug", skip_all)]
186 pub fn transact_with_monad_block_replay(
187 &mut self,
188 evm_env: EvmEnvFor<MonadEvmNetwork>,
189 target_tx_env: TxEnvFor<MonadEvmNetwork>,
190 target_chain_context: ChainFor<MonadEvmNetwork>,
191 replay: Vec<(B256, TxEnvFor<MonadEvmNetwork>, ChainFor<MonadEvmNetwork>)>,
192 replay_system_txes: bool,
193 ) -> eyre::Result<Option<(RawCallResult<MonadEvmNetwork>, bool)>> {
194 let block_number = evm_env.block_env.number();
195 let mut stack = self.inspector().clone();
196 let sancov_edges = stack.inner.sancov_edges;
197 let sancov_trace_cmp = stack.inner.sancov_trace_cmp;
198 let sancov_active = sancov_edges || sancov_trace_cmp;
199 let backend = self.backend_mut();
200
201 let (result, evm_env, tx_env, used_system_replay) = {
202 let caller = target_tx_env.caller();
203 backend.set_caller(caller).set_spec_id(evm_env.cfg_env.spec);
204 let target_contract = match target_tx_env.kind() {
205 TxKind::Call(to) => to,
206 TxKind::Create => caller.create(target_tx_env.nonce()),
207 };
208 backend.set_test_contract(target_contract);
209 let mut evm = <MonadEvmNetwork as FoundryEvmNetwork>::EvmFactory::default()
210 .create_foundry_evm_with_inspector(backend, evm_env, &mut stack);
211 *evm.chain_mut() = target_chain_context.clone();
212 evm.disable_inspector();
213 for (tx_hash, tx_env, chain_context) in replay {
214 evm.ctx_mut().chain = chain_context;
215 refresh_chain_journal(evm.ctx_mut());
216 evm.ctx_mut().cfg.disable_balance_check = true;
217 let is_system = is_known_system_sender(tx_env.caller())
218 || tx_env.tx_type() == SYSTEM_TRANSACTION_TYPE;
219 let result = if is_system {
220 try_transact_monad_system_replay(&mut evm, &tx_env).wrap_err_with(|| {
221 format!(
222 "Failed to replay system transaction: {tx_hash:?} in block {block_number}"
223 )
224 })?
225 } else {
226 None
227 };
228 if let Some(result) = result {
229 evm.db_mut().commit(result.state);
230 } else if !is_system || replay_system_txes {
231 let created = match tx_env.kind() {
232 TxKind::Create => Some(tx_env.caller().create(tx_env.nonce())),
233 TxKind::Call(_) => None,
234 };
235 let result = evm.transact(tx_env).wrap_err_with(|| {
236 format!(
237 "Failed to execute transaction: {tx_hash:?} in block {block_number}"
238 )
239 })?;
240 if result.result.is_success()
241 && let Some(address) = created
242 {
243 evm.db_mut().add_persistent_account(address);
244 }
245 evm.db_mut().commit(result.state);
246 }
247 }
248
249 evm.enable_inspector();
250 evm.ctx_mut().chain = target_chain_context;
251 refresh_chain_journal(evm.ctx_mut());
252 let _guard = sancov_active.then(|| SancovGuard::new(sancov_edges, sancov_trace_cmp));
253 let target_is_system = is_known_system_sender(target_tx_env.caller())
254 || target_tx_env.tx_type() == SYSTEM_TRANSACTION_TYPE;
255 let system_result = if target_is_system {
256 try_transact_monad_system_replay(&mut evm, &target_tx_env)?
257 } else {
258 None
259 };
260 let (result, used_system_replay) = if let Some(result) = system_result {
261 (result, true)
262 } else if target_is_system && !replay_system_txes {
263 return Ok(None);
264 } else {
265 (evm.transact(target_tx_env.clone()).wrap_err("EVM error")?, false)
266 };
267 let tx_env = if used_system_replay { target_tx_env } else { evm.tx().clone() };
268 let evm_env = evm.finish().1;
269 (result, evm_env, tx_env, used_system_replay)
270 };
271
272 let has_state_snapshot_failure = backend.has_state_snapshot_failure();
273 let fork_block_number = backend.active_fork_block_number();
274 let mut result = convert_executed_result(
275 evm_env,
276 tx_env,
277 stack,
278 result,
279 &*backend,
280 has_state_snapshot_failure,
281 fork_block_number,
282 )?;
283 if sancov_edges {
284 SancovGuard::append_edges_into(&mut result);
285 }
286 if sancov_trace_cmp {
287 SancovGuard::drain_cmp_into(&mut result);
288 }
289 self.commit(&mut result);
290 Ok(Some((result, used_system_replay)))
291 }
292}
293
294impl<FEN: FoundryEvmNetwork> Executor<FEN> {
295 #[inline]
297 pub fn new(
298 mut backend: Backend<FEN>,
299 evm_env: EvmEnvFor<FEN>,
300 tx_env: TxEnvFor<FEN>,
301 mut inspector: InspectorStack<FEN>,
302 networks: NetworkConfigs,
303 gas_limit: u64,
304 legacy_assertions: bool,
305 ) -> Self {
306 inspector.networks(networks);
307 backend.set_networks(networks);
308 let extra_cheatcode_addresses = inspector.extra_cheatcode_addresses();
309 backend.extend_persistent_accounts(extra_cheatcode_addresses.iter().copied());
310
311 backend.insert_account_info(
314 CHEATCODE_ADDRESS,
315 revm::state::AccountInfo {
316 code: Some(Bytecode::new_raw(Bytes::from_static(&[0]))),
317 code_hash: CHEATCODE_CONTRACT_HASH,
320 ..Default::default()
321 },
322 );
323
324 for &address in extra_cheatcode_addresses {
325 backend.insert_account_info(
326 address,
327 revm::state::AccountInfo {
328 code: Some(Bytecode::new_raw(Bytes::from_static(&[0]))),
329 code_hash: keccak256(address),
330 ..Default::default()
331 },
332 );
333 }
334
335 if !backend.is_in_forking_mode() && evm_env.cfg_env.spec.into() >= SpecId::PRAGUE {
336 let mut account =
337 backend.basic_ref(HISTORY_STORAGE_ADDRESS).unwrap_or_default().unwrap_or_default();
338 account.code_hash = keccak256(&HISTORY_STORAGE_CODE);
339 account.code = Some(Bytecode::new_raw(HISTORY_STORAGE_CODE.clone()));
340 backend.insert_account_info(HISTORY_STORAGE_ADDRESS, account);
341
342 let current_block = evm_env.block_env.number();
343 let mut block_number = history_window_start(current_block);
344 while block_number < current_block {
345 let block_hash =
346 backend.block_hash(block_number.saturating_to()).unwrap_or_default();
347 let slot = history_storage_slot(block_number);
348 let value = history_storage_value(block_hash);
349 let _ = backend.insert_account_storage(HISTORY_STORAGE_ADDRESS, slot, value);
350 block_number += U256::from(1);
351 }
352 }
353
354 Self {
355 backend: Arc::new(backend),
356 evm_env,
357 tx_env,
358 inspector,
359 gas_limit,
360 legacy_assertions,
361 }
362 }
363
364 fn clone_with_backend(&self, backend: Backend<FEN>) -> Self {
365 let evm_env = self.evm_env.clone();
366 Self {
367 backend: Arc::new(backend),
368 evm_env,
369 tx_env: self.tx_env.clone(),
370 inspector: self.inspector().clone(),
371 gas_limit: self.gas_limit,
372 legacy_assertions: self.legacy_assertions,
373 }
374 }
375
376 pub fn backend(&self) -> &Backend<FEN> {
378 &self.backend
379 }
380
381 pub fn backend_mut(&mut self) -> &mut Backend<FEN> {
386 Arc::make_mut(&mut self.backend)
387 }
388
389 fn chain_context_for_synthetic_transaction(
390 &self,
391 tx: &TxEnvFor<FEN>,
392 ) -> eyre::Result<ChainFor<FEN>> {
393 self.backend().chain_context_for_synthetic_transaction(tx)
394 }
395
396 pub const fn evm_env(&self) -> &EvmEnvFor<FEN> {
398 &self.evm_env
399 }
400
401 pub const fn evm_env_mut(&mut self) -> &mut EvmEnvFor<FEN> {
403 &mut self.evm_env
404 }
405
406 pub const fn tx_env(&self) -> &TxEnvFor<FEN> {
408 &self.tx_env
409 }
410
411 pub const fn tx_env_mut(&mut self) -> &mut TxEnvFor<FEN> {
413 &mut self.tx_env
414 }
415
416 pub const fn inspector(&self) -> &InspectorStack<FEN> {
418 &self.inspector
419 }
420
421 pub const fn inspector_mut(&mut self) -> &mut InspectorStack<FEN> {
423 &mut self.inspector
424 }
425
426 pub const fn spec_id(&self) -> SpecFor<FEN> {
428 self.evm_env.cfg_env.spec
429 }
430
431 pub fn set_spec_id(&mut self, spec_id: SpecFor<FEN>) {
433 self.evm_env.cfg_env.set_spec_and_mainnet_gas_params(spec_id);
434 }
435
436 pub const fn gas_limit(&self) -> u64 {
441 self.gas_limit
442 }
443
444 pub const fn set_gas_limit(&mut self, gas_limit: u64) {
446 self.gas_limit = gas_limit;
447 }
448
449 pub const fn legacy_assertions(&self) -> bool {
452 self.legacy_assertions
453 }
454
455 pub const fn set_legacy_assertions(&mut self, legacy_assertions: bool) {
458 self.legacy_assertions = legacy_assertions;
459 }
460
461 pub fn deploy_create2_deployer(&mut self) -> eyre::Result<()> {
463 trace!("deploying local create2 deployer");
464 let create2_deployer_account = self
465 .backend()
466 .basic_ref(DEFAULT_CREATE2_DEPLOYER)?
467 .ok_or_else(|| BackendError::MissingAccount(DEFAULT_CREATE2_DEPLOYER))?;
468
469 if create2_deployer_account.code.is_none_or(|code| code.is_empty()) {
471 let creator = DEFAULT_CREATE2_DEPLOYER_DEPLOYER;
472
473 let initial_balance = self.get_balance(creator)?;
475 self.set_balance(creator, U256::MAX)?;
476
477 let res =
478 self.deploy(creator, DEFAULT_CREATE2_DEPLOYER_CODE.into(), U256::ZERO, None)?;
479 trace!(create2=?res.address, "deployed local create2 deployer");
480
481 self.set_balance(creator, initial_balance)?;
482 }
483 Ok(())
484 }
485
486 pub fn set_balance(&mut self, address: Address, amount: U256) -> BackendResult<()> {
488 trace!(?address, ?amount, "setting account balance");
489 let mut account = self.backend().basic_ref(address)?.unwrap_or_default();
490 account.balance = amount;
491 self.backend_mut().insert_account_info(address, account);
492 Ok(())
493 }
494
495 pub fn get_balance(&self, address: Address) -> BackendResult<U256> {
497 Ok(self.backend().basic_ref(address)?.map(|acc| acc.balance).unwrap_or_default())
498 }
499
500 pub fn set_account_nonce(&mut self, address: Address, nonce: u64) -> BackendResult<()> {
502 let mut account = self.backend().basic_ref(address)?.unwrap_or_default();
503 account.nonce = nonce;
504 self.backend_mut().insert_account_info(address, account);
505 Ok(())
506 }
507
508 pub fn set_nonce(&mut self, address: Address, nonce: u64) -> BackendResult<()> {
510 self.set_account_nonce(address, nonce)?;
511 self.tx_env_mut().set_nonce(nonce);
512 Ok(())
513 }
514
515 pub fn get_nonce(&self, address: Address) -> BackendResult<u64> {
517 Ok(self.backend().basic_ref(address)?.map(|acc| acc.nonce).unwrap_or_default())
518 }
519
520 pub fn set_code(&mut self, address: Address, code: Bytecode) -> BackendResult<()> {
522 let mut account = self.backend().basic_ref(address)?.unwrap_or_default();
523 account.code_hash = keccak256(code.original_byte_slice());
524 account.code = Some(code);
525 self.backend_mut().insert_account_info(address, account);
526 Ok(())
527 }
528
529 pub fn set_storage(
531 &mut self,
532 address: Address,
533 storage: HashMap<U256, U256>,
534 ) -> BackendResult<()> {
535 self.backend_mut().replace_account_storage(address, storage)?;
536 Ok(())
537 }
538
539 pub fn set_storage_slot(
541 &mut self,
542 address: Address,
543 slot: U256,
544 value: U256,
545 ) -> BackendResult<()> {
546 self.backend_mut().insert_account_storage(address, slot, value)?;
547 Ok(())
548 }
549
550 pub fn apply_prestate_trace(
556 &mut self,
557 prestate: std::collections::BTreeMap<Address, alloy_rpc_types::trace::geth::AccountState>,
558 ) -> eyre::Result<()> {
559 let backend = self.backend_mut();
560 for (address, account_state) in prestate {
561 let code = account_state.code.map(Bytecode::new_raw).unwrap_or_default();
562 let info = revm::state::AccountInfo {
563 nonce: account_state.nonce.unwrap_or_default(),
564 balance: account_state.balance.unwrap_or_default(),
565 code_hash: keccak256(code.original_byte_slice()),
566 code: Some(code),
567 account_id: Default::default(),
568 };
569 backend.insert_account_info(address, info);
570
571 for (slot, value) in account_state.storage {
572 let slot = U256::from_be_bytes(slot.0);
573 let value = U256::from_be_bytes(value.0);
574 backend.insert_account_storage(address, slot, value)?;
575 }
576 }
577 Ok(())
578 }
579
580 pub fn is_empty_code(&self, address: Address) -> BackendResult<bool> {
582 Ok(self.backend().basic_ref(address)?.map(|acc| acc.is_empty_code_hash()).unwrap_or(true))
583 }
584
585 #[inline]
586 pub fn set_trace_requirements(&mut self, requirements: TraceRequirements) -> &mut Self {
587 self.inspector_mut().tracing_requirements(requirements);
588 self
589 }
590
591 #[inline]
592 pub fn set_script_execution(&mut self, script_address: Address) {
593 self.inspector_mut().script(script_address);
594 }
595
596 #[inline]
597 pub fn set_trace_printer(&mut self, trace_printer: bool) -> &mut Self {
598 self.inspector_mut().print(trace_printer);
599 self
600 }
601
602 #[inline]
603 pub fn create2_deployer(&self) -> Address {
604 self.inspector().create2_deployer
605 }
606
607 pub fn deploy(
612 &mut self,
613 from: Address,
614 code: Bytes,
615 value: U256,
616 rd: Option<&RevertDecoder>,
617 ) -> Result<DeployResult<FEN>, EvmError<FEN>> {
618 let (evm_env, tx_env) = self.prepare_call_env(from, TxKind::Create, code, value);
619 self.deploy_with_env(evm_env, tx_env, rd)
620 }
621
622 pub fn deploy_with_context(
624 &mut self,
625 from: Address,
626 code: Bytes,
627 value: U256,
628 chain_context: ChainFor<FEN>,
629 rd: Option<&RevertDecoder>,
630 ) -> Result<DeployResult<FEN>, EvmError<FEN>> {
631 let (evm_env, tx_env) = self.prepare_call_env(from, TxKind::Create, code, value);
632 self.deploy_with_env_and_context(evm_env, tx_env, chain_context, rd)
633 }
634
635 #[instrument(name = "deploy", level = "debug", skip_all)]
642 pub fn deploy_with_env(
643 &mut self,
644 evm_env: EvmEnvFor<FEN>,
645 tx_env: TxEnvFor<FEN>,
646 rd: Option<&RevertDecoder>,
647 ) -> Result<DeployResult<FEN>, EvmError<FEN>> {
648 let chain_context = self.chain_context_for_synthetic_transaction(&tx_env)?;
649 self.deploy_with_env_and_context(evm_env, tx_env, chain_context, rd)
650 }
651
652 #[instrument(name = "deploy", level = "debug", skip_all)]
658 pub fn deploy_with_env_and_context(
659 &mut self,
660 evm_env: EvmEnvFor<FEN>,
661 tx_env: TxEnvFor<FEN>,
662 chain_context: ChainFor<FEN>,
663 rd: Option<&RevertDecoder>,
664 ) -> Result<DeployResult<FEN>, EvmError<FEN>> {
665 assert!(
666 matches!(tx_env.kind(), TxKind::Create),
667 "Expected create transaction, got {:?}",
668 tx_env.kind()
669 );
670 trace!(sender=%tx_env.caller(), "deploying contract");
671
672 let mut result = self.transact_with_env_and_context(evm_env, tx_env, chain_context)?;
673 result = result.into_result(rd)?;
674 let Some(Output::Create(_, Some(address))) = result.out else {
675 panic!("Deployment succeeded, but no address was returned: {result:#?}");
676 };
677
678 self.backend_mut().add_persistent_account(address);
681
682 trace!(%address, "deployed contract");
683
684 Ok(DeployResult { raw: result, address })
685 }
686
687 #[instrument(name = "setup", level = "debug", skip_all)]
694 pub fn setup(
695 &mut self,
696 from: Option<Address>,
697 to: Address,
698 rd: Option<&RevertDecoder>,
699 ) -> Result<RawCallResult<FEN>, EvmError<FEN>> {
700 trace!(?from, ?to, "setting up contract");
701
702 let from = from.unwrap_or(CALLER);
703 self.backend_mut().set_test_contract(to).set_caller(from);
704 let calldata = Bytes::from_static(&ITest::setUpCall::SELECTOR);
705 let mut res = self.transact_raw(from, to, calldata, U256::ZERO)?;
706 res = res.into_result(rd)?;
707
708 self.evm_env_mut().block_env = res.evm_env.block_env.clone();
710 self.evm_env_mut().cfg_env.chain_id = res.evm_env.cfg_env.chain_id;
712
713 let success =
714 self.is_raw_call_success(to, Cow::Borrowed(&res.state_changeset), &res, false);
715 if !success {
716 return Err(res.into_execution_error("execution error".to_string()).into());
717 }
718
719 Ok(res)
720 }
721
722 pub fn call(
724 &self,
725 from: Address,
726 to: Address,
727 func: &Function,
728 args: &[DynSolValue],
729 value: U256,
730 rd: Option<&RevertDecoder>,
731 ) -> Result<CallResult<DynSolValue, FEN>, EvmError<FEN>> {
732 let calldata = Bytes::from(func.abi_encode_input(args)?);
733 let result = self.call_raw(from, to, calldata, value)?;
734 result.into_decoded_result(func, rd)
735 }
736
737 pub fn call_sol<C: SolCall>(
739 &self,
740 from: Address,
741 to: Address,
742 args: &C,
743 value: U256,
744 rd: Option<&RevertDecoder>,
745 ) -> Result<CallResult<C::Return, FEN>, EvmError<FEN>> {
746 let calldata = Bytes::from(args.abi_encode());
747 let mut raw = self.call_raw(from, to, calldata, value)?;
748 raw = raw.into_result(rd)?;
749 Ok(CallResult { decoded_result: C::abi_decode_returns(&raw.result)?, raw })
750 }
751
752 pub fn transact(
754 &mut self,
755 from: Address,
756 to: Address,
757 func: &Function,
758 args: &[DynSolValue],
759 value: U256,
760 rd: Option<&RevertDecoder>,
761 ) -> Result<CallResult<DynSolValue, FEN>, EvmError<FEN>> {
762 let calldata = Bytes::from(func.abi_encode_input(args)?);
763 let result = self.transact_raw(from, to, calldata, value)?;
764 result.into_decoded_result(func, rd)
765 }
766
767 pub fn call_raw(
769 &self,
770 from: Address,
771 to: Address,
772 calldata: Bytes,
773 value: U256,
774 ) -> eyre::Result<RawCallResult<FEN>> {
775 let (evm_env, tx_env) = self.prepare_call_env(from, TxKind::Call(to), calldata, value);
776 self.call_with_env(evm_env, tx_env)
777 }
778
779 pub fn call_raw_with_authorization(
782 &mut self,
783 from: Address,
784 to: Address,
785 calldata: Bytes,
786 value: U256,
787 authorization_list: Vec<SignedAuthorization>,
788 ) -> eyre::Result<RawCallResult<FEN>> {
789 let (evm_env, mut tx_env) = self.prepare_call_env(from, to.into(), calldata, value);
790 tx_env.set_signed_authorization(authorization_list);
791 tx_env.set_tx_type(4);
792 self.call_with_env(evm_env, tx_env)
793 }
794
795 pub fn transact_raw(
797 &mut self,
798 from: Address,
799 to: Address,
800 calldata: Bytes,
801 value: U256,
802 ) -> eyre::Result<RawCallResult<FEN>> {
803 let (evm_env, tx_env) = self.prepare_call_env(from, TxKind::Call(to), calldata, value);
804 self.transact_with_env(evm_env, tx_env)
805 }
806
807 pub fn transact_raw_with_context(
809 &mut self,
810 from: Address,
811 to: Address,
812 calldata: Bytes,
813 value: U256,
814 chain_context: ChainFor<FEN>,
815 ) -> eyre::Result<RawCallResult<FEN>> {
816 let (evm_env, tx_env) = self.prepare_call_env(from, TxKind::Call(to), calldata, value);
817 self.transact_with_env_and_context(evm_env, tx_env, chain_context)
818 }
819
820 pub fn transact_raw_with_authorization(
823 &mut self,
824 from: Address,
825 to: Address,
826 calldata: Bytes,
827 value: U256,
828 authorization_list: Vec<SignedAuthorization>,
829 ) -> eyre::Result<RawCallResult<FEN>> {
830 let (evm_env, mut tx_env) = self.prepare_call_env(from, TxKind::Call(to), calldata, value);
831 tx_env.set_signed_authorization(authorization_list);
832 tx_env.set_tx_type(4);
833 self.transact_with_env(evm_env, tx_env)
834 }
835
836 pub fn apply_beacon_root(
839 &mut self,
840 parent_beacon_block_root: alloy_primitives::B256,
841 ) -> eyre::Result<()> {
842 let calldata = Bytes::copy_from_slice(parent_beacon_block_root.as_slice());
843 let mut evm_env = self.evm_env.clone();
844 let inspector = self.inspector().clone();
845 let mut state = {
846 let mut backend = CowBackend::new_borrowed(self.backend());
847 let mut evm = FEN::EvmFactory::default().create_foundry_evm_with_inspector(
848 &mut backend,
849 evm_env.clone(),
850 inspector,
851 );
852 *evm.chain_mut() = ChainFor::<FEN>::for_transaction(&TxEnvFor::<FEN>::default());
853 let result =
854 evm.transact_system_call(SYSTEM_ADDRESS, BEACON_ROOTS_ADDRESS, calldata)?;
855 evm_env = evm.finish().1;
856 result.state
857 };
858 state.retain(|address, _| *address == BEACON_ROOTS_ADDRESS);
859
860 self.backend_mut().commit(state);
861 self.inspector_mut().set_block(evm_env.block_env);
862
863 Ok(())
864 }
865
866 #[instrument(name = "call", level = "debug", skip_all)]
870 pub fn call_with_env(
871 &self,
872 evm_env: EvmEnvFor<FEN>,
873 tx_env: TxEnvFor<FEN>,
874 ) -> eyre::Result<RawCallResult<FEN>> {
875 let chain_context = self.chain_context_for_synthetic_transaction(&tx_env)?;
876 self.call_with_env_and_context(evm_env, tx_env, chain_context)
877 }
878
879 #[instrument(name = "call", level = "debug", skip_all)]
881 pub fn call_with_env_and_context(
882 &self,
883 mut evm_env: EvmEnvFor<FEN>,
884 mut tx_env: TxEnvFor<FEN>,
885 chain_context: ChainFor<FEN>,
886 ) -> eyre::Result<RawCallResult<FEN>> {
887 let mut stack = self.inspector().clone();
888 let sancov_edges = stack.inner.sancov_edges;
889 let sancov_trace_cmp = stack.inner.sancov_trace_cmp;
890 let sancov_active = sancov_edges || sancov_trace_cmp;
891 let mut backend = CowBackend::new_borrowed(self.backend());
892 let result = {
893 let _guard = sancov_active.then(|| SancovGuard::new(sancov_edges, sancov_trace_cmp));
894 backend.inspect_with_context(&mut evm_env, &mut tx_env, chain_context, &mut stack)?
895 };
896 let has_state_snapshot_failure = backend.has_state_snapshot_failure();
897 let fork_block_number = backend.active_fork_block_number();
898 let mut result = convert_executed_result(
899 evm_env,
900 tx_env,
901 stack,
902 result,
903 &backend,
904 has_state_snapshot_failure,
905 fork_block_number,
906 )?;
907 if sancov_edges {
908 SancovGuard::append_edges_into(&mut result);
909 }
910 if sancov_trace_cmp {
911 SancovGuard::drain_cmp_into(&mut result);
912 }
913 Ok(result)
914 }
915
916 #[instrument(name = "transact", level = "debug", skip_all)]
918 pub fn transact_with_env(
919 &mut self,
920 evm_env: EvmEnvFor<FEN>,
921 tx_env: TxEnvFor<FEN>,
922 ) -> eyre::Result<RawCallResult<FEN>> {
923 let chain_context = self.chain_context_for_synthetic_transaction(&tx_env)?;
924 self.transact_with_env_and_context(evm_env, tx_env, chain_context)
925 }
926
927 #[instrument(name = "transact", level = "debug", skip_all)]
929 pub fn transact_with_env_and_context(
930 &mut self,
931 mut evm_env: EvmEnvFor<FEN>,
932 mut tx_env: TxEnvFor<FEN>,
933 chain_context: ChainFor<FEN>,
934 ) -> eyre::Result<RawCallResult<FEN>> {
935 let mut stack = self.inspector().clone();
936 let sancov_edges = stack.inner.sancov_edges;
937 let sancov_trace_cmp = stack.inner.sancov_trace_cmp;
938 let sancov_active = sancov_edges || sancov_trace_cmp;
939 let backend = self.backend_mut();
940 let result = {
941 let _guard = sancov_active.then(|| SancovGuard::new(sancov_edges, sancov_trace_cmp));
942 backend.inspect_with_context(&mut evm_env, &mut tx_env, chain_context, &mut stack)?
943 };
944 let has_state_snapshot_failure = backend.has_state_snapshot_failure();
945 let fork_block_number = backend.active_fork_block_number();
946 let mut result = convert_executed_result(
947 evm_env,
948 tx_env,
949 stack,
950 result,
951 &*backend,
952 has_state_snapshot_failure,
953 fork_block_number,
954 )?;
955 if sancov_edges {
956 SancovGuard::append_edges_into(&mut result);
957 }
958 if sancov_trace_cmp {
959 SancovGuard::drain_cmp_into(&mut result);
960 }
961 self.commit(&mut result);
962 Ok(result)
963 }
964
965 #[instrument(name = "transact_block_replay", level = "debug", skip_all)]
967 pub fn transact_with_ordinary_block_replay(
968 &mut self,
969 mut evm_env: EvmEnvFor<FEN>,
970 target_tx_env: TxEnvFor<FEN>,
971 replay: Vec<(B256, TxEnvFor<FEN>)>,
972 ) -> eyre::Result<RawCallResult<FEN>> {
973 eyre::ensure!(
976 target_tx_env.tx_type() != 0x79,
977 "EIP-8130 target transactions are not supported by tooling replay"
978 );
979 for (hash, tx) in &replay {
980 eyre::ensure!(
981 tx.tx_type() != 0x79,
982 "EIP-8130 prefix transaction {hash} is not supported by tooling replay"
983 );
984 }
985 let block_number = evm_env.block_env.number();
986 let mut stack = self.inspector().clone();
987 let sancov_edges = stack.inner.sancov_edges;
988 let sancov_trace_cmp = stack.inner.sancov_trace_cmp;
989 let sancov_active = sancov_edges || sancov_trace_cmp;
990 let backend = self.backend_mut();
991
992 let (result, evm_env, tx_env) = {
993 let caller = target_tx_env.caller();
994 backend.set_caller(caller).set_spec_id(evm_env.cfg_env.spec);
995 let target_contract = match target_tx_env.kind() {
996 TxKind::Call(to) => to,
997 TxKind::Create => caller.create(target_tx_env.nonce()),
1000 };
1001 backend.set_test_contract(target_contract);
1002 let target_chain_context = ChainFor::<FEN>::for_transaction(&target_tx_env);
1003 if !replay.is_empty() {
1004 evm_env.cfg_env.disable_balance_check = true;
1005 }
1006 let mut evm = FEN::EvmFactory::default()
1007 .create_foundry_evm_with_inspector(backend, evm_env, &mut stack);
1008 *evm.chain_mut() = target_chain_context;
1009 evm.disable_inspector();
1010 for (tx_hash, tx_env) in replay {
1011 let created = match tx_env.kind() {
1012 TxKind::Create => Some(tx_env.caller().create(tx_env.nonce())),
1013 TxKind::Call(_) => None,
1014 };
1015 let result = evm.transact(tx_env).wrap_err_with(|| {
1016 format!("Failed to execute transaction: {tx_hash:?} in block {block_number}")
1017 })?;
1018 if result.result.is_success()
1019 && let Some(address) = created
1020 {
1021 evm.db_mut().add_persistent_account(address);
1022 }
1023 evm.db_mut().commit(result.state);
1024 }
1025
1026 evm.enable_inspector();
1027 let _guard = sancov_active.then(|| SancovGuard::new(sancov_edges, sancov_trace_cmp));
1028 let result = evm.transact(target_tx_env).wrap_err("EVM error")?;
1029 let tx_env = evm.tx().clone();
1030 let evm_env = evm.finish().1;
1031 (result, evm_env, tx_env)
1032 };
1033
1034 let has_state_snapshot_failure = backend.has_state_snapshot_failure();
1035 let fork_block_number = backend.active_fork_block_number();
1036 let mut result = convert_executed_result(
1037 evm_env,
1038 tx_env,
1039 stack,
1040 result,
1041 &*backend,
1042 has_state_snapshot_failure,
1043 fork_block_number,
1044 )?;
1045 if sancov_edges {
1046 SancovGuard::append_edges_into(&mut result);
1047 }
1048 if sancov_trace_cmp {
1049 SancovGuard::drain_cmp_into(&mut result);
1050 }
1051 self.commit(&mut result);
1052 Ok(result)
1053 }
1054
1055 #[instrument(name = "commit", level = "debug", skip_all)]
1060 fn commit(&mut self, result: &mut RawCallResult<FEN>) {
1061 self.backend_mut().commit(result.state_changeset.clone());
1063
1064 self.inspector_mut().cheatcodes = result.cheatcodes.take();
1066 if let Some(cheats) = self.inspector_mut().cheatcodes.as_mut() {
1067 cheats.broadcastable_transactions.clear();
1069 cheats.ignored_traces.ignored.clear();
1070 if let Some(last_pause_call) = cheats.ignored_traces.last_pause_call.as_mut() {
1073 *last_pause_call = (0, 0);
1074 }
1075 }
1076
1077 self.inspector_mut().set_block(result.evm_env.block_env.clone());
1079 self.inspector_mut().set_gas_price(result.tx_env.gas_price());
1080 }
1081
1082 pub fn is_raw_call_mut_success(
1087 &self,
1088 address: Address,
1089 call_result: &mut RawCallResult<FEN>,
1090 should_fail: bool,
1091 ) -> bool {
1092 self.is_raw_call_success(
1093 address,
1094 Cow::Owned(std::mem::take(&mut call_result.state_changeset)),
1095 call_result,
1096 should_fail,
1097 )
1098 }
1099
1100 pub fn is_raw_call_success(
1104 &self,
1105 address: Address,
1106 state_changeset: Cow<'_, StateChangeset>,
1107 call_result: &RawCallResult<FEN>,
1108 should_fail: bool,
1109 ) -> bool {
1110 if call_result.has_state_snapshot_failure {
1111 return should_fail;
1113 }
1114 self.is_success(address, call_result.reverted, state_changeset, should_fail)
1115 }
1116
1117 pub fn is_raw_call_mut_success_handler_gate(
1121 &self,
1122 address: Address,
1123 call_result: &mut RawCallResult<FEN>,
1124 ) -> bool {
1125 if call_result.has_state_snapshot_failure {
1126 return false;
1127 }
1128 let state_changeset = std::mem::take(&mut call_result.state_changeset);
1129 self.is_success_handler_gate(address, call_result.reverted, Cow::Owned(state_changeset))
1130 }
1131
1132 pub fn is_success(
1154 &self,
1155 address: Address,
1156 reverted: bool,
1157 state_changeset: Cow<'_, StateChangeset>,
1158 should_fail: bool,
1159 ) -> bool {
1160 let success = self.is_success_raw(address, reverted, state_changeset, false);
1161 should_fail ^ success
1162 }
1163
1164 pub fn is_success_handler_gate(
1170 &self,
1171 address: Address,
1172 reverted: bool,
1173 state_changeset: Cow<'_, StateChangeset>,
1174 ) -> bool {
1175 self.is_success_raw(address, reverted, state_changeset, true)
1176 }
1177
1178 #[instrument(name = "is_success", level = "debug", skip_all)]
1179 fn is_success_raw(
1180 &self,
1181 address: Address,
1182 reverted: bool,
1183 state_changeset: Cow<'_, StateChangeset>,
1184 pending_global_failure_only: bool,
1185 ) -> bool {
1186 if reverted {
1188 return false;
1189 }
1190
1191 if self.backend().has_state_snapshot_failure() {
1193 return false;
1194 }
1195
1196 let global_failed = if pending_global_failure_only {
1201 Self::has_pending_global_failure(&state_changeset)
1202 } else {
1203 self.has_global_failure(&state_changeset)
1204 };
1205 if global_failed {
1206 return false;
1207 }
1208
1209 if !self.legacy_assertions {
1210 return true;
1211 }
1212
1213 {
1215 let mut backend = self.backend().clone_empty();
1217
1218 for address in [address, CHEATCODE_ADDRESS] {
1221 let Ok(acc) = self.backend().basic_ref(address) else { return false };
1222 backend.insert_account_info(address, acc.unwrap_or_default());
1223 }
1224
1225 backend.commit(state_changeset.into_owned());
1230
1231 let executor = self.clone_with_backend(backend);
1233 let call = executor.call_sol(CALLER, address, &ITest::failedCall {}, U256::ZERO, None);
1234 match call {
1235 Ok(CallResult { raw: _, decoded_result: failed }) => {
1236 trace!(failed, "DSTest::failed()");
1237 !failed
1238 }
1239 Err(err) => {
1240 trace!(%err, "failed to call DSTest::failed()");
1241 true
1242 }
1243 }
1244 }
1245 }
1246
1247 pub fn has_pending_global_failure(state_changeset: &StateChangeset) -> bool {
1250 if let Some(acc) = state_changeset.get(&CHEATCODE_ADDRESS)
1251 && let Some(failed_slot) = acc.storage.get(&GLOBAL_FAIL_SLOT)
1252 && !failed_slot.present_value().is_zero()
1253 {
1254 return true;
1255 }
1256
1257 false
1258 }
1259
1260 pub fn has_global_failure(&self, state_changeset: &StateChangeset) -> bool {
1263 if Self::has_pending_global_failure(state_changeset) {
1264 return true;
1265 }
1266
1267 self.backend()
1268 .storage_ref(CHEATCODE_ADDRESS, GLOBAL_FAIL_SLOT)
1269 .is_ok_and(|failed_slot| !failed_slot.is_zero())
1270 }
1271
1272 pub fn prepare_call_env(
1277 &self,
1278 caller: Address,
1279 kind: TxKind,
1280 data: Bytes,
1281 value: U256,
1282 ) -> (EvmEnvFor<FEN>, TxEnvFor<FEN>) {
1283 let mut cfg_env = self.evm_env.cfg_env.clone();
1284 cfg_env.spec = self.spec_id();
1285
1286 let mut block_env = self.evm_env.block_env.clone();
1290 block_env.set_basefee(0);
1291 block_env.set_gas_limit(self.gas_limit);
1292
1293 let mut tx_env = self.tx_env.clone();
1294 tx_env.set_caller(caller);
1295 tx_env.set_kind(kind);
1296 tx_env.set_data(data);
1297 tx_env.set_value(value);
1298 tx_env.set_gas_price(0);
1300 tx_env.set_gas_priority_fee(None);
1301 tx_env.set_gas_limit(self.gas_limit);
1302 tx_env.set_chain_id(Some(self.evm_env.cfg_env.chain_id));
1303
1304 (EvmEnv { cfg_env, block_env }, tx_env)
1305 }
1306
1307 pub fn call_sol_default<C: SolCall>(&self, to: Address, args: &C) -> C::Return
1308 where
1309 C::Return: Default,
1310 {
1311 self.call_sol(CALLER, to, args, U256::ZERO, None)
1312 .map(|c| c.decoded_result)
1313 .inspect_err(|e| warn!(target: "forge::test", "failed calling {:?}: {e}", C::SIGNATURE))
1314 .unwrap_or_default()
1315 }
1316}
1317
1318#[derive(Debug, thiserror::Error)]
1320#[error("execution reverted: {reason} (gas: {})", raw.gas_used)]
1321pub struct ExecutionErr<FEN: FoundryEvmNetwork = EthEvmNetwork> {
1322 pub raw: RawCallResult<FEN>,
1324 pub reason: String,
1326}
1327
1328impl<FEN: FoundryEvmNetwork> std::ops::Deref for ExecutionErr<FEN> {
1329 type Target = RawCallResult<FEN>;
1330
1331 #[inline]
1332 fn deref(&self) -> &Self::Target {
1333 &self.raw
1334 }
1335}
1336
1337impl<FEN: FoundryEvmNetwork> std::ops::DerefMut for ExecutionErr<FEN> {
1338 #[inline]
1339 fn deref_mut(&mut self) -> &mut Self::Target {
1340 &mut self.raw
1341 }
1342}
1343
1344#[derive(Debug, thiserror::Error)]
1345pub enum EvmError<FEN: FoundryEvmNetwork = EthEvmNetwork> {
1346 #[error(transparent)]
1348 Execution(Box<ExecutionErr<FEN>>),
1349 #[error(transparent)]
1351 Abi(#[from] alloy_dyn_abi::Error),
1352 #[error("{0}")]
1354 Skip(SkipReason),
1355 #[error("{0}")]
1357 Eyre(
1358 #[from]
1359 #[source]
1360 eyre::Report,
1361 ),
1362}
1363
1364impl<FEN: FoundryEvmNetwork> From<ExecutionErr<FEN>> for EvmError<FEN> {
1365 fn from(err: ExecutionErr<FEN>) -> Self {
1366 Self::Execution(Box::new(err))
1367 }
1368}
1369
1370impl<FEN: FoundryEvmNetwork> From<alloy_sol_types::Error> for EvmError<FEN> {
1371 fn from(err: alloy_sol_types::Error) -> Self {
1372 Self::Abi(err.into())
1373 }
1374}
1375
1376#[derive(Debug)]
1378pub struct DeployResult<FEN: FoundryEvmNetwork = EthEvmNetwork> {
1379 pub raw: RawCallResult<FEN>,
1381 pub address: Address,
1383}
1384
1385impl<FEN: FoundryEvmNetwork> std::ops::Deref for DeployResult<FEN> {
1386 type Target = RawCallResult<FEN>;
1387
1388 #[inline]
1389 fn deref(&self) -> &Self::Target {
1390 &self.raw
1391 }
1392}
1393
1394impl<FEN: FoundryEvmNetwork> std::ops::DerefMut for DeployResult<FEN> {
1395 #[inline]
1396 fn deref_mut(&mut self) -> &mut Self::Target {
1397 &mut self.raw
1398 }
1399}
1400
1401impl<FEN: FoundryEvmNetwork> From<DeployResult<FEN>> for RawCallResult<FEN> {
1402 fn from(d: DeployResult<FEN>) -> Self {
1403 d.raw
1404 }
1405}
1406
1407#[derive(Debug)]
1409pub struct RawCallResult<FEN: FoundryEvmNetwork = EthEvmNetwork> {
1410 pub exit_reason: Option<InstructionResult>,
1412 pub execution_cancelled: bool,
1414 pub reverted: bool,
1416 pub has_state_snapshot_failure: bool,
1421 pub result: Bytes,
1423 pub gas_used: u64,
1425 pub gas_refunded: u64,
1427 pub stipend: u64,
1429 pub logs: Vec<Log>,
1431 pub labels: AddressHashMap<String>,
1433 pub traces: Option<SparsedTraceArena>,
1435 pub debug_bytecodes: AddressHashMap<Bytes>,
1437 pub line_coverage: Option<HitMaps>,
1439 pub edge_coverage: Option<EdgeCoverage>,
1441 pub evm_cmp_values: Option<Vec<CmpOperands>>,
1443 pub observed_calls: Vec<ObservedCall>,
1445 pub sancov_coverage: Option<Vec<u8>>,
1448 pub sancov_cmp_values: Option<Vec<foundry_evm_sancov::CmpSample>>,
1450 pub transactions: Option<BroadcastableTransactions<FEN::Network>>,
1452 pub state_changeset: StateChangeset,
1454 pub evm_env: EvmEnvFor<FEN>,
1456 pub tx_env: TxEnvFor<FEN>,
1458 pub cheatcodes: Option<Box<Cheatcodes<FEN>>>,
1460 pub out: Option<Output>,
1462 pub fork_block_number: Option<u64>,
1464 pub chisel_state: Option<(Vec<U256>, Vec<u8>)>,
1466 pub reverter: Option<Address>,
1467 pub skip_payloads: Vec<Bytes>,
1472}
1473
1474impl<FEN: FoundryEvmNetwork> Default for RawCallResult<FEN> {
1475 fn default() -> Self {
1476 Self {
1477 exit_reason: None,
1478 execution_cancelled: false,
1479 reverted: false,
1480 has_state_snapshot_failure: false,
1481 result: Bytes::new(),
1482 gas_used: 0,
1483 gas_refunded: 0,
1484 stipend: 0,
1485 logs: Vec::new(),
1486 labels: HashMap::default(),
1487 traces: None,
1488 debug_bytecodes: HashMap::default(),
1489 line_coverage: None,
1490 edge_coverage: None,
1491 evm_cmp_values: None,
1492 observed_calls: Vec::new(),
1493 sancov_coverage: None,
1494 sancov_cmp_values: None,
1495 transactions: None,
1496 state_changeset: HashMap::default(),
1497 evm_env: EvmEnv::default(),
1498 tx_env: TxEnvFor::<FEN>::default(),
1499 cheatcodes: Default::default(),
1500 out: None,
1501 fork_block_number: None,
1502 chisel_state: None,
1503 reverter: None,
1504 skip_payloads: Vec::new(),
1505 }
1506 }
1507}
1508
1509impl<FEN: FoundryEvmNetwork> RawCallResult<FEN> {
1510 pub fn from_evm_result(r: Result<Self, EvmError<FEN>>) -> eyre::Result<(Self, Option<String>)> {
1512 match r {
1513 Ok(r) => Ok((r, None)),
1514 Err(EvmError::Execution(e)) => Ok((e.raw, Some(e.reason))),
1515 Err(e) => Err(e.into()),
1516 }
1517 }
1518
1519 pub fn skip_reason(&self) -> Option<SkipReason> {
1524 if !self.reverted || !self.skip_payloads.contains(&self.result) {
1525 return None;
1526 }
1527 SkipReason::decode(&self.result)
1528 }
1529
1530 pub fn into_evm_error(self, rd: Option<&RevertDecoder>) -> EvmError<FEN> {
1532 if let Some(reason) = self.skip_reason() {
1533 return EvmError::Skip(reason);
1534 }
1535 let reason = rd.unwrap_or_default().decode(&self.result, self.exit_reason);
1536 EvmError::Execution(Box::new(self.into_execution_error(reason)))
1537 }
1538
1539 pub const fn into_execution_error(self, reason: String) -> ExecutionErr<FEN> {
1541 ExecutionErr { raw: self, reason }
1542 }
1543
1544 pub fn into_result(self, rd: Option<&RevertDecoder>) -> Result<Self, EvmError<FEN>> {
1546 if let Some(reason) = self.exit_reason
1547 && reason.is_ok()
1548 {
1549 Ok(self)
1550 } else {
1551 Err(self.into_evm_error(rd))
1552 }
1553 }
1554
1555 pub fn into_decoded_result(
1557 mut self,
1558 func: &Function,
1559 rd: Option<&RevertDecoder>,
1560 ) -> Result<CallResult<DynSolValue, FEN>, EvmError<FEN>> {
1561 self = self.into_result(rd)?;
1562 let mut result = func.abi_decode_output(&self.result)?;
1563 let decoded_result =
1564 if result.len() == 1 { result.pop().unwrap() } else { DynSolValue::Tuple(result) };
1565 Ok(CallResult { raw: self, decoded_result })
1566 }
1567
1568 pub fn transactions(&self) -> Option<&BroadcastableTransactions<FEN::Network>> {
1570 self.cheatcodes.as_ref().map(|c| &c.broadcastable_transactions)
1571 }
1572
1573 pub fn merge_edge_coverage(
1575 &mut self,
1576 history_map: &mut Vec<u8>,
1577 edge_indices: &mut EdgeIndexMap,
1578 ) -> (bool, bool) {
1579 let mut new_coverage = false;
1580 let mut is_edge = false;
1581 if let Some(x) = &mut self.edge_coverage {
1582 match x {
1583 EdgeCoverage::Hash(x) => {
1584 if history_map.len() < x.len() {
1585 history_map.resize(x.len(), 0);
1586 }
1587 for (curr, hist) in std::iter::zip(x.iter_mut(), history_map.iter_mut()) {
1590 Self::merge_edge_count(*curr, hist, &mut new_coverage, &mut is_edge);
1591
1592 *curr = 0;
1594 }
1595 }
1596 EdgeCoverage::CollisionFree(hits) => {
1597 for hit in hits.drain(..) {
1598 let edge_index = edge_indices.edge_index(hit.edge);
1599 if history_map.len() <= edge_index {
1600 history_map.resize(edge_index + 1, 0);
1601 }
1602 Self::merge_edge_count(
1603 hit.count,
1604 &mut history_map[edge_index],
1605 &mut new_coverage,
1606 &mut is_edge,
1607 );
1608 }
1609 }
1610 }
1611 }
1612 (new_coverage, is_edge)
1613 }
1614
1615 const fn merge_edge_count(
1616 curr: u8,
1617 hist: &mut u8,
1618 new_coverage: &mut bool,
1619 is_edge: &mut bool,
1620 ) {
1621 let Some(bucket) = Self::bin_count(curr) else {
1622 return;
1623 };
1624
1625 if *hist < bucket {
1627 if *hist == 0 {
1628 *is_edge = true;
1630 }
1631 *hist = bucket;
1632 *new_coverage = true;
1633 }
1634 }
1635
1636 const fn bin_count(count: u8) -> Option<u8> {
1639 match count {
1640 0 => None,
1641 1 => Some(1),
1642 2 => Some(2),
1643 3 => Some(4),
1644 4..=7 => Some(8),
1645 8..=15 => Some(16),
1646 16..=31 => Some(32),
1647 32..=127 => Some(64),
1648 128..=255 => Some(128),
1649 }
1650 }
1651
1652 pub fn merge_sancov_coverage(&mut self, history_map: &mut Vec<u8>) -> (bool, bool) {
1655 let mut new_coverage = false;
1656 let mut is_edge = false;
1657 if let Some(x) = &mut self.sancov_coverage {
1658 if history_map.len() < x.len() {
1659 history_map.resize(x.len(), 0);
1660 }
1661 for (curr, hist) in std::iter::zip(x.iter_mut(), history_map.iter_mut()) {
1662 if *curr > 0 {
1663 if let Some(bucket) = Self::bin_count(*curr)
1664 && *hist < bucket
1665 {
1666 if *hist == 0 {
1667 is_edge = true;
1668 }
1669 *hist = bucket;
1670 new_coverage = true;
1671 }
1672 *curr = 0;
1673 }
1674 }
1675 }
1676 (new_coverage, is_edge)
1677 }
1678
1679 pub fn merge_all_coverage(
1682 &mut self,
1683 evm_history: &mut Vec<u8>,
1684 evm_edge_indices: &mut EdgeIndexMap,
1685 sancov_history: &mut Vec<u8>,
1686 ) -> (bool, bool) {
1687 let (new_evm, edge_evm) = self.merge_edge_coverage(evm_history, evm_edge_indices);
1688 let (new_san, edge_san) = self.merge_sancov_coverage(sancov_history);
1689 (new_evm || new_san, edge_evm || edge_san)
1690 }
1691}
1692
1693pub struct CallResult<T = DynSolValue, FEN: FoundryEvmNetwork = EthEvmNetwork> {
1695 pub raw: RawCallResult<FEN>,
1697 pub decoded_result: T,
1699}
1700
1701impl<T, FEN: FoundryEvmNetwork> std::ops::Deref for CallResult<T, FEN> {
1702 type Target = RawCallResult<FEN>;
1703
1704 #[inline]
1705 fn deref(&self) -> &Self::Target {
1706 &self.raw
1707 }
1708}
1709
1710impl<T, FEN: FoundryEvmNetwork> std::ops::DerefMut for CallResult<T, FEN> {
1711 #[inline]
1712 fn deref_mut(&mut self) -> &mut Self::Target {
1713 &mut self.raw
1714 }
1715}
1716
1717pub(crate) fn calculate_stipend(tx_env: &impl Transaction, cfg: &impl Cfg) -> u64 {
1718 let eip2780 = cfg.is_amsterdam_eip2780_enabled().then(|| Eip2780TxInfo {
1719 value: tx_env.value(),
1720 is_self_transfer: matches!(tx_env.kind(), TxKind::Call(to) if to == tx_env.caller()),
1721 });
1722 revm::interpreter::gas::calculate_initial_tx_gas_for_tx(tx_env, cfg.spec().into(), eip2780)
1723 .initial_total_gas()
1724}
1725
1726fn convert_executed_result<FEN: FoundryEvmNetwork, H: IntoInstructionResult>(
1728 evm_env: EvmEnvFor<FEN>,
1729 tx_env: TxEnvFor<FEN>,
1730 mut inspector: InspectorStack<FEN>,
1731 ResultAndState { result, state: state_changeset }: ResultAndState<H>,
1732 db: &dyn DatabaseRef<Error = DatabaseError>,
1733 has_state_snapshot_failure: bool,
1734 fork_block_number: Option<u64>,
1735) -> eyre::Result<RawCallResult<FEN>> {
1736 let execution_cancelled = inspector.execution_cancelled();
1737 let (exit_reason, gas_refunded, gas_used, out, exec_logs) = match result {
1738 ExecutionResult::Success { reason, gas, output, logs } => {
1739 (reason.into(), gas.final_refunded(), gas.tx_gas_used(), Some(output), logs)
1740 }
1741 ExecutionResult::Revert { gas, output, logs } => {
1742 (InstructionResult::Revert, 0_u64, gas.tx_gas_used(), Some(Output::Call(output)), logs)
1743 }
1744 ExecutionResult::Halt { reason, gas, logs } => {
1745 (reason.into_instruction_result(), 0_u64, gas.tx_gas_used(), None, logs)
1746 }
1747 };
1748 let stipend = calculate_stipend(&tx_env, &evm_env.cfg_env);
1749
1750 let result = match &out {
1751 Some(Output::Call(data)) => data.clone(),
1752 _ => Bytes::new(),
1753 };
1754 let observed_calls = inspector
1755 .inner
1756 .fuzzer
1757 .as_mut()
1758 .map(|fuzzer| fuzzer.take_observed_calls())
1759 .unwrap_or_default();
1760
1761 let InspectorData {
1762 mut logs,
1763 labels,
1764 traces,
1765 line_coverage,
1766 edge_coverage,
1767 evm_cmp_values,
1768 mut cheatcodes,
1769 chisel_state,
1770 reverter,
1771 } = inspector.collect();
1772 let fork_block_number = cheatcodes
1773 .as_ref()
1774 .and_then(|cheats| cheats.fork_block_number_override)
1775 .or(fork_block_number);
1776 let debug_bytecodes = collect_debug_bytecodes(traces.as_ref(), db);
1777
1778 if logs.is_empty() {
1779 logs = exec_logs;
1780 }
1781
1782 let transactions = cheatcodes
1783 .as_ref()
1784 .map(|c| c.broadcastable_transactions.clone())
1785 .filter(|txs| !txs.is_empty());
1786 let skip_payloads =
1787 cheatcodes.as_mut().map(|c| std::mem::take(&mut c.skip_payloads)).unwrap_or_default();
1788
1789 Ok(RawCallResult {
1790 exit_reason: Some(exit_reason),
1791 execution_cancelled,
1792 reverted: !matches!(exit_reason, return_ok!()),
1793 has_state_snapshot_failure,
1794 result,
1795 gas_used,
1796 gas_refunded,
1797 stipend,
1798 logs,
1799 labels,
1800 traces,
1801 debug_bytecodes,
1802 line_coverage,
1803 edge_coverage,
1804 evm_cmp_values,
1805 observed_calls,
1806 sancov_coverage: None,
1807 sancov_cmp_values: None,
1808 transactions,
1809 state_changeset,
1810 evm_env,
1811 tx_env,
1812 cheatcodes,
1813 out,
1814 fork_block_number,
1815 chisel_state,
1816 reverter,
1817 skip_payloads,
1818 })
1819}
1820
1821fn collect_debug_bytecodes(
1822 traces: Option<&SparsedTraceArena>,
1823 db: &dyn DatabaseRef<Error = DatabaseError>,
1824) -> AddressHashMap<Bytes> {
1825 let mut bytecodes = HashMap::default();
1826 let Some(traces) = traces else { return bytecodes };
1827
1828 for node in traces.arena.nodes() {
1829 let address = node.trace.address;
1830 if bytecodes.contains_key(&address) {
1831 continue;
1832 }
1833
1834 let Ok(Some(account)) = db.basic_ref(address) else { continue };
1835 let code: Option<Bytecode> =
1836 account.code.or_else(|| db.code_by_hash_ref(account.code_hash).ok());
1837 let code: Bytes = code.map(|code| code.original_bytes()).unwrap_or_default();
1838
1839 if !code.is_empty() {
1840 bytecodes.insert(address, code);
1841 }
1842 }
1843
1844 bytecodes
1845}
1846
1847pub struct FuzzTestTimer {
1849 inner: Option<(Instant, Duration)>,
1851}
1852
1853impl FuzzTestTimer {
1854 pub fn new(timeout: Option<u32>) -> Self {
1855 Self { inner: timeout.map(|timeout| (Instant::now(), Duration::from_secs(timeout.into()))) }
1856 }
1857
1858 pub const fn is_enabled(&self) -> bool {
1860 self.inner.is_some()
1861 }
1862
1863 pub fn is_timed_out(&self) -> bool {
1865 self.inner.is_some_and(|(start, duration)| start.elapsed() > duration)
1866 }
1867}
1868
1869#[derive(Clone, Debug)]
1872pub struct EarlyExit {
1873 inner: Arc<AtomicBool>,
1875 fail_fast: bool,
1877}
1878
1879impl EarlyExit {
1880 pub fn new(fail_fast: bool) -> Self {
1881 Self { inner: Arc::new(AtomicBool::new(false)), fail_fast }
1882 }
1883
1884 pub fn record_failure(&self) {
1886 if self.fail_fast {
1887 self.inner.store(true, Ordering::Relaxed);
1888 }
1889 }
1890
1891 pub fn record_ctrl_c(&self) {
1893 self.inner.store(true, Ordering::Relaxed);
1894 }
1895
1896 pub fn should_stop(&self) -> bool {
1898 self.inner.load(Ordering::Relaxed)
1899 }
1900}
1901
1902#[derive(Clone, Debug)]
1904pub(crate) enum EvmExecutionCancellation {
1905 EarlyExit(EarlyExit),
1907 Campaign { early_exit: EarlyExit, stop: Arc<AtomicBool>, deadline: Option<Instant> },
1909}
1910
1911impl EvmExecutionCancellation {
1912 pub(crate) const fn early_exit(early_exit: EarlyExit) -> Self {
1913 Self::EarlyExit(early_exit)
1914 }
1915
1916 pub(crate) const fn campaign(
1917 early_exit: EarlyExit,
1918 stop: Arc<AtomicBool>,
1919 deadline: Option<Instant>,
1920 ) -> Self {
1921 Self::Campaign { early_exit, stop, deadline }
1922 }
1923
1924 pub(crate) fn should_stop(&self, poll_deadline: bool) -> bool {
1926 match self {
1927 Self::EarlyExit(early_exit) => early_exit.should_stop(),
1928 Self::Campaign { early_exit, stop, deadline } => {
1929 if early_exit.should_stop() || stop.load(Ordering::Relaxed) {
1930 return true;
1931 }
1932 if poll_deadline && deadline.is_some_and(|deadline| Instant::now() > deadline) {
1933 stop.store(true, Ordering::Relaxed);
1934 return true;
1935 }
1936 false
1937 }
1938 }
1939 }
1940
1941 pub(crate) fn request_stop(&self) {
1942 if let Self::Campaign { stop, .. } = self {
1943 stop.store(true, Ordering::Relaxed);
1944 }
1945 }
1946
1947 pub(crate) const fn early_exit_ref(&self) -> &EarlyExit {
1948 match self {
1949 Self::EarlyExit(early_exit) | Self::Campaign { early_exit, .. } => early_exit,
1950 }
1951 }
1952}
1953
1954#[inline]
1956pub fn should_ignore_revert(
1957 fail_on_revert: bool,
1958 target: Address,
1959 reverter: Option<Address>,
1960 extra_cheatcode_addresses: &[Address],
1961) -> bool {
1962 !fail_on_revert
1963 && reverter.is_some_and(|reverter| {
1964 reverter != target
1965 && reverter != CHEATCODE_ADDRESS
1966 && !extra_cheatcode_addresses.contains(&reverter)
1967 })
1968}
1969
1970#[cfg(test)]
1971mod tests {
1972 use super::*;
1973 use crate::inspectors::{EdgeCovHit, EdgeKey};
1974 use foundry_cheatcodes::{
1975 CheatsConfig,
1976 Vm::{blobhashesCall, mockCallRevert_1Call, revertToStateCall, snapshotStateCall},
1977 };
1978 use foundry_config::Config;
1979 use foundry_evm_core::{constants::MAGIC_SKIP, evm::TempoEvmNetwork, opts::EvmOpts};
1980 use foundry_evm_traces::InternalTraceMode;
1981 use revm::context::{CfgEnv, TxEnv};
1982 use std::{sync::mpsc, thread};
1983
1984 #[cfg(feature = "base")]
1985 use foundry_evm_core::evm::BaseEvmNetwork;
1986
1987 #[cfg(feature = "monad")]
1988 use foundry_evm_core::constants::MONAD_CHEATCODE_ADDRESS;
1989
1990 fn dense_call(edge: EdgeKey) -> RawCallResult {
1991 RawCallResult {
1992 edge_coverage: Some(EdgeCoverage::CollisionFree(vec![EdgeCovHit { edge, count: 1 }])),
1993 ..Default::default()
1994 }
1995 }
1996
1997 #[test]
1998 fn nested_revert_is_ignored_only_when_allowed() {
1999 let target = Address::from([0x11; 20]);
2000 let nested = Address::from([0x22; 20]);
2001
2002 assert!(should_ignore_revert(false, target, Some(nested), &[]));
2003 assert!(!should_ignore_revert(true, target, Some(nested), &[]));
2004 assert!(!should_ignore_revert(false, target, Some(target), &[]));
2005 assert!(!should_ignore_revert(false, target, Some(CHEATCODE_ADDRESS), &[]));
2006 assert!(!should_ignore_revert(false, target, None, &[]));
2007 }
2008
2009 #[cfg(feature = "monad")]
2010 #[test]
2011 fn network_cheatcode_revert_handling_is_monad_specific() {
2012 let target = Address::from([0x11; 20]);
2013
2014 assert!(should_ignore_revert(false, target, Some(MONAD_CHEATCODE_ADDRESS), &[]));
2015 assert!(!should_ignore_revert(
2016 false,
2017 target,
2018 Some(MONAD_CHEATCODE_ADDRESS),
2019 &[MONAD_CHEATCODE_ADDRESS],
2020 ));
2021 }
2022
2023 #[cfg(feature = "monad")]
2024 #[test]
2025 fn executor_tooling_follows_concrete_builder() {
2026 let ethereum = ExecutorBuilder::<EthEvmNetwork>::new().build(
2027 EvmEnvFor::<EthEvmNetwork>::default(),
2028 TxEnvFor::<EthEvmNetwork>::default(),
2029 Backend::spawn(None).unwrap(),
2030 NetworkConfigs::with_monad(),
2031 );
2032 assert!(ethereum.backend().networks().is_monad());
2033 assert!(!ethereum.backend().is_persistent(&MONAD_CHEATCODE_ADDRESS));
2034
2035 let monad = ExecutorBuilder::<MonadEvmNetwork>::new().build(
2036 EvmEnvFor::<MonadEvmNetwork>::default(),
2037 TxEnvFor::<MonadEvmNetwork>::default(),
2038 Backend::spawn(None).unwrap(),
2039 NetworkConfigs::with_monad(),
2040 );
2041 assert!(monad.inspector().networks.is_monad());
2042 assert!(monad.backend().networks().is_monad());
2043 assert!(monad.backend().is_persistent(&MONAD_CHEATCODE_ADDRESS));
2044 }
2045
2046 #[test]
2047 fn tempo_labels_follow_concrete_builder() {
2048 let ethereum = ExecutorBuilder::<EthEvmNetwork>::new().build(
2049 EvmEnvFor::<EthEvmNetwork>::default(),
2050 TxEnvFor::<EthEvmNetwork>::default(),
2051 Backend::spawn(None).unwrap(),
2052 NetworkConfigs::with_tempo(),
2053 );
2054 assert!(ethereum.inspector().tempo_labels.is_none());
2055
2056 let tempo = ExecutorBuilder::<TempoEvmNetwork>::new().build(
2057 EvmEnvFor::<TempoEvmNetwork>::default(),
2058 TxEnvFor::<TempoEvmNetwork>::default(),
2059 Backend::spawn(None).unwrap(),
2060 NetworkConfigs::default(),
2061 );
2062 assert!(tempo.inspector().tempo_labels.is_some());
2063 }
2064
2065 #[test]
2066 fn collision_free_edge_merge_uses_stable_indices() {
2067 let first =
2068 EdgeKey { address: Address::ZERO, depth: None, pc: 0, jump_dest: U256::from(10) };
2069 let second =
2070 EdgeKey { address: Address::ZERO, depth: None, pc: 0, jump_dest: U256::from(20) };
2071 let mut history = Vec::new();
2072 let mut edge_indices = EdgeIndexMap::default();
2073
2074 assert_eq!(
2075 dense_call(first).merge_edge_coverage(&mut history, &mut edge_indices),
2076 (true, true)
2077 );
2078 assert_eq!(history, [1]);
2079
2080 assert_eq!(
2081 dense_call(second).merge_edge_coverage(&mut history, &mut edge_indices),
2082 (true, true)
2083 );
2084 assert_eq!(history, [1, 1]);
2085
2086 assert_eq!(
2087 dense_call(first).merge_edge_coverage(&mut history, &mut edge_indices),
2088 (false, false)
2089 );
2090 assert_eq!(history, [1, 1]);
2091 }
2092
2093 #[test]
2094 fn collision_free_edge_merge_handles_sparse_observation_indices() {
2095 let first =
2096 EdgeKey { address: Address::ZERO, depth: None, pc: 0, jump_dest: U256::from(10) };
2097 let second =
2098 EdgeKey { address: Address::ZERO, depth: None, pc: 0, jump_dest: U256::from(20) };
2099 let mut edge_indices = EdgeIndexMap::default();
2100 edge_indices.edge_index(first);
2101 edge_indices.edge_index(second);
2102 let mut history = Vec::new();
2103
2104 assert_eq!(
2105 dense_call(second).merge_edge_coverage(&mut history, &mut edge_indices),
2106 (true, true)
2107 );
2108 assert_eq!(history, [0, 1]);
2109 }
2110
2111 #[test]
2112 fn cheatcode_skip_payload_is_classified_as_skip() {
2113 let raw = RawCallResult::<EthEvmNetwork> {
2114 reverted: true,
2115 result: Bytes::from_static(b"FOUNDRY::SKIPwith reason"),
2116 skip_payloads: vec![Bytes::from_static(b"FOUNDRY::SKIPwith reason")],
2117 ..Default::default()
2118 };
2119
2120 let err = raw.into_evm_error(None);
2121 assert!(matches!(err, EvmError::Skip(_)));
2122 }
2123
2124 #[test]
2125 fn forged_skip_payload_is_execution_error() {
2126 let raw = RawCallResult::<EthEvmNetwork> {
2127 reverted: true,
2128 result: Bytes::from_static(MAGIC_SKIP),
2129 reverter: Some(CHEATCODE_ADDRESS),
2130 ..Default::default()
2131 };
2132
2133 let err = raw.into_evm_error(None);
2134 assert!(matches!(err, EvmError::Execution(_)));
2135 }
2136
2137 #[cfg(feature = "base")]
2138 #[test]
2139 fn base_block_replay_rejects_eip8130_before_execution() {
2140 for target_is_eip8130 in [true, false] {
2141 let mut executor = ExecutorBuilder::<BaseEvmNetwork>::new().build(
2142 EvmEnvFor::<BaseEvmNetwork>::default(),
2143 TxEnvFor::<BaseEvmNetwork>::default(),
2144 Backend::spawn(None).unwrap(),
2145 NetworkConfigs::with_base(),
2146 );
2147 let unsupported = TxEnvFor::<BaseEvmNetwork> {
2148 eip8130: Some(
2149 serde_json::from_value(serde_json::json!({
2150 "signed": {
2151 "tx": {
2152 "chainId": 8453, "sender": null, "nonceKey": "0x0",
2153 "nonceSequence": 0, "validAfter": 0, "validBefore": 0,
2154 "maxPriorityFeePerGas": "0x0", "maxFeePerGas": "0x0",
2155 "gasLimit": 100000, "accountChanges": [], "calls": [],
2156 "metadata": "0x", "payer": null
2157 },
2158 "senderAuth": "0x", "payerAuth": "0x"
2159 },
2160 "mode": "Verified", "simulation_sender_actor_id": null
2161 }))
2162 .unwrap(),
2163 ),
2164 ..Default::default()
2165 };
2166 let ordinary = TxEnvFor::<BaseEvmNetwork>::default();
2167 let hash = B256::repeat_byte(1);
2168 let (target, prefix, expected) = if target_is_eip8130 {
2169 (
2170 unsupported,
2171 vec![(hash, ordinary)],
2172 "EIP-8130 target transactions are not supported by tooling replay".to_string(),
2173 )
2174 } else {
2175 (
2176 ordinary.clone(),
2177 vec![(B256::ZERO, ordinary), (hash, unsupported)],
2178 format!(
2179 "EIP-8130 prefix transaction {hash} is not supported by tooling replay"
2180 ),
2181 )
2182 };
2183 let error = executor
2184 .transact_with_ordinary_block_replay(
2185 EvmEnvFor::<BaseEvmNetwork>::default(),
2186 target,
2187 prefix,
2188 )
2189 .unwrap_err();
2190 assert_eq!(error.to_string(), expected);
2191 assert_eq!(executor.get_nonce(Address::ZERO).unwrap(), 0);
2192 }
2193 }
2194
2195 #[test]
2196 fn block_replay_commits_prefix_and_traces_only_target() {
2197 let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
2198 let mut executor = ExecutorBuilder::default().gas_limit(1 << 20).build(
2199 EvmEnvFor::<EthEvmNetwork>::default(),
2200 TxEnvFor::<EthEvmNetwork>::default(),
2201 backend,
2202 NetworkConfigs::default(),
2203 );
2204 executor.set_balance(CALLER, U256::MAX).unwrap();
2205 executor.set_trace_requirements(TraceRequirements::none().with_calls(true));
2206
2207 let address = Address::repeat_byte(0x11);
2208 executor
2210 .set_code(
2211 address,
2212 Bytecode::new_raw(Bytes::from_static(&[
2213 0x60, 0x00, 0x54, 0x60, 0x01, 0x01, 0x80, 0x60, 0x00, 0x55, 0x60, 0x00, 0x52,
2214 0x60, 0x20, 0x60, 0x00, 0xf3,
2215 ])),
2216 )
2217 .unwrap();
2218 let prefix = TxEnv {
2219 caller: CALLER,
2220 gas_limit: 100_000,
2221 kind: TxKind::Call(address),
2222 ..Default::default()
2223 };
2224 let reverted_create = TxEnv {
2225 nonce: 1,
2226 kind: TxKind::Create,
2227 data: Bytes::from_static(&[0x5f, 0x5f, 0xfd]),
2228 ..prefix.clone()
2229 };
2230 let target = TxEnv { nonce: 2, ..prefix.clone() };
2231
2232 let result = executor
2233 .transact_with_ordinary_block_replay(
2234 EvmEnv::default(),
2235 target,
2236 vec![(B256::repeat_byte(1), prefix), (B256::repeat_byte(2), reverted_create)],
2237 )
2238 .unwrap();
2239
2240 assert_eq!(result.result, Bytes::from(U256::from(2).to_be_bytes::<32>()));
2241 assert_eq!(result.tx_env.nonce, 2);
2242 assert_eq!(executor.get_nonce(CALLER).unwrap(), 3);
2243 assert_eq!(executor.backend().storage_ref(address, U256::ZERO).unwrap(), U256::from(2));
2244 assert_eq!(result.traces.unwrap().arena.nodes().len(), 1);
2245 }
2246
2247 #[test]
2248 fn block_replay_initializes_create_target_from_canonical_nonce() {
2249 let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
2250 let mut executor = ExecutorBuilder::default().gas_limit(1 << 20).build(
2251 EvmEnvFor::<EthEvmNetwork>::default(),
2252 TxEnvFor::<EthEvmNetwork>::default(),
2253 backend,
2254 NetworkConfigs::default(),
2255 );
2256 executor.set_balance(CALLER, U256::MAX).unwrap();
2257
2258 let prefix = TxEnv {
2259 caller: CALLER,
2260 gas_limit: 100_000,
2261 kind: TxKind::Call(Address::repeat_byte(0x11)),
2262 ..Default::default()
2263 };
2264 let target = TxEnv {
2265 nonce: 1,
2266 kind: TxKind::Create,
2267 data: Bytes::from_static(&[0x00]),
2268 ..prefix.clone()
2269 };
2270 let expected = CALLER.create(1);
2271
2272 let result = executor
2273 .transact_with_ordinary_block_replay(
2274 EvmEnv::default(),
2275 target,
2276 vec![(B256::repeat_byte(1), prefix)],
2277 )
2278 .unwrap();
2279
2280 assert!(
2281 matches!(result.out, Some(Output::Create(_, Some(address))) if address == expected)
2282 );
2283 assert!(executor.backend().is_persistent(&expected));
2284 assert!(executor.backend().has_cheatcode_access(&expected));
2285 }
2286
2287 #[test]
2288 fn block_replay_preserves_successful_prefix_deployment() {
2289 let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
2290 let mut executor = ExecutorBuilder::default().gas_limit(1 << 20).build(
2291 EvmEnvFor::<EthEvmNetwork>::default(),
2292 TxEnvFor::<EthEvmNetwork>::default(),
2293 backend,
2294 NetworkConfigs::default(),
2295 );
2296 executor.set_balance(CALLER, U256::MAX).unwrap();
2297
2298 let deployed = CALLER.create(0);
2299 let prefix = TxEnv {
2300 caller: CALLER,
2301 gas_limit: 100_000,
2302 kind: TxKind::Create,
2303 data: Bytes::from_static(&[0x00]),
2304 ..Default::default()
2305 };
2306 let target = TxEnv { nonce: 1, kind: TxKind::Call(deployed), ..prefix.clone() };
2307
2308 executor
2309 .transact_with_ordinary_block_replay(
2310 EvmEnv::default(),
2311 target,
2312 vec![(B256::repeat_byte(1), prefix)],
2313 )
2314 .unwrap();
2315
2316 assert!(executor.backend().is_persistent(&deployed));
2317 }
2318
2319 #[cfg(feature = "monad")]
2320 #[test]
2321 fn block_replay_executes_monad_system_prefix() {
2322 use foundry_evm_core::evm::MonadEvmNetwork;
2323
2324 let backend = Backend::<MonadEvmNetwork>::spawn(None).unwrap();
2325 let mut executor = ExecutorBuilder::<MonadEvmNetwork>::default().gas_limit(1 << 20).build(
2326 EvmEnvFor::<MonadEvmNetwork>::default(),
2327 TxEnvFor::<MonadEvmNetwork>::default(),
2328 backend,
2329 NetworkConfigs::with_monad(),
2330 );
2331 executor.set_balance(CALLER, U256::MAX).unwrap();
2332
2333 let system_address = alloy_primitives::address!("6f49a8f621353f12378d0046e7d7e4b9b249dc9e");
2334 let staking_address =
2335 alloy_primitives::address!("0000000000000000000000000000000000001000");
2336 let selector = keccak256("syscallSnapshot()");
2337 let system = TxEnv {
2338 caller: system_address,
2339 gas_limit: 0,
2340 kind: TxKind::Call(staking_address),
2341 data: Bytes::copy_from_slice(&selector[..4]),
2342 chain_id: None,
2343 ..Default::default()
2344 };
2345 let target = TxEnv {
2346 caller: CALLER,
2347 gas_limit: 100_000,
2348 kind: TxKind::Call(Address::repeat_byte(0x11)),
2349 ..Default::default()
2350 };
2351 let system_chain = ChainFor::<MonadEvmNetwork>::for_transaction(&system);
2352 let target_chain = ChainFor::<MonadEvmNetwork>::for_transaction(&target);
2353
2354 let (result, used_system_replay) = executor
2355 .transact_with_monad_block_replay(
2356 EvmEnvFor::<MonadEvmNetwork>::default(),
2357 target,
2358 target_chain,
2359 vec![(B256::repeat_byte(1), system, system_chain)],
2360 false,
2361 )
2362 .unwrap()
2363 .unwrap();
2364
2365 assert!(!used_system_replay);
2366 assert!(!result.reverted);
2367 assert_eq!(executor.get_nonce(system_address).unwrap(), 1);
2368 }
2369
2370 #[cfg(feature = "monad")]
2371 #[test]
2372 fn block_replay_executes_monad_system_target() {
2373 use foundry_evm_core::evm::MonadEvmNetwork;
2374
2375 let backend = Backend::<MonadEvmNetwork>::spawn(None).unwrap();
2376 let mut executor = ExecutorBuilder::<MonadEvmNetwork>::default().gas_limit(1 << 20).build(
2377 EvmEnvFor::<MonadEvmNetwork>::default(),
2378 TxEnvFor::<MonadEvmNetwork>::default(),
2379 backend,
2380 NetworkConfigs::with_monad(),
2381 );
2382
2383 let system_address = alloy_primitives::address!("6f49a8f621353f12378d0046e7d7e4b9b249dc9e");
2384 let staking_address =
2385 alloy_primitives::address!("0000000000000000000000000000000000001000");
2386 let selector = keccak256("syscallSnapshot()");
2387 let target = TxEnv {
2388 caller: system_address,
2389 gas_limit: 0,
2390 kind: TxKind::Call(staking_address),
2391 data: Bytes::copy_from_slice(&selector[..4]),
2392 chain_id: None,
2393 ..Default::default()
2394 };
2395 let target_chain = ChainFor::<MonadEvmNetwork>::for_transaction(&target);
2396
2397 let (result, used_system_replay) = executor
2398 .transact_with_monad_block_replay(
2399 EvmEnvFor::<MonadEvmNetwork>::default(),
2400 target,
2401 target_chain,
2402 Vec::new(),
2403 false,
2404 )
2405 .unwrap()
2406 .unwrap();
2407
2408 assert!(used_system_replay);
2409 assert!(!result.reverted);
2410 assert_eq!(executor.get_nonce(system_address).unwrap(), 1);
2411 }
2412
2413 #[test]
2414 fn mismatched_skip_payload_is_execution_error() {
2415 let raw = RawCallResult::<EthEvmNetwork> {
2416 reverted: true,
2417 result: Bytes::from_static(b"FOUNDRY::SKIPforged"),
2418 skip_payloads: vec![Bytes::from_static(b"FOUNDRY::SKIPgenuine")],
2419 ..Default::default()
2420 };
2421
2422 let err = raw.into_evm_error(None);
2423 assert!(matches!(err, EvmError::Execution(_)));
2424 }
2425
2426 #[test]
2427 fn set_spec_id_updates_spec_dependent_cfg_state() {
2428 let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
2429 let mut executor = ExecutorBuilder::default().build(
2430 EvmEnvFor::<EthEvmNetwork>::default(),
2431 TxEnvFor::<EthEvmNetwork>::default(),
2432 backend,
2433 NetworkConfigs::default(),
2434 );
2435
2436 executor.evm_env_mut().cfg_env.set_spec_and_mainnet_gas_params(SpecId::HOMESTEAD);
2437 assert_eq!(
2438 executor.evm_env().cfg_env.gas_params(),
2439 &revm::context_interface::cfg::GasParams::new_spec(SpecId::HOMESTEAD),
2440 );
2441 assert!(!executor.evm_env().cfg_env.is_amsterdam_eip8037_enabled());
2442
2443 executor.set_spec_id(SpecId::AMSTERDAM);
2444
2445 assert_eq!(executor.spec_id(), SpecId::AMSTERDAM);
2446 assert_eq!(
2447 executor.evm_env().cfg_env.gas_params(),
2448 &revm::context_interface::cfg::GasParams::new_spec(SpecId::AMSTERDAM),
2449 );
2450 assert!(executor.evm_env().cfg_env.is_amsterdam_eip8037_enabled());
2451 }
2452
2453 #[test]
2454 fn calculate_stipend_uses_eip2780_transaction_context() {
2455 let caller = Address::repeat_byte(0x11);
2456 let recipient = Address::repeat_byte(0x22);
2457 let mut tx = TxEnv { caller, kind: TxKind::Call(recipient), ..Default::default() };
2458 let cfg = CfgEnv::new_with_spec(SpecId::AMSTERDAM);
2459
2460 assert_eq!(
2461 calculate_stipend(&tx, &cfg),
2462 revm::primitives::eip2780::TX_BASE_COST
2463 + revm::primitives::eip8038::COLD_ACCOUNT_ACCESS
2464 );
2465 let cfg = cfg.with_enable_amsterdam_eip2780(false);
2466 assert_eq!(
2467 calculate_stipend(&tx, &cfg),
2468 revm::context_interface::cfg::GasParams::new_spec(SpecId::AMSTERDAM).tx_base_stipend()
2469 );
2470
2471 tx.kind = TxKind::Call(caller);
2472 let cfg = cfg.with_enable_amsterdam_eip2780(true);
2473 assert_eq!(calculate_stipend(&tx, &cfg), revm::primitives::eip2780::TX_BASE_COST);
2474 }
2475
2476 #[test]
2477 fn amsterdam_intercepted_create_refunds_state_gas() {
2478 let cheats_config =
2479 Arc::new(CheatsConfig::new(&Config::default(), EvmOpts::default(), None, None, false));
2480 let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
2481 let mut executor = ExecutorBuilder::default()
2482 .inspectors(|stack| stack.cheatcodes(cheats_config))
2483 .spec_id(SpecId::AMSTERDAM)
2484 .gas_limit(1_000_000)
2485 .build(EvmEnv::default(), TxEnv::default(), backend, NetworkConfigs::default());
2486
2487 let target = Address::repeat_byte(0x11);
2488 executor
2490 .set_code(
2491 target,
2492 Bytecode::new_raw(Bytes::from_static(&[0x5f, 0x5f, 0x5f, 0xf0, 0x50, 0x00])),
2493 )
2494 .unwrap();
2495 executor.inspector_mut().cheatcodes.as_mut().unwrap().intercept_next_create_call = true;
2496
2497 let result = executor.transact_raw(CALLER, target, Bytes::new(), U256::ZERO).unwrap();
2498
2499 assert!(!result.reverted);
2500 assert!(
2501 result.gas_used
2502 < revm::context_interface::cfg::GasParams::new_spec(SpecId::AMSTERDAM)
2503 .create_state_gas(),
2504 "failed CREATE retained its conditional state-gas charge"
2505 );
2506 }
2507
2508 #[test]
2509 fn amsterdam_mocked_call_revert_refunds_state_gas() {
2510 let cheats_config =
2511 Arc::new(CheatsConfig::new(&Config::default(), EvmOpts::default(), None, None, false));
2512 let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
2513 let mut executor = ExecutorBuilder::default()
2514 .inspectors(|stack| stack.cheatcodes(cheats_config))
2515 .spec_id(SpecId::AMSTERDAM)
2516 .gas_limit(1_000_000)
2517 .build(EvmEnv::default(), TxEnv::default(), backend, NetworkConfigs::default());
2518
2519 let target = Address::repeat_byte(0x11);
2520 let mocked = Address::repeat_byte(0x22);
2521 executor
2522 .transact_raw(
2523 CALLER,
2524 CHEATCODE_ADDRESS,
2525 mockCallRevert_1Call {
2526 callee: mocked,
2527 msgValue: U256::from(1),
2528 data: Bytes::new(),
2529 revertData: Bytes::new(),
2530 }
2531 .abi_encode()
2532 .into(),
2533 U256::ZERO,
2534 )
2535 .unwrap();
2536 executor.set_code(mocked, Bytecode::default()).unwrap();
2537 executor.set_balance(target, U256::from(1)).unwrap();
2538
2539 let mut code = vec![0x5f, 0x5f, 0x5f, 0x5f, 0x60, 0x01, 0x73];
2541 code.extend_from_slice(mocked.as_slice());
2542 code.extend_from_slice(&[0x5a, 0xf1, 0x50, 0x00]);
2543 executor.set_code(target, Bytecode::new_raw(code.into())).unwrap();
2544
2545 let result = executor.transact_raw(CALLER, target, Bytes::new(), U256::ZERO).unwrap();
2546
2547 assert!(!result.reverted);
2548 assert!(
2549 result.gas_used
2550 < revm::context_interface::cfg::GasParams::new_spec(SpecId::AMSTERDAM)
2551 .new_account_state_gas(),
2552 "reverted mocked CALL retained its conditional state-gas charge"
2553 );
2554 }
2555
2556 #[test]
2557 fn set_trace_requirements_replaces_trace_mode_between_transactions() {
2558 let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
2559 let mut executor = ExecutorBuilder::default().gas_limit(1 << 20).build(
2560 EvmEnvFor::<EthEvmNetwork>::default(),
2561 TxEnvFor::<EthEvmNetwork>::default(),
2562 backend,
2563 NetworkConfigs::default(),
2564 );
2565 executor.evm_env_mut().cfg_env.disable_nonce_check = true;
2566 let target = Address::repeat_byte(0x11);
2567 executor
2569 .set_code(
2570 target,
2571 Bytecode::new_raw(Bytes::from_static(&[
2572 0x60, 0x04, 0x56, 0x00, 0x5b, 0x60, 0x01, 0x60, 0x00, 0x55, 0x00,
2573 ])),
2574 )
2575 .unwrap();
2576
2577 let untraced = executor.transact_raw(CALLER, target, Bytes::new(), U256::ZERO).unwrap();
2578 assert!(untraced.traces.is_none());
2579
2580 executor.set_trace_requirements(TraceRequirements::none().with_debug(true));
2581 let debug = executor.transact_raw(CALLER, target, Bytes::new(), U256::ZERO).unwrap();
2582 let debug_steps = &debug.traces.as_ref().unwrap().nodes()[0].trace.steps;
2583 assert_eq!(debug_steps.len(), 7);
2584 assert!(debug_steps.iter().all(|step| step.stack.is_some() && step.memory.is_some()));
2585
2586 executor.set_trace_requirements(
2587 TraceRequirements::none().with_decode_internal(InternalTraceMode::Full),
2588 );
2589 let internal = executor.transact_raw(CALLER, target, Bytes::new(), U256::ZERO).unwrap();
2590 let internal_steps = &internal.traces.as_ref().unwrap().nodes()[0].trace.steps;
2591 assert_eq!(internal_steps.len(), 2);
2592 assert!(internal_steps.iter().all(|step| step.stack.is_some() && step.memory.is_some()));
2593
2594 executor.set_trace_requirements(TraceRequirements::none());
2595 let untraced = executor.transact_raw(CALLER, target, Bytes::new(), U256::ZERO).unwrap();
2596 assert!(untraced.traces.is_none());
2597 }
2598
2599 #[test]
2600 fn early_exit_interrupts_active_evm_execution() {
2601 const GAS_LIMIT: u64 = 1 << 24;
2602 let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
2603 let mut executor = ExecutorBuilder::default().gas_limit(GAS_LIMIT).build(
2604 EvmEnvFor::<EthEvmNetwork>::default(),
2605 TxEnvFor::<EthEvmNetwork>::default(),
2606 backend,
2607 NetworkConfigs::default(),
2608 );
2609 let early_exit = EarlyExit::new(false);
2610 executor.inspector_mut().set_early_exit(early_exit.clone());
2611
2612 let target = Address::repeat_byte(0x11);
2613 executor
2615 .set_code(target, Bytecode::new_raw(Bytes::from_static(&[0x5b, 0x60, 0x00, 0x56])))
2616 .unwrap();
2617
2618 let (started_tx, started_rx) = mpsc::channel();
2619 let (result_tx, result_rx) = mpsc::channel();
2620 let handle = thread::spawn(move || {
2621 started_tx.send(()).unwrap();
2622 let result = executor.transact_raw(CALLER, target, Bytes::new(), U256::ZERO);
2623 let _ = result_tx.send(result);
2624 });
2625
2626 started_rx.recv().unwrap();
2627 thread::sleep(Duration::from_millis(1));
2628 early_exit.record_ctrl_c();
2629
2630 let result = result_rx.recv_timeout(Duration::from_secs(1));
2631 handle.join().unwrap();
2632 let result = result.expect("active EVM execution did not observe early exit").unwrap();
2633 assert!(result.execution_cancelled);
2634 assert!(!result.reverted);
2635 assert_eq!(result.exit_reason, Some(InstructionResult::Stop));
2636 assert!(result.gas_used > 21_000, "interrupt fired before EVM execution started");
2637 assert!(result.gas_used < GAS_LIMIT, "execution ran out of gas instead of exiting");
2638 }
2639
2640 #[test]
2641 fn completed_execution_is_not_retroactively_cancelled() {
2642 let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
2643 let mut executor = ExecutorBuilder::default().gas_limit(1 << 24).build(
2644 EvmEnvFor::<EthEvmNetwork>::default(),
2645 TxEnvFor::<EthEvmNetwork>::default(),
2646 backend,
2647 NetworkConfigs::default(),
2648 );
2649 let early_exit = EarlyExit::new(false);
2650 executor.inspector_mut().set_early_exit(early_exit.clone());
2651
2652 let target = Address::repeat_byte(0x11);
2653 executor.set_code(target, Bytecode::new_raw(Bytes::from_static(&[0x00]))).unwrap();
2654 let result = executor.transact_raw(CALLER, target, Bytes::new(), U256::ZERO).unwrap();
2655 early_exit.record_ctrl_c();
2656
2657 assert!(!result.execution_cancelled);
2658 assert!(!result.reverted);
2659 }
2660
2661 #[test]
2662 fn campaign_deadline_interrupts_active_evm_execution() {
2663 const GAS_LIMIT: u64 = 1 << 24;
2664 let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
2665 let mut executor = ExecutorBuilder::default().gas_limit(GAS_LIMIT).build(
2666 EvmEnvFor::<EthEvmNetwork>::default(),
2667 TxEnvFor::<EthEvmNetwork>::default(),
2668 backend,
2669 NetworkConfigs::default(),
2670 );
2671 let cancellation = EvmExecutionCancellation::campaign(
2672 EarlyExit::new(false),
2673 Arc::new(AtomicBool::new(false)),
2674 Some(Instant::now()),
2675 );
2676 executor.inspector_mut().set_execution_cancellation(cancellation);
2677
2678 let target = Address::repeat_byte(0x11);
2679 executor
2680 .set_code(target, Bytecode::new_raw(Bytes::from_static(&[0x5b, 0x60, 0x00, 0x56])))
2681 .unwrap();
2682
2683 let result = executor.transact_raw(CALLER, target, Bytes::new(), U256::ZERO).unwrap();
2684 assert!(result.execution_cancelled);
2685 assert!(!result.reverted);
2686 assert_eq!(result.exit_reason, Some(InstructionResult::Stop));
2687 assert!(result.gas_used < GAS_LIMIT, "execution ran out of gas instead of timing out");
2688 }
2689
2690 #[test]
2691 fn beacon_root_system_call_does_not_persist_system_address() {
2692 let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
2693 let mut executor = ExecutorBuilder::default().spec_id(SpecId::CANCUN).build(
2694 EvmEnvFor::<EthEvmNetwork>::default(),
2695 TxEnvFor::<EthEvmNetwork>::default(),
2696 backend,
2697 NetworkConfigs::default(),
2698 );
2699 let before = executor.backend().basic_ref(SYSTEM_ADDRESS).unwrap();
2700
2701 executor.apply_beacon_root(B256::repeat_byte(0x11)).unwrap();
2702
2703 assert_eq!(
2704 executor.backend().basic_ref(SYSTEM_ADDRESS).unwrap(),
2705 before,
2706 "EIP-4788 system calls must not persist the system caller account",
2707 );
2708 }
2709
2710 #[test]
2725 fn pre_override_blob_hashes_restored_on_revert_to_state() {
2726 let cheats_config =
2727 Arc::new(CheatsConfig::new(&Config::default(), EvmOpts::default(), None, None, false));
2728
2729 let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
2730 let mut executor = ExecutorBuilder::default()
2731 .inspectors(|stack| stack.cheatcodes(cheats_config))
2732 .spec_id(SpecId::CANCUN)
2733 .build(EvmEnv::default(), TxEnv::default(), backend, NetworkConfigs::default());
2734
2735 let original: Vec<B256> = vec![B256::repeat_byte(0x11), B256::repeat_byte(0x22)];
2736 executor.tx_env_mut().set_blob_hashes(original.clone());
2737
2738 let snap_result = executor
2739 .transact_raw(
2740 CALLER,
2741 CHEATCODE_ADDRESS,
2742 snapshotStateCall {}.abi_encode().into(),
2743 U256::ZERO,
2744 )
2745 .expect("snapshotState failed");
2746 assert!(!snap_result.reverted, "snapshotState reverted unexpectedly");
2747 let snapshot_id = U256::from_be_slice(&snap_result.result[..32]);
2748
2749 let new_hashes = vec![B256::repeat_byte(0x33)];
2750 let blob_result = executor
2751 .transact_raw(
2752 CALLER,
2753 CHEATCODE_ADDRESS,
2754 blobhashesCall { hashes: new_hashes }.abi_encode().into(),
2755 U256::ZERO,
2756 )
2757 .expect("blobhashes failed");
2758 assert!(!blob_result.reverted, "blobhashes reverted unexpectedly");
2759
2760 let revert_result = executor
2761 .transact_raw(
2762 CALLER,
2763 CHEATCODE_ADDRESS,
2764 revertToStateCall { snapshotId: snapshot_id }.abi_encode().into(),
2765 U256::ZERO,
2766 )
2767 .expect("revertToState failed");
2768 assert!(!revert_result.reverted, "revertToState reverted unexpectedly");
2769
2770 assert_eq!(
2771 revert_result.tx_env.blob_hashes, original,
2772 "pre_override_blob_hashes must be restored to original non-empty hashes, not []",
2773 );
2774 assert!(
2775 executor.inspector().cheatcodes.as_ref().unwrap().env_overrides.is_empty(),
2776 "inactive env overrides must be removed after restoring their metadata",
2777 );
2778 }
2779 #[cfg(feature = "monad")]
2780 #[test]
2781 fn concrete_system_replay_preserves_envelope_and_rejects_without_commit() {
2782 let mut executor = ExecutorBuilder::<MonadEvmNetwork>::new().gas_limit(1 << 20).build(
2783 EvmEnvFor::<MonadEvmNetwork>::default(),
2784 TxEnvFor::<MonadEvmNetwork>::default(),
2785 Backend::spawn(None).unwrap(),
2786 NetworkConfigs::with_monad(),
2787 );
2788 let caller = alloy_primitives::address!("6f49a8f621353f12378d0046e7d7e4b9b249dc9e");
2789 let selector = keccak256("syscallSnapshot()");
2790 let system = TxEnv {
2791 caller,
2792 gas_limit: 0,
2793 kind: TxKind::Call(alloy_primitives::address!(
2794 "0000000000000000000000000000000000001000"
2795 )),
2796 data: Bytes::copy_from_slice(&selector[..4]),
2797 chain_id: None,
2798 ..Default::default()
2799 };
2800 let result = executor
2801 .try_transact_system_replay_with_env_and_context(
2802 EvmEnvFor::<MonadEvmNetwork>::default(),
2803 system.clone(),
2804 ChainFor::<MonadEvmNetwork>::for_transaction(&system),
2805 )
2806 .unwrap()
2807 .unwrap();
2808 assert!(!result.reverted);
2809 assert_eq!(result.tx_env, system);
2810 assert_eq!(executor.get_nonce(caller).unwrap(), 1);
2811
2812 assert!(
2814 executor
2815 .try_transact_system_replay_with_env_and_context(
2816 EvmEnvFor::<MonadEvmNetwork>::default(),
2817 system.clone(),
2818 ChainFor::<MonadEvmNetwork>::for_transaction(&system),
2819 )
2820 .is_err()
2821 );
2822 assert_eq!(executor.get_nonce(caller).unwrap(), 1);
2823
2824 let ordinary = TxEnv { caller: CALLER, ..Default::default() };
2825 let nonce = executor.get_nonce(CALLER).unwrap();
2826 assert!(
2827 executor
2828 .try_transact_system_replay_with_env_and_context(
2829 EvmEnvFor::<MonadEvmNetwork>::default(),
2830 ordinary.clone(),
2831 ChainFor::<MonadEvmNetwork>::for_transaction(&ordinary),
2832 )
2833 .unwrap()
2834 .is_none()
2835 );
2836 assert_eq!(executor.get_nonce(CALLER).unwrap(), nonce);
2837 }
2838}