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