1use crate::{
4 Cheatcode, CheatsConfig, CheatsCtxt, Error, Result,
5 Vm::{self, AccountAccess},
6 evm::{
7 DealRecord, GasRecord, RecordAccess, journaled_account,
8 mock::{MockCallDataContext, MockCallReturnData},
9 prank::Prank,
10 },
11 inspector::utils::CommonCreateInput,
12 script::{Broadcast, Wallets},
13 test::{
14 assume::AssumeNoRevert,
15 expect::{
16 self, ExpectedCallData, ExpectedCallTracker, ExpectedCallType, ExpectedCreate,
17 ExpectedEmitTracker, ExpectedRevert, ExpectedRevertKind,
18 },
19 revert_handlers,
20 },
21 utils::IgnoredTraces,
22};
23use alloy_consensus::BlobTransactionSidecarVariant;
24use alloy_network::{Ethereum, Network, TransactionBuilder};
25use alloy_primitives::{
26 Address, B256, Bytes, Log, TxKind, U256, hex,
27 map::{AddressHashMap, HashMap, HashSet},
28};
29use alloy_rpc_types::AccessList;
30use alloy_signer_local::PrivateKeySigner;
31use alloy_sol_types::{SolCall, SolInterface, SolValue};
32use foundry_common::{
33 FoundryTransactionBuilder, SELECTOR_LEN, TransactionMaybeSigned,
34 mapping_slots::{MappingSlots, step as mapping_step},
35};
36use foundry_evm_core::{
37 Breakpoints, EvmEnv, FoundryTransaction, InspectorExt,
38 abi::Vm::stopExpectSafeMemoryCall,
39 backend::{DatabaseError, DatabaseExt, LocalForkId, RevertDiagnostic},
40 constants::{CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS, MAGIC_ASSUME},
41 env::FoundryContextExt,
42 evm::{
43 BlockEnvFor, EthEvmNetwork, FoundryContextFor, FoundryEvmFactory, FoundryEvmNetwork,
44 NestedEvmClosure, SpecFor, TransactionRequestFor, TxEnvFor, with_cloned_context,
45 },
46};
47use foundry_evm_traces::{
48 TracingInspector, TracingInspectorConfig, identifier::SignaturesIdentifier,
49};
50use foundry_wallets::wallet_multi::MultiWallet;
51use itertools::Itertools;
52use proptest::test_runner::{RngAlgorithm, TestRng, TestRunner};
53use rand::Rng;
54use revm::{
55 Inspector,
56 bytecode::opcode as op,
57 context::{Cfg, ContextTr, Host, JournalTr, Transaction, TransactionType, result::EVMError},
58 context_interface::{CreateScheme, transaction::SignedAuthorization},
59 handler::FrameResult,
60 interpreter::{
61 CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, FrameInput, Gas,
62 InstructionResult, Interpreter, InterpreterAction, InterpreterResult,
63 interpreter_types::{Jumps, LoopControl, MemoryTr},
64 return_ok,
65 },
66};
67use serde_json::Value;
68use std::{
69 cmp::max,
70 collections::{BTreeMap, VecDeque},
71 fmt::Debug,
72 fs::File,
73 io::BufReader,
74 ops::Range,
75 path::PathBuf,
76 sync::{Arc, OnceLock},
77};
78
79mod utils;
80
81pub mod analysis;
82pub use analysis::CheatcodeAnalysis;
83
84pub trait CheatcodesExecutor<FEN: FoundryEvmNetwork> {
86 fn with_nested_evm(
89 &mut self,
90 cheats: &mut Cheatcodes<FEN>,
91 ecx: &mut FoundryContextFor<'_, FEN>,
92 f: NestedEvmClosure<'_, SpecFor<FEN>, BlockEnvFor<FEN>, TxEnvFor<FEN>>,
93 ) -> Result<(), EVMError<DatabaseError>>;
94
95 fn transact_on_db(
97 &mut self,
98 cheats: &mut Cheatcodes<FEN>,
99 ecx: &mut FoundryContextFor<'_, FEN>,
100 fork_id: Option<U256>,
101 transaction: B256,
102 ) -> eyre::Result<()>;
103
104 fn transact_from_tx_on_db(
106 &mut self,
107 cheats: &mut Cheatcodes<FEN>,
108 ecx: &mut FoundryContextFor<'_, FEN>,
109 tx: TxEnvFor<FEN>,
110 ) -> eyre::Result<()>;
111
112 #[allow(clippy::type_complexity)]
117 fn with_fresh_nested_evm(
118 &mut self,
119 cheats: &mut Cheatcodes<FEN>,
120 db: &mut <FoundryContextFor<'_, FEN> as ContextTr>::Db,
121 evm_env: EvmEnv<SpecFor<FEN>, BlockEnvFor<FEN>>,
122 f: NestedEvmClosure<'_, SpecFor<FEN>, BlockEnvFor<FEN>, TxEnvFor<FEN>>,
123 ) -> Result<EvmEnv<SpecFor<FEN>, BlockEnvFor<FEN>>, EVMError<DatabaseError>>;
124
125 fn console_log(&mut self, msg: &str);
127
128 fn tracing_inspector(&mut self) -> Option<&mut TracingInspector> {
130 None
131 }
132
133 fn set_in_inner_context(&mut self, _enabled: bool, _original_origin: Option<Address>) {}
137}
138
139pub(crate) fn exec_create<FEN: FoundryEvmNetwork>(
141 executor: &mut dyn CheatcodesExecutor<FEN>,
142 inputs: CreateInputs,
143 ccx: &mut CheatsCtxt<'_, '_, FEN>,
144) -> std::result::Result<CreateOutcome, EVMError<DatabaseError>> {
145 let mut inputs = Some(inputs);
146 let mut outcome = None;
147 executor.with_nested_evm(ccx.state, ccx.ecx, &mut |evm| {
148 let inputs = inputs.take().unwrap();
149 evm.journal_inner_mut().depth += 1;
150
151 let frame = FrameInput::Create(Box::new(inputs));
152
153 let result = match evm.run_execution(frame)? {
154 FrameResult::Call(_) => unreachable!(),
155 FrameResult::Create(create) => create,
156 };
157
158 evm.journal_inner_mut().depth -= 1;
159
160 outcome = Some(result);
161 Ok(())
162 })?;
163 Ok(outcome.unwrap())
164}
165
166#[derive(Debug, Default, Clone, Copy)]
169struct TransparentCheatcodesExecutor;
170
171impl<FEN: FoundryEvmNetwork> CheatcodesExecutor<FEN> for TransparentCheatcodesExecutor {
172 fn with_nested_evm(
173 &mut self,
174 cheats: &mut Cheatcodes<FEN>,
175 ecx: &mut FoundryContextFor<'_, FEN>,
176 f: NestedEvmClosure<'_, SpecFor<FEN>, BlockEnvFor<FEN>, TxEnvFor<FEN>>,
177 ) -> Result<(), EVMError<DatabaseError>> {
178 with_cloned_context(ecx, |db, evm_env, journal_inner| {
179 let mut evm = FEN::EvmFactory::default().create_foundry_nested_evm(db, evm_env, cheats);
180 *evm.journal_inner_mut() = journal_inner;
181 f(&mut *evm)?;
182 let sub_inner = evm.journal_inner_mut().clone();
183 let sub_evm_env = evm.to_evm_env();
184 Ok((sub_evm_env, sub_inner))
185 })
186 }
187
188 fn with_fresh_nested_evm(
189 &mut self,
190 cheats: &mut Cheatcodes<FEN>,
191 db: &mut <FoundryContextFor<'_, FEN> as ContextTr>::Db,
192 evm_env: EvmEnv<SpecFor<FEN>, BlockEnvFor<FEN>>,
193 f: NestedEvmClosure<'_, SpecFor<FEN>, BlockEnvFor<FEN>, TxEnvFor<FEN>>,
194 ) -> Result<EvmEnv<SpecFor<FEN>, BlockEnvFor<FEN>>, EVMError<DatabaseError>> {
195 let mut evm = FEN::EvmFactory::default().create_foundry_nested_evm(db, evm_env, cheats);
196 f(&mut *evm)?;
197 Ok(evm.to_evm_env())
198 }
199
200 fn transact_on_db(
201 &mut self,
202 cheats: &mut Cheatcodes<FEN>,
203 ecx: &mut FoundryContextFor<'_, FEN>,
204 fork_id: Option<U256>,
205 transaction: B256,
206 ) -> eyre::Result<()> {
207 let evm_env = ecx.evm_clone();
208 let (db, inner) = ecx.db_journal_inner_mut();
209 db.transact(fork_id, transaction, evm_env, inner, cheats)
210 }
211
212 fn transact_from_tx_on_db(
213 &mut self,
214 cheats: &mut Cheatcodes<FEN>,
215 ecx: &mut FoundryContextFor<'_, FEN>,
216 tx: TxEnvFor<FEN>,
217 ) -> eyre::Result<()> {
218 let evm_env = ecx.evm_clone();
219 let (db, inner) = ecx.db_journal_inner_mut();
220 db.transact_from_tx(tx, evm_env, inner, cheats)
221 }
222
223 fn console_log(&mut self, _msg: &str) {}
224}
225
226macro_rules! try_or_return {
227 ($e:expr) => {
228 match $e {
229 Ok(v) => v,
230 Err(_) => return,
231 }
232 };
233}
234
235#[derive(Debug, Default)]
237pub struct TestContext {
238 pub opened_read_files: HashMap<PathBuf, BufReader<File>>,
240}
241
242impl Clone for TestContext {
244 fn clone(&self) -> Self {
245 Default::default()
246 }
247}
248
249impl TestContext {
250 pub fn clear(&mut self) {
252 self.opened_read_files.clear();
253 }
254}
255
256#[derive(Clone, Debug)]
258pub struct BroadcastableTransaction<N: Network = Ethereum> {
259 pub rpc: Option<String>,
261 pub transaction: TransactionMaybeSigned<N>,
263}
264
265#[derive(Clone, Debug, Copy)]
266pub struct RecordDebugStepInfo {
267 pub start_node_idx: usize,
269 pub original_tracer_config: TracingInspectorConfig,
271}
272
273#[derive(Clone, Debug, Default)]
302pub struct EnvOverrides {
303 pub basefee: Option<u64>,
305 pub gas_price: Option<u128>,
307 pub blob_hashes: Option<Vec<B256>>,
309 pub pre_override_gas_price: Option<u128>,
313 pub pre_override_tx_type: Option<u8>,
317 pub pre_override_blob_hashes: Option<Vec<B256>>,
320 pending_opcode: Option<u8>,
325 pending_blobhash_index: Option<u64>,
329}
330
331impl EnvOverrides {
332 #[inline]
334 pub const fn is_any_set(&self) -> bool {
335 self.basefee.is_some() || self.gas_price.is_some() || self.blob_hashes.is_some()
336 }
337}
338
339#[derive(Clone, Debug, Default)]
341pub struct GasMetering {
342 pub paused: bool,
344 pub touched: bool,
347 pub reset: bool,
349 pub paused_frames: Vec<Gas>,
351
352 pub active_gas_snapshot: Option<(String, String)>,
354
355 pub last_call_gas: Option<crate::Vm::Gas>,
358
359 pub last_frame_gas: Option<crate::Vm::Gas>,
362
363 pub recording: bool,
365 pub last_gas_used: u64,
367 pub gas_records: Vec<GasRecord>,
369}
370
371impl GasMetering {
372 pub const fn start(&mut self) {
374 self.recording = true;
375 }
376
377 pub const fn stop(&mut self) {
379 self.recording = false;
380 }
381
382 pub fn resume(&mut self) {
384 if self.paused {
385 self.paused = false;
386 self.touched = true;
387 }
388 self.paused_frames.clear();
389 }
390
391 pub fn reset(&mut self) {
393 self.paused = false;
394 self.touched = true;
395 self.reset = true;
396 self.paused_frames.clear();
397 }
398}
399
400#[derive(Clone, Debug, Default)]
402pub struct ArbitraryStorage {
403 values: HashMap<Address, HashMap<U256, U256>>,
407 copies: HashMap<Address, Address>,
409 overwrites: HashSet<Address>,
411}
412
413impl ArbitraryStorage {
414 pub fn mark_arbitrary(&mut self, address: &Address, overwrite: bool) {
416 self.values.insert(*address, HashMap::default());
417 if overwrite {
418 self.overwrites.insert(*address);
419 } else {
420 self.overwrites.remove(address);
421 }
422 }
423
424 pub fn mark_copy(&mut self, from: &Address, to: &Address) {
426 if self.values.contains_key(from) {
427 self.copies.insert(*to, *from);
428 }
429 }
430
431 fn targets(&self) -> impl Iterator<Item = Address> + '_ {
433 self.values.keys().copied()
434 }
435
436 fn target_overwrite_modes(&self) -> impl Iterator<Item = (Address, bool)> + '_ {
439 self.values.keys().map(|address| (*address, self.overwrites.contains(address)))
440 }
441
442 fn copied_targets(&self) -> impl Iterator<Item = Address> + '_ {
444 self.copies.keys().copied()
445 }
446
447 fn copied_target_sources(&self) -> impl Iterator<Item = (Address, Address)> + '_ {
449 self.copies.iter().map(|(target, source)| (*target, *source))
450 }
451
452 fn cache_value(&mut self, address: Address, slot: U256, data: U256) {
454 if let Some(values) = self.values.get_mut(&address) {
455 values.insert(slot, data);
456 return;
457 }
458
459 let Some(source) = self.copies.get(&address).copied() else {
460 return;
461 };
462 if let Some(values) = self.values.get_mut(&source) {
463 values.insert(slot, data);
464 }
465 }
466
467 fn cached_value(&self, address: Address, slot: U256) -> Option<U256> {
469 self.values.get(&address).and_then(|values| values.get(&slot)).copied()
470 }
471
472 pub fn save<CTX: ContextTr>(
476 &mut self,
477 ecx: &mut CTX,
478 address: Address,
479 slot: U256,
480 data: U256,
481 ) {
482 self.values.get_mut(&address).expect("missing arbitrary address entry").insert(slot, data);
483 if ecx.journal_mut().load_account(address).is_ok() {
484 ecx.journal_mut()
485 .sstore(address, slot, data)
486 .expect("could not set arbitrary storage value");
487 }
488 }
489
490 pub fn copy<CTX: ContextTr>(
496 &mut self,
497 ecx: &mut CTX,
498 target: Address,
499 slot: U256,
500 new_value: U256,
501 ) -> U256 {
502 let source = self.copies.get(&target).expect("missing arbitrary copy target entry");
503 let storage_cache = self.values.get_mut(source).expect("missing arbitrary source storage");
504 let value = match storage_cache.get(&slot) {
505 Some(value) => *value,
506 None => {
507 storage_cache.insert(slot, new_value);
508 if ecx.journal_mut().load_account(*source).is_ok() {
510 ecx.journal_mut()
511 .sstore(*source, slot, new_value)
512 .expect("could not copy arbitrary storage value");
513 }
514 new_value
515 }
516 };
517 if ecx.journal_mut().load_account(target).is_ok() {
519 ecx.journal_mut().sstore(target, slot, value).expect("could not set storage");
520 }
521 value
522 }
523}
524
525pub type BroadcastableTransactions<N> = VecDeque<BroadcastableTransaction<N>>;
527
528#[derive(Clone, Debug)]
546pub struct Cheatcodes<FEN: FoundryEvmNetwork = EthEvmNetwork> {
547 pub analysis: Option<CheatcodeAnalysis>,
549
550 pub block: Option<BlockEnvFor<FEN>>,
555
556 pub active_delegations: Vec<SignedAuthorization>,
560
561 pub active_blob_sidecar: Option<BlobTransactionSidecarVariant>,
563
564 pub gas_price: Option<u128>,
569
570 pub labels: AddressHashMap<String>,
572
573 pub pranks: BTreeMap<usize, Prank>,
575
576 pub expected_revert: Option<ExpectedRevert>,
578
579 pub assume_no_revert: Option<AssumeNoRevert>,
581
582 pub fork_revert_diagnostic: Option<RevertDiagnostic>,
584
585 pub accesses: RecordAccess,
587
588 pub recording_accesses: bool,
590
591 pub recorded_account_diffs_stack: Option<Vec<Vec<AccountAccess>>>,
597
598 pub record_debug_steps_info: Option<RecordDebugStepInfo>,
600
601 pub recorded_logs: Option<Vec<crate::Vm::Log>>,
603
604 pub mocked_calls: HashMap<Address, BTreeMap<MockCallDataContext, VecDeque<MockCallReturnData>>>,
607
608 pub mocked_functions: HashMap<Address, HashMap<Bytes, Address>>,
610
611 pub expected_calls: ExpectedCallTracker,
613 pub expected_emits: ExpectedEmitTracker,
615 pub expected_creates: Vec<ExpectedCreate>,
617
618 pub allowed_mem_writes: HashMap<u64, Vec<Range<u64>>>,
620
621 pub broadcast: Option<Broadcast>,
623
624 pub broadcastable_transactions: BroadcastableTransactions<FEN::Network>,
626
627 pub access_list: Option<AccessList>,
629
630 pub config: Arc<CheatsConfig>,
632
633 pub test_context: TestContext,
635
636 pub fs_commit: bool,
639
640 pub serialized_jsons: BTreeMap<String, BTreeMap<String, Value>>,
643
644 pub eth_deals: Vec<DealRecord>,
646
647 pub gas_metering: GasMetering,
649
650 pub gas_snapshots: BTreeMap<String, BTreeMap<String, String>>,
653
654 pub mapping_slots: Option<AddressHashMap<MappingSlots>>,
656
657 pub pc: usize,
659 pub breakpoints: Breakpoints,
662
663 pub intercept_next_create_call: bool,
665
666 test_runner: Option<TestRunner>,
669
670 pub ignored_traces: IgnoredTraces,
672
673 pub arbitrary_storage: Option<ArbitraryStorage>,
675
676 pub deprecated: HashMap<&'static str, Option<&'static str>>,
678 pub wallets: Option<Wallets>,
680 pub private_key_signers: HashMap<U256, PrivateKeySigner>,
682 signatures_identifier: OnceLock<Option<SignaturesIdentifier>>,
684 pub dynamic_gas_limit: bool,
686 pub execution_evm_version: Option<SpecFor<FEN>>,
688
689 pub env_overrides: HashMap<Option<LocalForkId>, EnvOverrides>,
699
700 pub env_overrides_snapshots: HashMap<U256, HashMap<Option<LocalForkId>, EnvOverrides>>,
710
711 pub in_isolation_context: bool,
721}
722
723impl Default for Cheatcodes {
727 fn default() -> Self {
728 Self::new(Arc::default())
729 }
730}
731
732impl<FEN: FoundryEvmNetwork> Cheatcodes<FEN> {
733 pub fn new(config: Arc<CheatsConfig>) -> Self {
735 Self {
736 analysis: None,
737 fs_commit: true,
738 labels: config.labels.clone(),
739 config,
740 block: Default::default(),
741 active_delegations: Default::default(),
742 active_blob_sidecar: Default::default(),
743 gas_price: Default::default(),
744 pranks: Default::default(),
745 expected_revert: Default::default(),
746 assume_no_revert: Default::default(),
747 fork_revert_diagnostic: Default::default(),
748 accesses: Default::default(),
749 recording_accesses: Default::default(),
750 recorded_account_diffs_stack: Default::default(),
751 recorded_logs: Default::default(),
752 record_debug_steps_info: Default::default(),
753 mocked_calls: Default::default(),
754 mocked_functions: Default::default(),
755 expected_calls: Default::default(),
756 expected_emits: Default::default(),
757 expected_creates: Default::default(),
758 allowed_mem_writes: Default::default(),
759 broadcast: Default::default(),
760 broadcastable_transactions: Default::default(),
761 access_list: Default::default(),
762 test_context: Default::default(),
763 serialized_jsons: Default::default(),
764 eth_deals: Default::default(),
765 gas_metering: Default::default(),
766 gas_snapshots: Default::default(),
767 mapping_slots: Default::default(),
768 pc: Default::default(),
769 breakpoints: Default::default(),
770 intercept_next_create_call: Default::default(),
771 test_runner: Default::default(),
772 ignored_traces: Default::default(),
773 arbitrary_storage: Default::default(),
774 deprecated: Default::default(),
775 wallets: Default::default(),
776 private_key_signers: Default::default(),
777 signatures_identifier: Default::default(),
778 dynamic_gas_limit: Default::default(),
779 execution_evm_version: None,
780 env_overrides: Default::default(),
781 env_overrides_snapshots: Default::default(),
782 in_isolation_context: false,
783 }
784 }
785
786 pub fn set_analysis(&mut self, analysis: CheatcodeAnalysis) {
788 self.analysis = Some(analysis);
789 }
790
791 pub fn env_overrides_for(&self, fork_id: Option<U256>) -> Option<&EnvOverrides> {
793 self.env_overrides.get(&fork_id).filter(|o| o.is_any_set())
794 }
795
796 pub fn env_overrides_for_mut(&mut self, fork_id: Option<U256>) -> &mut EnvOverrides {
799 self.env_overrides.entry(fork_id).or_default()
800 }
801
802 pub fn get_prank(&self, depth: usize) -> Option<&Prank> {
806 self.pranks.range(..=depth).last().map(|(_, prank)| prank)
807 }
808
809 pub fn wallets(&mut self) -> &Wallets {
811 self.wallets.get_or_insert_with(|| Wallets::new(MultiWallet::default(), None))
812 }
813
814 pub fn set_wallets(&mut self, wallets: Wallets) {
816 self.wallets = Some(wallets);
817 }
818
819 pub fn add_delegation(&mut self, authorization: SignedAuthorization) {
821 self.active_delegations.push(authorization);
822 }
823
824 pub fn signatures_identifier(&self) -> Option<&SignaturesIdentifier> {
826 self.signatures_identifier
827 .get_or_init(|| {
828 if let Some(artifacts) = &self.config.available_artifacts {
829 return SignaturesIdentifier::new_offline_with_abis(
830 artifacts.values().map(|contract| &contract.abi),
831 )
832 .ok();
833 }
834 SignaturesIdentifier::new(true).ok()
835 })
836 .as_ref()
837 }
838
839 fn apply_cheatcode(
841 &mut self,
842 ecx: &mut FoundryContextFor<'_, FEN>,
843 call: &CallInputs,
844 executor: &mut dyn CheatcodesExecutor<FEN>,
845 ) -> Result {
846 let decoded = Vm::VmCalls::abi_decode(&call.input.bytes(ecx)).map_err(|e| {
848 if let alloy_sol_types::Error::UnknownSelector { name: _, selector } = e {
849 let msg = format!(
850 "unknown cheatcode with selector {selector}; \
851 you may have a mismatch between the `Vm` interface (likely in `forge-std`) \
852 and the `forge` version"
853 );
854 return alloy_sol_types::Error::Other(std::borrow::Cow::Owned(msg));
855 }
856 e
857 })?;
858
859 let caller = call.caller;
860
861 ecx.db_mut().ensure_cheatcode_access_forking_mode(&caller)?;
864
865 apply_dispatch(
866 &decoded,
867 &mut CheatsCtxt { state: self, ecx, gas_limit: call.gas_limit, caller },
868 executor,
869 )
870 }
871
872 fn allow_cheatcodes_on_create(
878 &self,
879 ecx: &mut FoundryContextFor<FEN>,
880 caller: Address,
881 created_address: Address,
882 ) {
883 if ecx.journal().depth() <= 1 || ecx.db().has_cheatcode_access(&caller) {
884 ecx.db_mut().allow_cheatcode_access(created_address);
885 }
886 }
887
888 fn apply_accesslist(&mut self, ecx: &mut FoundryContextFor<FEN>) {
894 if let Some(access_list) = &self.access_list {
895 ecx.tx_mut().set_access_list(access_list.clone());
896
897 if ecx.tx().tx_type() == TransactionType::Legacy as u8 {
898 ecx.tx_mut().set_tx_type(TransactionType::Eip2930 as u8);
899 }
900 }
901 }
902
903 pub fn on_revert(&mut self, ecx: &mut FoundryContextFor<FEN>) {
908 trace!(deals=?self.eth_deals.len(), "rolling back deals");
909
910 if self.expected_revert.is_some() {
912 return;
913 }
914
915 if ecx.journal().depth() > 0 {
917 return;
918 }
919
920 while let Some(record) = self.eth_deals.pop() {
924 if let Some(acc) = ecx.journal_mut().evm_state_mut().get_mut(&record.address) {
925 acc.info.balance = record.old_balance;
926 }
927 }
928 }
929
930 pub fn call_with_executor(
931 &mut self,
932 ecx: &mut FoundryContextFor<'_, FEN>,
933 call: &mut CallInputs,
934 executor: &mut dyn CheatcodesExecutor<FEN>,
935 ) -> Option<CallOutcome> {
936 if let Some(spec_id) = self.execution_evm_version {
938 ecx.cfg_mut().set_spec_and_mainnet_gas_params(spec_id);
939 }
940
941 let gas = Gas::new(call.gas_limit);
942 let curr_depth = ecx.journal().depth();
943
944 if curr_depth == 0 {
948 let sender = ecx.tx().caller();
949 let account = match super::evm::journaled_account(ecx, sender) {
950 Ok(account) => account,
951 Err(err) => {
952 return Some(CallOutcome {
953 result: InterpreterResult {
954 result: InstructionResult::Revert,
955 output: err.abi_encode().into(),
956 gas,
957 },
958 memory_offset: call.return_memory_offset.clone(),
959 was_precompile_called: false,
960 precompile_call_logs: vec![],
961 charged_new_account_state_gas: false,
962 });
963 }
964 };
965 let prev = account.info.nonce;
966 account.info.nonce = prev.saturating_sub(1);
967
968 trace!(target: "cheatcodes", %sender, nonce=account.info.nonce, prev, "corrected nonce");
969 }
970
971 if call.target_address == CHEATCODE_ADDRESS {
972 return match self.apply_cheatcode(ecx, call, executor) {
973 Ok(retdata) => Some(CallOutcome {
974 result: InterpreterResult {
975 result: InstructionResult::Return,
976 output: retdata.into(),
977 gas,
978 },
979 memory_offset: call.return_memory_offset.clone(),
980 was_precompile_called: true,
981 precompile_call_logs: vec![],
982 charged_new_account_state_gas: false,
983 }),
984 Err(err) => Some(CallOutcome {
985 result: InterpreterResult {
986 result: InstructionResult::Revert,
987 output: err.abi_encode().into(),
988 gas,
989 },
990 memory_offset: call.return_memory_offset.clone(),
991 was_precompile_called: false,
992 precompile_call_logs: vec![],
993 charged_new_account_state_gas: false,
994 }),
995 };
996 }
997
998 if call.target_address == HARDHAT_CONSOLE_ADDRESS {
999 return None;
1000 }
1001
1002 if let Some(expected) = &mut self.expected_revert {
1006 expected.max_depth = max(curr_depth + 1, expected.max_depth);
1007 }
1008
1009 if let Some(expected_calls_for_target) = self.expected_calls.get_mut(&call.bytecode_address)
1013 {
1014 let input = call.input.as_bytes(ecx);
1015 let value = call.transfer_value();
1016
1017 for (calldata, (expected, actual_count)) in expected_calls_for_target {
1019 if calldata.len() <= input.len() &&
1022 input.get(..calldata.len()) == Some(calldata.as_ref()) &&
1024 expected.value.is_none_or(|expected_value| Some(expected_value) == value) &&
1026 expected.gas.is_none_or(|gas| gas == call.gas_limit) &&
1028 expected.min_gas.is_none_or(|min_gas| min_gas <= call.gas_limit)
1030 {
1031 *actual_count += 1;
1032 }
1033 }
1034 }
1035
1036 if let Some(prank) = &self.get_prank(curr_depth) {
1038 if prank.delegate_call
1040 && curr_depth == prank.depth
1041 && call.scheme == CallScheme::DelegateCall
1042 {
1043 call.target_address = prank.new_caller;
1044 call.caller = prank.new_caller;
1045 if let Some(new_origin) = prank.new_origin {
1046 ecx.tx_mut().set_caller(new_origin);
1047 }
1048 }
1049
1050 if curr_depth >= prank.depth && call.caller == prank.prank_caller {
1051 let prank_applied = if curr_depth == prank.depth {
1053 let _ = journaled_account(ecx, prank.new_caller);
1055 call.caller = prank.new_caller;
1056 true
1057 } else {
1058 false
1059 };
1060
1061 let prank_applied = if let Some(new_origin) = prank.new_origin {
1063 ecx.tx_mut().set_caller(new_origin);
1064 true
1065 } else {
1066 prank_applied
1067 };
1068
1069 if prank_applied && let Some(applied_prank) = prank.first_time_applied() {
1071 self.pranks.insert(curr_depth, applied_prank);
1072 }
1073 }
1074 }
1075
1076 if let Some(mocks) = self.mocked_calls.get_mut(&call.bytecode_address) {
1078 let input = call.input.bytes(ecx);
1079 let value = call.transfer_value();
1080 let ctx = MockCallDataContext { calldata: input.clone(), value };
1081
1082 if let Some(return_data_queue) = match mocks.get_mut(&ctx) {
1083 Some(queue) => Some(queue),
1084 None => mocks
1085 .iter_mut()
1086 .find(|(mock, _)| {
1087 input.get(..mock.calldata.len()) == Some(&mock.calldata[..])
1088 && mock.value.is_none_or(|mock_value| Some(mock_value) == value)
1089 })
1090 .map(|(_, v)| v),
1091 } && let Some(return_data) = return_data_queue.front().map(|x| x.to_owned())
1092 {
1093 if let Some(value) = call.transfer_value() {
1094 let checkpoint = ecx.journal_mut().checkpoint();
1095 match ecx.journal_mut().transfer_loaded(
1096 call.transfer_from(),
1097 call.transfer_to(),
1098 value,
1099 ) {
1100 None => {
1101 if return_data.ret_type.is_ok() {
1102 ecx.journal_mut().checkpoint_commit();
1103 } else {
1104 ecx.journal_mut().checkpoint_revert(checkpoint);
1105 }
1106 }
1107 Some(err) => {
1108 ecx.journal_mut().checkpoint_revert(checkpoint);
1109 return Some(CallOutcome {
1110 result: InterpreterResult {
1111 result: err.into(),
1112 output: Bytes::new(),
1113 gas,
1114 },
1115 memory_offset: call.return_memory_offset.clone(),
1116 was_precompile_called: false,
1117 precompile_call_logs: vec![],
1118 charged_new_account_state_gas: false,
1119 });
1120 }
1121 }
1122 }
1123
1124 if return_data_queue.len() > 1 {
1126 return_data_queue.pop_front();
1127 }
1128
1129 return Some(CallOutcome {
1130 result: InterpreterResult {
1131 result: return_data.ret_type,
1132 output: return_data.data,
1133 gas,
1134 },
1135 memory_offset: call.return_memory_offset.clone(),
1136 was_precompile_called: true,
1137 precompile_call_logs: vec![],
1138 charged_new_account_state_gas: false,
1139 });
1140 }
1141 }
1142
1143 self.apply_accesslist(ecx);
1145
1146 if let Some(broadcast) = &self.broadcast {
1148 let is_fixed_gas_limit = call.gas_limit >= 21_000 && !self.dynamic_gas_limit;
1151 self.dynamic_gas_limit = false;
1152
1153 if curr_depth == broadcast.depth && call.caller == broadcast.original_caller {
1158 ecx.tx_mut().set_caller(broadcast.new_origin);
1162
1163 call.caller = broadcast.new_origin;
1164 if !call.is_static {
1169 if let Err(err) = ecx.journal_mut().load_account(broadcast.new_origin) {
1170 return Some(CallOutcome {
1171 result: InterpreterResult {
1172 result: InstructionResult::Revert,
1173 output: Error::encode(err),
1174 gas,
1175 },
1176 memory_offset: call.return_memory_offset.clone(),
1177 was_precompile_called: false,
1178 precompile_call_logs: vec![],
1179 charged_new_account_state_gas: false,
1180 });
1181 }
1182
1183 let input = call.input.bytes(ecx);
1184 let chain_id = ecx.cfg().chain_id();
1185 let rpc = ecx.db().active_fork_url();
1186 let account =
1187 ecx.journal_mut().evm_state_mut().get_mut(&broadcast.new_origin).unwrap();
1188
1189 let mut tx_req = TransactionRequestFor::<FEN>::default()
1190 .with_from(broadcast.new_origin)
1191 .with_to(call.target_address)
1192 .with_value(call.transfer_value().unwrap_or_default())
1193 .with_input(input)
1194 .with_nonce(account.info.nonce)
1195 .with_chain_id(chain_id);
1196 if is_fixed_gas_limit {
1197 tx_req.set_gas_limit(call.gas_limit)
1198 }
1199
1200 let active_delegations = std::mem::take(&mut self.active_delegations);
1201 if let Some(blob_sidecar) = self.active_blob_sidecar.take() {
1203 if !active_delegations.is_empty() {
1205 let msg = "both delegation and blob are active; `attachBlob` and `attachDelegation` are not compatible";
1206 return Some(CallOutcome {
1207 result: InterpreterResult {
1208 result: InstructionResult::Revert,
1209 output: Error::encode(msg),
1210 gas,
1211 },
1212 memory_offset: call.return_memory_offset.clone(),
1213 was_precompile_called: false,
1214 precompile_call_logs: vec![],
1215 charged_new_account_state_gas: false,
1216 });
1217 }
1218 tx_req.set_blob_sidecar(blob_sidecar);
1219 }
1220
1221 if !active_delegations.is_empty() {
1223 for auth in &active_delegations {
1224 let Ok(authority) = auth.recover_authority() else {
1225 continue;
1226 };
1227 if authority == broadcast.new_origin {
1228 account.info.nonce += 1;
1231 }
1232 }
1233 tx_req.set_authorization_list(active_delegations);
1234 }
1235 if let Some(fee_token) = self.config.fee_token {
1236 tx_req.set_fee_token(fee_token);
1237 }
1238 self.broadcastable_transactions.push_back(BroadcastableTransaction {
1239 rpc,
1240 transaction: TransactionMaybeSigned::new(tx_req),
1241 });
1242 debug!(target: "cheatcodes", tx=?self.broadcastable_transactions.back().unwrap(), "broadcastable call");
1243
1244 if !self.config.evm_opts.isolate {
1246 let prev = account.info.nonce;
1247 account.info.nonce += 1;
1248 debug!(target: "cheatcodes", address=%broadcast.new_origin, nonce=prev+1, prev, "incremented nonce");
1249 }
1250 } else if broadcast.single_call {
1251 let msg = "`staticcall`s are not allowed after `broadcast`; use `startBroadcast` instead";
1252 return Some(CallOutcome {
1253 result: InterpreterResult {
1254 result: InstructionResult::Revert,
1255 output: Error::encode(msg),
1256 gas,
1257 },
1258 memory_offset: call.return_memory_offset.clone(),
1259 was_precompile_called: false,
1260 precompile_call_logs: vec![],
1261 charged_new_account_state_gas: false,
1262 });
1263 }
1264 }
1265 }
1266
1267 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1269 let (initialized, old_balance, old_nonce) =
1272 if let Ok(acc) = ecx.journal_mut().load_account(call.target_address) {
1273 (acc.data.info.exists(), acc.data.info.balance, acc.data.info.nonce)
1274 } else {
1275 (false, U256::ZERO, 0)
1276 };
1277
1278 let kind = match call.scheme {
1279 CallScheme::Call => crate::Vm::AccountAccessKind::Call,
1280 CallScheme::CallCode => crate::Vm::AccountAccessKind::CallCode,
1281 CallScheme::DelegateCall => crate::Vm::AccountAccessKind::DelegateCall,
1282 CallScheme::StaticCall => crate::Vm::AccountAccessKind::StaticCall,
1283 };
1284
1285 recorded_account_diffs_stack.push(vec![AccountAccess {
1291 chainInfo: crate::Vm::ChainInfo {
1292 forkId: ecx.db().active_fork_id().unwrap_or_default(),
1293 chainId: U256::from(ecx.cfg().chain_id()),
1294 },
1295 accessor: call.caller,
1296 account: call.bytecode_address,
1297 kind,
1298 initialized,
1299 oldBalance: old_balance,
1300 newBalance: U256::ZERO, oldNonce: old_nonce,
1302 newNonce: 0, value: call.call_value(),
1304 data: call.input.bytes(ecx),
1305 reverted: false,
1306 deployedCode: Bytes::new(),
1307 storageAccesses: vec![], depth: ecx.journal().depth().try_into().expect("journaled state depth exceeds u64"),
1309 }]);
1310 }
1311
1312 None
1313 }
1314
1315 pub fn rng(&mut self) -> &mut impl Rng {
1316 self.test_runner().rng()
1317 }
1318
1319 pub fn test_runner(&mut self) -> &mut TestRunner {
1320 self.test_runner.get_or_insert_with(|| match self.config.seed {
1321 Some(seed) => TestRunner::new_with_rng(
1322 proptest::test_runner::Config::default(),
1323 TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>()),
1324 ),
1325 None => TestRunner::new(proptest::test_runner::Config::default()),
1326 })
1327 }
1328
1329 pub fn set_seed(&mut self, seed: U256) {
1330 self.test_runner = Some(TestRunner::new_with_rng(
1331 proptest::test_runner::Config::default(),
1332 TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>()),
1333 ));
1334 }
1335
1336 pub fn arbitrary_storage(&mut self) -> &mut ArbitraryStorage {
1339 self.arbitrary_storage.get_or_insert_with(ArbitraryStorage::default)
1340 }
1341
1342 pub fn arbitrary_storage_targets(&self) -> impl Iterator<Item = Address> + '_ {
1344 self.arbitrary_storage.as_ref().into_iter().flat_map(ArbitraryStorage::targets)
1345 }
1346
1347 pub fn arbitrary_storage_target_overwrite_modes(
1350 &self,
1351 ) -> impl Iterator<Item = (Address, bool)> + '_ {
1352 self.arbitrary_storage
1353 .as_ref()
1354 .into_iter()
1355 .flat_map(ArbitraryStorage::target_overwrite_modes)
1356 }
1357
1358 pub fn arbitrary_storage_copied_targets(&self) -> impl Iterator<Item = Address> + '_ {
1360 self.arbitrary_storage.as_ref().into_iter().flat_map(ArbitraryStorage::copied_targets)
1361 }
1362
1363 pub fn arbitrary_storage_copied_target_sources(
1365 &self,
1366 ) -> impl Iterator<Item = (Address, Address)> + '_ {
1367 self.arbitrary_storage
1368 .as_ref()
1369 .into_iter()
1370 .flat_map(ArbitraryStorage::copied_target_sources)
1371 }
1372
1373 pub fn cache_arbitrary_storage_value(&mut self, address: Address, slot: U256, value: U256) {
1375 if let Some(storage) = &mut self.arbitrary_storage {
1376 storage.cache_value(address, slot, value);
1377 }
1378 }
1379
1380 pub fn cached_arbitrary_storage_value(&self, address: Address, slot: U256) -> Option<U256> {
1382 self.arbitrary_storage.as_ref().and_then(|storage| storage.cached_value(address, slot))
1383 }
1384
1385 pub fn has_arbitrary_storage(&self, address: &Address) -> bool {
1387 match &self.arbitrary_storage {
1388 Some(storage) => storage.values.contains_key(address),
1389 None => false,
1390 }
1391 }
1392
1393 pub fn should_overwrite_arbitrary_storage(
1397 &self,
1398 address: &Address,
1399 storage_slot: U256,
1400 ) -> bool {
1401 match &self.arbitrary_storage {
1402 Some(storage) => {
1403 storage.overwrites.contains(address)
1404 && storage
1405 .values
1406 .get(address)
1407 .and_then(|arbitrary_values| arbitrary_values.get(&storage_slot))
1408 .is_none()
1409 }
1410 None => false,
1411 }
1412 }
1413
1414 pub fn is_arbitrary_storage_copy(&self, address: &Address) -> bool {
1416 match &self.arbitrary_storage {
1417 Some(storage) => storage.copies.contains_key(address),
1418 None => false,
1419 }
1420 }
1421
1422 #[inline(always)]
1423 pub fn has_step_hooks(&self) -> bool {
1424 self.broadcast.is_some()
1425 || self.gas_metering.paused
1426 || self.gas_metering.reset
1427 || self.recording_accesses
1428 || self.recorded_account_diffs_stack.is_some()
1429 || !self.allowed_mem_writes.is_empty()
1430 || self.mapping_slots.is_some()
1431 || self.gas_metering.recording
1432 || self.has_active_env_overrides()
1433 }
1434
1435 #[inline(always)]
1436 pub fn has_step_end_hooks(&self) -> bool {
1437 self.gas_metering.paused
1438 || self.gas_metering.touched
1439 || self.arbitrary_storage.is_some()
1440 || self.has_active_env_overrides()
1441 }
1442
1443 #[inline(always)]
1444 pub fn has_log_hooks(&self) -> bool {
1445 !self.expected_emits.is_empty() || self.recorded_logs.is_some()
1446 }
1447
1448 #[inline(always)]
1449 pub fn has_recording_accesses_only_step_hook(&self) -> bool {
1450 self.recording_accesses
1451 && self.broadcast.is_none()
1452 && !self.gas_metering.paused
1453 && !self.gas_metering.reset
1454 && self.recorded_account_diffs_stack.is_none()
1455 && self.allowed_mem_writes.is_empty()
1456 && self.mapping_slots.is_none()
1457 && !self.gas_metering.recording
1458 && !self.has_active_env_overrides()
1459 }
1460
1461 #[inline(always)]
1462 fn has_active_env_overrides(&self) -> bool {
1463 self.env_overrides.values().any(EnvOverrides::is_any_set)
1464 }
1465
1466 pub fn struct_defs(&self) -> Option<&foundry_common::fmt::StructDefinitions> {
1468 self.analysis.as_ref().and_then(|analysis| analysis.struct_defs().ok())
1469 }
1470}
1471
1472impl<FEN: FoundryEvmNetwork> Inspector<FoundryContextFor<'_, FEN>> for Cheatcodes<FEN> {
1473 fn initialize_interp(
1474 &mut self,
1475 interpreter: &mut Interpreter,
1476 ecx: &mut FoundryContextFor<'_, FEN>,
1477 ) {
1478 if let Some(block) = self.block.take() {
1481 ecx.set_block(block);
1482 }
1483 if let Some(gas_price) = self.gas_price.take() {
1484 ecx.tx_mut().set_gas_price(gas_price);
1485 }
1486
1487 if self.gas_metering.paused {
1489 self.gas_metering.paused_frames.push(interpreter.gas);
1490 }
1491
1492 if let Some(expected) = &mut self.expected_revert {
1494 expected.max_depth = max(ecx.journal().depth(), expected.max_depth);
1495 }
1496 }
1497
1498 fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut FoundryContextFor<'_, FEN>) {
1499 self.pc = interpreter.bytecode.pc();
1500
1501 if !self.has_step_hooks() {
1502 return;
1503 }
1504
1505 if self.broadcast.is_some() {
1506 self.set_gas_limit_type(interpreter);
1507 }
1508
1509 if self.gas_metering.paused {
1511 self.meter_gas(interpreter);
1512 }
1513
1514 if self.gas_metering.reset {
1516 self.meter_gas_reset(interpreter);
1517 }
1518
1519 if self.recording_accesses {
1521 self.record_accesses(interpreter);
1522 }
1523
1524 if self.recorded_account_diffs_stack.is_some() {
1526 self.record_state_diffs(interpreter, ecx);
1527 }
1528
1529 if !self.allowed_mem_writes.is_empty() {
1531 self.check_mem_opcodes(
1532 interpreter,
1533 ecx.journal().depth().try_into().expect("journaled state depth exceeds u64"),
1534 );
1535 }
1536
1537 if let Some(mapping_slots) = &mut self.mapping_slots {
1539 mapping_step(mapping_slots, interpreter);
1540 }
1541
1542 if self.gas_metering.recording {
1544 self.meter_gas_record(interpreter, ecx);
1545 }
1546
1547 if !self.env_overrides.is_empty() {
1552 let fork_id = ecx.db().active_fork_id();
1553 if let Some(env_overrides) =
1554 self.env_overrides.get_mut(&fork_id).filter(|o| o.is_any_set())
1555 {
1556 env_overrides.pending_opcode = None;
1560 env_overrides.pending_blobhash_index = None;
1561
1562 let opcode = interpreter.bytecode.opcode();
1563 match opcode {
1564 op::BASEFEE | op::GASPRICE => {
1565 env_overrides.pending_opcode = Some(opcode);
1566 }
1567 op::BLOBHASH => {
1568 env_overrides.pending_opcode = Some(opcode);
1569 env_overrides.pending_blobhash_index =
1570 interpreter.stack.peek(0).ok().and_then(|index| index.try_into().ok());
1571 }
1572 _ => {}
1573 }
1574 }
1575 }
1576 }
1577
1578 fn step_end(&mut self, interpreter: &mut Interpreter, ecx: &mut FoundryContextFor<'_, FEN>) {
1579 if !self.has_step_end_hooks() {
1580 return;
1581 }
1582
1583 if self.gas_metering.paused {
1584 self.meter_gas_end(interpreter);
1585 }
1586
1587 if self.gas_metering.touched {
1588 self.meter_gas_check(interpreter);
1589 }
1590
1591 if self.arbitrary_storage.is_some() {
1593 self.arbitrary_storage_end(interpreter, ecx);
1594 }
1595
1596 if !self.env_overrides.is_empty() {
1606 let fork_id = ecx.db().active_fork_id();
1607 if self.env_overrides.get(&fork_id).is_some_and(|o| o.is_any_set()) {
1608 let opcode_failed = interpreter
1614 .bytecode
1615 .action
1616 .as_ref()
1617 .and_then(|a| a.instruction_result())
1618 .is_some();
1619 if opcode_failed {
1620 if let Some(env_overrides) = self.env_overrides.get_mut(&fork_id) {
1621 env_overrides.pending_opcode = None;
1622 env_overrides.pending_blobhash_index = None;
1623 }
1624 } else {
1625 self.apply_env_overrides(interpreter, fork_id);
1626 }
1627 }
1628 }
1629 }
1630
1631 fn log(&mut self, _ecx: &mut FoundryContextFor<'_, FEN>, log: Log) {
1632 if !self.expected_emits.is_empty()
1633 && let Some(err) = expect::handle_expect_emit(self, &log, None)
1634 {
1635 let _ = sh_err!("{err:?}");
1639 }
1640
1641 record_logs(&mut self.recorded_logs, &log);
1643 }
1644
1645 fn log_full(
1646 &mut self,
1647 interpreter: &mut Interpreter,
1648 _ecx: &mut FoundryContextFor<'_, FEN>,
1649 log: Log,
1650 ) {
1651 if !self.expected_emits.is_empty() {
1652 expect::handle_expect_emit(self, &log, Some(interpreter));
1653 }
1654
1655 record_logs(&mut self.recorded_logs, &log);
1657 }
1658
1659 fn call(
1660 &mut self,
1661 ecx: &mut FoundryContextFor<'_, FEN>,
1662 inputs: &mut CallInputs,
1663 ) -> Option<CallOutcome> {
1664 Self::call_with_executor(self, ecx, inputs, &mut TransparentCheatcodesExecutor)
1665 }
1666
1667 fn call_end(
1668 &mut self,
1669 ecx: &mut FoundryContextFor<'_, FEN>,
1670 call: &CallInputs,
1671 outcome: &mut CallOutcome,
1672 ) {
1673 let cheatcode_call = call.target_address == CHEATCODE_ADDRESS
1674 || call.target_address == HARDHAT_CONSOLE_ADDRESS;
1675
1676 if !cheatcode_call {
1680 let curr_depth = ecx.journal().depth();
1682 if let Some(prank) = &self.get_prank(curr_depth)
1683 && curr_depth == prank.depth
1684 {
1685 ecx.tx_mut().set_caller(prank.prank_origin);
1686
1687 if prank.single_call {
1689 self.pranks.remove(&curr_depth);
1690 }
1691 }
1692
1693 if let Some(broadcast) = &self.broadcast
1695 && curr_depth == broadcast.depth
1696 {
1697 ecx.tx_mut().set_caller(broadcast.original_origin);
1698
1699 if broadcast.single_call {
1701 let _ = self.broadcast.take();
1702 }
1703 }
1704 }
1705
1706 if let Some(assume_no_revert) = &mut self.assume_no_revert {
1708 if outcome.result.is_revert() && assume_no_revert.reverted_by.is_none() {
1711 assume_no_revert.reverted_by = Some(call.target_address);
1712 }
1713
1714 let curr_depth = ecx.journal().depth();
1716 if curr_depth <= assume_no_revert.depth && !cheatcode_call {
1717 if outcome.result.is_revert() {
1720 let assume_no_revert = std::mem::take(&mut self.assume_no_revert).unwrap();
1721 return match revert_handlers::handle_assume_no_revert(
1722 &assume_no_revert,
1723 outcome.result.result,
1724 &outcome.result.output,
1725 &self.config.available_artifacts,
1726 ) {
1727 Ok(_) => {
1730 outcome.result.output = Error::from(MAGIC_ASSUME).abi_encode().into();
1731 }
1732 Err(error) => {
1735 trace!(expected=?assume_no_revert, ?error, status=?outcome.result.result, "Expected revert mismatch");
1736 outcome.result.result = InstructionResult::Revert;
1737 outcome.result.output = error.abi_encode().into();
1738 }
1739 };
1740 }
1741 self.assume_no_revert = None;
1743 }
1744 }
1745
1746 if let Some(expected_revert) = &mut self.expected_revert {
1748 let call_failed = !matches!(outcome.result.result, return_ok!());
1751 if call_failed {
1752 if expected_revert.reverter.is_some()
1756 && (expected_revert.reverted_by.is_none() || expected_revert.count > 1)
1757 {
1758 expected_revert.reverted_by = Some(call.target_address);
1759 }
1760 }
1761
1762 let curr_depth = ecx.journal().depth();
1763 if curr_depth <= expected_revert.depth {
1764 let internal = self.config.internal_expect_revert;
1769 let went_deeper = expected_revert.max_depth > expected_revert.depth;
1770 let needs_processing = match expected_revert.kind {
1771 ExpectedRevertKind::Default => (|| {
1772 if cheatcode_call {
1774 return false;
1775 }
1776 if call_failed {
1778 return true;
1779 }
1780 if !internal && went_deeper {
1782 return true;
1783 }
1784 if curr_depth == 0 {
1786 return true;
1787 }
1788 !internal
1791 })(),
1792 ExpectedRevertKind::Cheatcode { pending_processing } => {
1795 cheatcode_call && !pending_processing
1796 }
1797 };
1798
1799 if needs_processing {
1800 let mut expected_revert = std::mem::take(&mut self.expected_revert).unwrap();
1801 let clear_last_frame_gas =
1802 matches!(expected_revert.kind, ExpectedRevertKind::Default);
1803 return match revert_handlers::handle_expect_revert(
1804 cheatcode_call,
1805 false,
1806 self.config.internal_expect_revert,
1807 &expected_revert,
1808 outcome.result.result,
1809 outcome.result.output.clone(),
1810 &self.config.available_artifacts,
1811 ) {
1812 Err(error) => {
1813 trace!(expected=?expected_revert, ?error, status=?outcome.result.result, "Expected revert mismatch");
1814 outcome.result.result = InstructionResult::Revert;
1815 outcome.result.output = error.abi_encode().into();
1816 }
1817 Ok((_, retdata)) => {
1818 expected_revert.actual_count += 1;
1819 if expected_revert.actual_count < expected_revert.count {
1820 self.expected_revert = Some(expected_revert);
1821 }
1822 if clear_last_frame_gas {
1823 self.gas_metering.last_frame_gas = None;
1824 }
1825 outcome.result.result = InstructionResult::Return;
1826 outcome.result.output = retdata;
1827 }
1828 };
1829 }
1830
1831 if let ExpectedRevertKind::Cheatcode { pending_processing } =
1834 &mut self.expected_revert.as_mut().unwrap().kind
1835 {
1836 *pending_processing = false;
1837 }
1838 }
1839 }
1840
1841 if cheatcode_call {
1844 return;
1845 }
1846
1847 let gas = outcome.result.gas;
1850 let frame_gas = crate::Vm::Gas {
1851 gasLimit: gas.limit(),
1852 gasTotalUsed: gas.total_gas_spent(),
1853 gasMemoryUsed: 0,
1854 gasRefunded: gas.refunded(),
1855 gasRemaining: gas.remaining(),
1856 };
1857 self.gas_metering.last_call_gas = Some(frame_gas.clone());
1858 self.gas_metering.last_frame_gas = Some(frame_gas);
1859
1860 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1863 if ecx.journal().depth() > 0
1865 && let Some(mut last_recorded_depth) = recorded_account_diffs_stack.pop()
1866 {
1867 if outcome.result.is_revert() {
1870 for element in &mut *last_recorded_depth {
1871 element.reverted = true;
1872 for storage_access in &mut element.storageAccesses {
1873 storage_access.reverted = true;
1874 }
1875 }
1876 }
1877
1878 if let Some(call_access) = last_recorded_depth.first_mut() {
1879 let curr_depth = ecx.journal().depth();
1884 if call_access.depth == curr_depth as u64
1885 && let Ok(acc) = ecx.journal_mut().load_account(call.target_address)
1886 {
1887 debug_assert!(access_is_call(call_access.kind));
1888 call_access.newBalance = acc.data.info.balance;
1889 call_access.newNonce = acc.data.info.nonce;
1890 }
1891 if let Some(last) = recorded_account_diffs_stack.last_mut() {
1896 last.extend(last_recorded_depth);
1897 } else {
1898 recorded_account_diffs_stack.push(last_recorded_depth);
1899 }
1900 }
1901 }
1902 }
1903
1904 let diag = self.fork_revert_diagnostic.take();
1907
1908 if outcome.result.is_revert() {
1911 if let Some(err) = diag {
1914 outcome.result.output = Error::encode(err.to_error_msg(&self.labels));
1915 }
1916 return;
1917 }
1918
1919 let should_check_emits = self
1931 .expected_emits
1932 .iter()
1933 .any(|(expected, _)| {
1934 let curr_depth = ecx.journal().depth();
1935 expected.depth == curr_depth
1936 }) &&
1937 !call.is_static;
1939 if should_check_emits {
1940 let expected_counts = self
1941 .expected_emits
1942 .iter()
1943 .filter_map(|(expected, count_map)| {
1944 let count = match expected.address {
1945 Some(emitter) => match count_map.get(&emitter) {
1946 Some(log_count) => expected
1947 .log
1948 .as_ref()
1949 .map(|l| log_count.count(l))
1950 .unwrap_or_else(|| log_count.count_unchecked()),
1951 None => 0,
1952 },
1953 None => match &expected.log {
1954 Some(log) => count_map.values().map(|logs| logs.count(log)).sum(),
1955 None => count_map.values().map(|logs| logs.count_unchecked()).sum(),
1956 },
1957 };
1958
1959 (count != expected.count).then_some((expected, count))
1960 })
1961 .collect::<Vec<_>>();
1962
1963 if let Some((expected, _)) = self
1965 .expected_emits
1966 .iter()
1967 .find(|(expected, _)| !expected.found && expected.count > 0)
1968 {
1969 outcome.result.result = InstructionResult::Revert;
1970 let mismatch_error = expected.mismatch_error.clone();
1971 let expected_log = expected.log.clone();
1972 let checks = expected.checks;
1973 let anonymous = expected.anonymous;
1974 let error_msg = mismatch_error
1975 .as_ref()
1976 .map(|mismatch| {
1977 mismatch.to_error_msg(self, checks, expected_log.as_ref(), anonymous)
1978 })
1979 .unwrap_or_else(|| "log != expected log".to_string());
1980 outcome.result.output = error_msg.abi_encode().into();
1981 return;
1982 }
1983
1984 if !expected_counts.is_empty() {
1985 let msg = if outcome.result.is_ok() {
1986 let (expected, count) = expected_counts.first().unwrap();
1987 format!("log emitted {count} times, expected {}", expected.count)
1988 } else {
1989 "expected an emit, but the call reverted instead. \
1990 ensure you're testing the happy path when using `expectEmit`"
1991 .to_string()
1992 };
1993
1994 outcome.result.result = InstructionResult::Revert;
1995 outcome.result.output = Error::encode(msg);
1996 return;
1997 }
1998
1999 self.expected_emits.clear()
2003 }
2004
2005 if let TxKind::Call(test_contract) = ecx.tx().kind() {
2008 if ecx.db().is_forked_mode()
2011 && outcome.result.result == InstructionResult::Stop
2012 && call.target_address != test_contract
2013 {
2014 self.fork_revert_diagnostic =
2015 ecx.db().diagnose_revert(call.target_address, ecx.journal().evm_state());
2016 }
2017 }
2018
2019 if ecx.journal().depth() == 0 {
2021 if outcome.result.is_revert() {
2025 return;
2026 }
2027
2028 for (address, calldatas) in &self.expected_calls {
2033 for (calldata, (expected, actual_count)) in calldatas {
2035 let ExpectedCallData { gas, min_gas, value, count, call_type } = expected;
2037
2038 let failed = match call_type {
2039 ExpectedCallType::Count => *count != *actual_count,
2043 ExpectedCallType::NonCount => *count > *actual_count,
2048 };
2049 if failed {
2050 let expected_values = [
2051 Some(format!("data {}", hex::encode_prefixed(calldata))),
2052 value.as_ref().map(|v| format!("value {v}")),
2053 gas.map(|g| format!("gas {g}")),
2054 min_gas.map(|g| format!("minimum gas {g}")),
2055 ]
2056 .into_iter()
2057 .flatten()
2058 .join(", ");
2059 let but = if outcome.result.is_ok() {
2060 let s = if *actual_count == 1 { "" } else { "s" };
2061 format!("was called {actual_count} time{s}")
2062 } else {
2063 "the call reverted instead; \
2064 ensure you're testing the happy path when using `expectCall`"
2065 .to_string()
2066 };
2067 let s = if *count == 1 { "" } else { "s" };
2068 let msg = format!(
2069 "expected call to {address} with {expected_values} \
2070 to be called {count} time{s}, but {but}"
2071 );
2072 outcome.result.result = InstructionResult::Revert;
2073 outcome.result.output = Error::encode(msg);
2074
2075 return;
2076 }
2077 }
2078 }
2079
2080 for (expected, _) in &mut self.expected_emits {
2084 if expected.count == 0 && !expected.found {
2085 expected.found = true;
2086 }
2087 }
2088 self.expected_emits.retain(|(expected, _)| !expected.found);
2089 if !self.expected_emits.is_empty() {
2091 let msg = if outcome.result.is_ok() {
2092 "expected an emit, but no logs were emitted afterwards. \
2093 you might have mismatched events or not enough events were emitted"
2094 } else {
2095 "expected an emit, but the call reverted instead. \
2096 ensure you're testing the happy path when using `expectEmit`"
2097 };
2098 outcome.result.result = InstructionResult::Revert;
2099 outcome.result.output = Error::encode(msg);
2100 return;
2101 }
2102
2103 if let Some(expected_create) = self.expected_creates.first() {
2105 let msg = format!(
2106 "expected {} call by address {} for bytecode {} but not found",
2107 expected_create.create_scheme,
2108 hex::encode_prefixed(expected_create.deployer),
2109 hex::encode_prefixed(&expected_create.bytecode),
2110 );
2111 outcome.result.result = InstructionResult::Revert;
2112 outcome.result.output = Error::encode(msg);
2113 }
2114 }
2115 }
2116
2117 fn create(
2118 &mut self,
2119 ecx: &mut FoundryContextFor<'_, FEN>,
2120 mut input: &mut CreateInputs,
2121 ) -> Option<CreateOutcome> {
2122 if let Some(spec_id) = self.execution_evm_version {
2124 ecx.cfg_mut().set_spec_and_mainnet_gas_params(spec_id);
2125 }
2126
2127 let gas = Gas::new(input.gas_limit());
2128 if self.intercept_next_create_call {
2130 self.intercept_next_create_call = false;
2132
2133 let output = input.init_code();
2135
2136 return Some(CreateOutcome {
2138 result: InterpreterResult { result: InstructionResult::Revert, output, gas },
2139 address: None,
2140 });
2141 }
2142
2143 let curr_depth = ecx.journal().depth();
2144
2145 if let Some(prank) = &self.get_prank(curr_depth)
2147 && curr_depth >= prank.depth
2148 && input.caller() == prank.prank_caller
2149 {
2150 let prank_applied = if curr_depth == prank.depth {
2152 let _ = journaled_account(ecx, prank.new_caller);
2154 input.set_caller(prank.new_caller);
2155 true
2156 } else {
2157 false
2158 };
2159
2160 let prank_applied = if let Some(new_origin) = prank.new_origin {
2162 ecx.tx_mut().set_caller(new_origin);
2163 true
2164 } else {
2165 prank_applied
2166 };
2167
2168 if prank_applied && let Some(applied_prank) = prank.first_time_applied() {
2170 self.pranks.insert(curr_depth, applied_prank);
2171 }
2172 }
2173
2174 self.apply_accesslist(ecx);
2176
2177 if let Some(broadcast) = &mut self.broadcast
2179 && curr_depth >= broadcast.depth
2180 && input.caller() == broadcast.original_caller
2181 {
2182 if let Err(err) = ecx.journal_mut().load_account(broadcast.new_origin) {
2183 return Some(CreateOutcome {
2184 result: InterpreterResult {
2185 result: InstructionResult::Revert,
2186 output: Error::encode(err),
2187 gas,
2188 },
2189 address: None,
2190 });
2191 }
2192
2193 ecx.tx_mut().set_caller(broadcast.new_origin);
2194
2195 if curr_depth == broadcast.depth || broadcast.deploy_from_code {
2196 broadcast.deploy_from_code = false;
2198
2199 input.set_caller(broadcast.new_origin);
2200
2201 let rpc = ecx.db().active_fork_url();
2202 let account = &ecx.journal().evm_state()[&broadcast.new_origin];
2203 let mut tx_req = TransactionRequestFor::<FEN>::default()
2204 .with_from(broadcast.new_origin)
2205 .with_kind(TxKind::Create)
2206 .with_value(input.value())
2207 .with_input(input.init_code())
2208 .with_nonce(account.info.nonce);
2209 if let Some(fee_token) = self.config.fee_token {
2210 tx_req.set_fee_token(fee_token);
2211 }
2212 self.broadcastable_transactions.push_back(BroadcastableTransaction {
2213 rpc,
2214 transaction: TransactionMaybeSigned::new(tx_req),
2215 });
2216
2217 input.log_debug(self, &input.scheme().unwrap_or(CreateScheme::Create));
2218 }
2219 }
2220
2221 let address = input.allow_cheatcodes(self, ecx);
2223
2224 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
2226 recorded_account_diffs_stack.push(vec![AccountAccess {
2227 chainInfo: crate::Vm::ChainInfo {
2228 forkId: ecx.db().active_fork_id().unwrap_or_default(),
2229 chainId: U256::from(ecx.cfg().chain_id()),
2230 },
2231 accessor: input.caller(),
2232 account: address,
2233 kind: crate::Vm::AccountAccessKind::Create,
2234 initialized: true,
2235 oldBalance: U256::ZERO, newBalance: U256::ZERO, oldNonce: 0, newNonce: 1, value: input.value(),
2240 data: input.init_code(),
2241 reverted: false,
2242 deployedCode: Bytes::new(), storageAccesses: vec![], depth: curr_depth as u64,
2245 }]);
2246 }
2247
2248 None
2249 }
2250
2251 fn create_end(
2252 &mut self,
2253 ecx: &mut FoundryContextFor<'_, FEN>,
2254 call: &CreateInputs,
2255 outcome: &mut CreateOutcome,
2256 ) {
2257 let call = Some(call);
2258 let curr_depth = ecx.journal().depth();
2259
2260 if let Some(prank) = &self.get_prank(curr_depth)
2262 && curr_depth == prank.depth
2263 {
2264 ecx.tx_mut().set_caller(prank.prank_origin);
2265
2266 if prank.single_call {
2268 std::mem::take(&mut self.pranks);
2269 }
2270 }
2271
2272 if let Some(broadcast) = &self.broadcast
2274 && curr_depth == broadcast.depth
2275 {
2276 ecx.tx_mut().set_caller(broadcast.original_origin);
2277
2278 if broadcast.single_call {
2280 std::mem::take(&mut self.broadcast);
2281 }
2282 }
2283
2284 if let Some(expected_revert) = &mut self.expected_revert {
2286 if outcome.result.is_revert()
2298 && expected_revert.reverter.is_some()
2299 && expected_revert.reverted_by.is_none()
2300 && let Some(addr) = outcome.address
2301 {
2302 expected_revert.reverted_by = Some(addr);
2303 }
2304
2305 if curr_depth <= expected_revert.depth
2306 && matches!(expected_revert.kind, ExpectedRevertKind::Default)
2307 {
2308 let mut expected_revert = std::mem::take(&mut self.expected_revert).unwrap();
2309 return match revert_handlers::handle_expect_revert(
2310 false,
2311 true,
2312 self.config.internal_expect_revert,
2313 &expected_revert,
2314 outcome.result.result,
2315 outcome.result.output.clone(),
2316 &self.config.available_artifacts,
2317 ) {
2318 Ok((address, retdata)) => {
2319 expected_revert.actual_count += 1;
2320 if expected_revert.actual_count < expected_revert.count {
2321 expected_revert.reverted_by = None;
2323 self.expected_revert = Some(expected_revert.clone());
2324 }
2325
2326 outcome.result.result = InstructionResult::Return;
2327 outcome.result.output = retdata;
2328 outcome.address = address;
2329 self.gas_metering.last_frame_gas = None;
2330 }
2331 Err(err) => {
2332 outcome.result.result = InstructionResult::Revert;
2333 outcome.result.output = err.abi_encode().into();
2334 }
2335 };
2336 }
2337 }
2338
2339 if curr_depth > 0 {
2340 let gas = outcome.result.gas;
2343 self.gas_metering.last_frame_gas = Some(crate::Vm::Gas {
2344 gasLimit: gas.limit(),
2345 gasTotalUsed: gas.total_gas_spent(),
2346 gasMemoryUsed: 0,
2347 gasRefunded: gas.refunded(),
2348 gasRemaining: gas.remaining(),
2349 });
2350 }
2351
2352 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
2355 if curr_depth > 0
2357 && let Some(last_depth) = &mut recorded_account_diffs_stack.pop()
2358 {
2359 if outcome.result.is_revert() {
2362 for element in &mut *last_depth {
2363 element.reverted = true;
2364 for storage_access in &mut element.storageAccesses {
2365 storage_access.reverted = true;
2366 }
2367 }
2368 }
2369
2370 if let Some(create_access) = last_depth.first_mut() {
2371 let depth = ecx.journal().depth();
2376 if create_access.depth == depth as u64 {
2377 debug_assert_eq!(
2378 create_access.kind as u8,
2379 crate::Vm::AccountAccessKind::Create as u8
2380 );
2381 if let Some(address) = outcome.address
2382 && let Ok(created_acc) = ecx.journal_mut().load_account(address)
2383 {
2384 create_access.newBalance = created_acc.data.info.balance;
2385 create_access.newNonce = created_acc.data.info.nonce;
2386 create_access.deployedCode = created_acc
2387 .data
2388 .info
2389 .code
2390 .clone()
2391 .unwrap_or_default()
2392 .original_bytes();
2393 }
2394 }
2395 if let Some(last) = recorded_account_diffs_stack.last_mut() {
2400 last.append(last_depth);
2401 } else {
2402 recorded_account_diffs_stack.push(last_depth.clone());
2403 }
2404 }
2405 }
2406 }
2407
2408 if !self.expected_creates.is_empty()
2410 && let (Some(address), Some(call)) = (outcome.address, call)
2411 && let Ok(created_acc) = ecx.journal_mut().load_account(address)
2412 {
2413 let bytecode = created_acc.data.info.code.clone().unwrap_or_default().original_bytes();
2414 if let Some((index, _)) =
2415 self.expected_creates.iter().find_position(|expected_create| {
2416 expected_create.deployer == call.caller()
2417 && expected_create.create_scheme.eq(call.scheme().into())
2418 && expected_create.bytecode == bytecode
2419 })
2420 {
2421 self.expected_creates.swap_remove(index);
2422 }
2423 }
2424 }
2425}
2426
2427impl<FEN: FoundryEvmNetwork> InspectorExt for Cheatcodes<FEN> {
2428 fn should_use_create2_factory(&mut self, depth: usize, inputs: &CreateInputs) -> bool {
2429 let target_depth = if let Some(prank) = &self.get_prank(depth) {
2430 prank.depth
2431 } else if let Some(broadcast) = &self.broadcast {
2432 broadcast.depth
2433 } else {
2434 1
2435 };
2436
2437 if depth != target_depth {
2438 return false;
2439 }
2440
2441 match inputs.scheme() {
2442 CreateScheme::Create2 { .. } => {
2443 self.broadcast.is_some() || self.config.always_use_create_2_factory
2444 }
2445 CreateScheme::Create => self.config.batch_rewrite_creates && self.broadcast.is_some(),
2446 _ => false,
2447 }
2448 }
2449
2450 fn create2_deployer(&self) -> Address {
2451 self.config.evm_opts.create2_deployer
2452 }
2453}
2454
2455impl<FEN: FoundryEvmNetwork> Cheatcodes<FEN> {
2456 #[cold]
2457 fn meter_gas(&mut self, interpreter: &mut Interpreter) {
2458 if let Some(paused_gas) = self.gas_metering.paused_frames.last() {
2459 let memory = *interpreter.gas.memory();
2462 interpreter.gas = *paused_gas;
2463 interpreter.gas.memory_mut().words_num = memory.words_num;
2464 interpreter.gas.memory_mut().expansion_cost = memory.expansion_cost;
2465 } else {
2466 self.gas_metering.paused_frames.push(interpreter.gas);
2468 }
2469 }
2470
2471 #[cold]
2472 fn meter_gas_record(
2473 &mut self,
2474 interpreter: &mut Interpreter,
2475 ecx: &mut FoundryContextFor<'_, FEN>,
2476 ) {
2477 if interpreter.bytecode.action.as_ref().and_then(|i| i.instruction_result()).is_none() {
2478 self.gas_metering.gas_records.iter_mut().for_each(|record| {
2479 let curr_depth = ecx.journal().depth();
2480 if curr_depth == record.depth {
2481 if self.gas_metering.last_gas_used != 0 {
2484 let gas_diff = interpreter
2485 .gas
2486 .total_gas_spent()
2487 .saturating_sub(self.gas_metering.last_gas_used);
2488 record.gas_used = record.gas_used.saturating_add(gas_diff);
2489 }
2490
2491 self.gas_metering.last_gas_used = interpreter.gas.total_gas_spent();
2494 }
2495 });
2496 }
2497 }
2498
2499 #[cold]
2500 fn meter_gas_end(&mut self, interpreter: &mut Interpreter) {
2501 if let Some(interpreter_action) = interpreter.bytecode.action.as_ref()
2503 && will_exit(interpreter_action)
2504 {
2505 self.gas_metering.paused_frames.pop();
2506 }
2507 }
2508
2509 #[cold]
2510 const fn meter_gas_reset(&mut self, interpreter: &mut Interpreter) {
2511 let mut gas = Gas::new(interpreter.gas.limit());
2512 gas.memory_mut().words_num = interpreter.gas.memory().words_num;
2513 gas.memory_mut().expansion_cost = interpreter.gas.memory().expansion_cost;
2514 interpreter.gas = gas;
2515 self.gas_metering.reset = false;
2516 }
2517
2518 #[cold]
2519 fn meter_gas_check(&mut self, interpreter: &mut Interpreter) {
2520 if let Some(interpreter_action) = interpreter.bytecode.action.as_ref()
2521 && will_exit(interpreter_action)
2522 {
2523 if interpreter.gas.total_gas_spent()
2527 < u64::try_from(interpreter.gas.refunded()).unwrap_or_default()
2528 {
2529 interpreter.gas = Gas::new(interpreter.gas.limit());
2530 }
2531 }
2532 }
2533
2534 #[cold]
2548 fn apply_env_overrides(&mut self, interpreter: &mut Interpreter, fork_id: Option<U256>) {
2549 let Some(env_overrides) = self.env_overrides.get_mut(&fork_id) else { return };
2550 let Some(opcode) = env_overrides.pending_opcode.take() else { return };
2551 match opcode {
2552 op::BASEFEE => {
2553 if let Some(basefee) = env_overrides.basefee {
2554 Self::replace_top_of_stack(interpreter, U256::from(basefee));
2556 }
2557 }
2558 op::GASPRICE => {
2559 if let Some(gas_price) = env_overrides.gas_price {
2560 Self::replace_top_of_stack(interpreter, U256::from(gas_price));
2562 }
2563 }
2564 op::BLOBHASH => {
2565 let blob_hashes = env_overrides.blob_hashes.clone();
2566 let blobhash_index = env_overrides.pending_blobhash_index.take();
2567 if let Some(ref blob_hashes) = blob_hashes
2568 && let Some(index) = blobhash_index
2569 {
2570 let hash = blob_hashes.get(index as usize).copied().unwrap_or_default();
2573 Self::replace_top_of_stack(interpreter, hash.into());
2574 }
2575 }
2576 _ => {}
2577 }
2578 }
2579
2580 fn replace_top_of_stack(interpreter: &mut Interpreter, value: U256) {
2588 if interpreter.stack.pop().is_err() {
2589 debug_assert!(false, "env override expected opcode result on stack");
2590 return;
2591 }
2592 let _ = interpreter.stack.push(value);
2593 }
2594
2595 #[cold]
2603 fn arbitrary_storage_end(
2604 &mut self,
2605 interpreter: &mut Interpreter,
2606 ecx: &mut FoundryContextFor<'_, FEN>,
2607 ) {
2608 let (key, target_address) = if interpreter.bytecode.opcode() == op::SLOAD {
2609 (try_or_return!(interpreter.stack.peek(0)), interpreter.input.target_address)
2610 } else {
2611 return;
2612 };
2613
2614 let Some(value) = ecx.sload(target_address, key) else {
2615 return;
2616 };
2617
2618 if (value.is_cold && value.data.is_zero())
2619 || self.should_overwrite_arbitrary_storage(&target_address, key)
2620 {
2621 if self.has_arbitrary_storage(&target_address) {
2622 let arbitrary_value = self
2623 .cached_arbitrary_storage_value(target_address, key)
2624 .unwrap_or_else(|| self.rng().random());
2625 self.arbitrary_storage.as_mut().unwrap().save(
2626 ecx,
2627 target_address,
2628 key,
2629 arbitrary_value,
2630 );
2631 } else if self.is_arbitrary_storage_copy(&target_address) {
2632 let arbitrary_value = self.rng().random();
2633 self.arbitrary_storage.as_mut().unwrap().copy(
2634 ecx,
2635 target_address,
2636 key,
2637 arbitrary_value,
2638 );
2639 }
2640 }
2641 }
2642
2643 #[cold]
2645 fn record_accesses(&mut self, interpreter: &mut Interpreter) {
2646 let access = &mut self.accesses;
2647 match interpreter.bytecode.opcode() {
2648 op::SLOAD => {
2649 let key = try_or_return!(interpreter.stack.peek(0));
2650 access.record_read(interpreter.input.target_address, key);
2651 }
2652 op::SSTORE => {
2653 let key = try_or_return!(interpreter.stack.peek(0));
2654 access.record_write(interpreter.input.target_address, key);
2655 }
2656 _ => {}
2657 }
2658 }
2659
2660 #[cold]
2661 fn record_state_diffs(
2662 &mut self,
2663 interpreter: &mut Interpreter,
2664 ecx: &mut FoundryContextFor<'_, FEN>,
2665 ) {
2666 let Some(account_accesses) = &mut self.recorded_account_diffs_stack else { return };
2667 match interpreter.bytecode.opcode() {
2668 op::SELFDESTRUCT => {
2669 let Some(last) = account_accesses.last_mut() else { return };
2671
2672 let target = try_or_return!(interpreter.stack.peek(0));
2674 let target = Address::from_word(B256::from(target));
2675 let (initialized, old_balance, old_nonce) = ecx
2676 .journal_mut()
2677 .load_account(target)
2678 .map(|account| {
2679 (
2680 account.data.info.exists(),
2681 account.data.info.balance,
2682 account.data.info.nonce,
2683 )
2684 })
2685 .unwrap_or_default();
2686
2687 let value = ecx
2689 .balance(interpreter.input.target_address)
2690 .map(|b| b.data)
2691 .unwrap_or(U256::ZERO);
2692
2693 last.push(crate::Vm::AccountAccess {
2695 chainInfo: crate::Vm::ChainInfo {
2696 forkId: ecx.db().active_fork_id().unwrap_or_default(),
2697 chainId: U256::from(ecx.cfg().chain_id()),
2698 },
2699 accessor: interpreter.input.target_address,
2700 account: target,
2701 kind: crate::Vm::AccountAccessKind::SelfDestruct,
2702 initialized,
2703 oldBalance: old_balance,
2704 newBalance: old_balance + value,
2705 oldNonce: old_nonce,
2706 newNonce: old_nonce, value,
2708 data: Bytes::new(),
2709 reverted: false,
2710 deployedCode: Bytes::new(),
2711 storageAccesses: vec![],
2712 depth: ecx
2713 .journal()
2714 .depth()
2715 .try_into()
2716 .expect("journaled state depth exceeds u64"),
2717 });
2718 }
2719
2720 op::SLOAD => {
2721 let Some(last) = account_accesses.last_mut() else { return };
2722
2723 let key = try_or_return!(interpreter.stack.peek(0));
2724 let address = interpreter.input.target_address;
2725
2726 let checkpoint = ecx.journal_mut().checkpoint();
2730 let present_value =
2731 ecx.sload(address, key).map(|previous| previous.data).unwrap_or_default();
2732 ecx.journal_mut().checkpoint_revert(checkpoint);
2733 let access = crate::Vm::StorageAccess {
2734 account: interpreter.input.target_address,
2735 slot: key.into(),
2736 isWrite: false,
2737 previousValue: present_value.into(),
2738 newValue: present_value.into(),
2739 reverted: false,
2740 };
2741 let curr_depth =
2742 ecx.journal().depth().try_into().expect("journaled state depth exceeds u64");
2743 append_storage_access(last, access, curr_depth);
2744 }
2745 op::SSTORE => {
2746 let Some(last) = account_accesses.last_mut() else { return };
2747
2748 let key = try_or_return!(interpreter.stack.peek(0));
2749 let value = try_or_return!(interpreter.stack.peek(1));
2750 let address = interpreter.input.target_address;
2751 let checkpoint = ecx.journal_mut().checkpoint();
2755 let previous_value =
2756 ecx.sload(address, key).map(|previous| previous.data).unwrap_or_default();
2757 ecx.journal_mut().checkpoint_revert(checkpoint);
2758
2759 let access = crate::Vm::StorageAccess {
2760 account: address,
2761 slot: key.into(),
2762 isWrite: true,
2763 previousValue: previous_value.into(),
2764 newValue: value.into(),
2765 reverted: false,
2766 };
2767 let curr_depth =
2768 ecx.journal().depth().try_into().expect("journaled state depth exceeds u64");
2769 append_storage_access(last, access, curr_depth);
2770 }
2771
2772 op::EXTCODECOPY | op::EXTCODESIZE | op::EXTCODEHASH | op::BALANCE => {
2774 let kind = match interpreter.bytecode.opcode() {
2775 op::EXTCODECOPY => crate::Vm::AccountAccessKind::Extcodecopy,
2776 op::EXTCODESIZE => crate::Vm::AccountAccessKind::Extcodesize,
2777 op::EXTCODEHASH => crate::Vm::AccountAccessKind::Extcodehash,
2778 op::BALANCE => crate::Vm::AccountAccessKind::Balance,
2779 _ => unreachable!(),
2780 };
2781 let address =
2782 Address::from_word(B256::from(try_or_return!(interpreter.stack.peek(0))));
2783 let checkpoint = ecx.journal_mut().checkpoint();
2784 let (initialized, balance, nonce) = ecx
2785 .journal_mut()
2786 .load_account(address)
2787 .map(|acc| (acc.data.info.exists(), acc.data.info.balance, acc.data.info.nonce))
2788 .unwrap_or_default();
2789 ecx.journal_mut().checkpoint_revert(checkpoint);
2790 let curr_depth =
2791 ecx.journal().depth().try_into().expect("journaled state depth exceeds u64");
2792 let account_access = crate::Vm::AccountAccess {
2793 chainInfo: crate::Vm::ChainInfo {
2794 forkId: ecx.db().active_fork_id().unwrap_or_default(),
2795 chainId: U256::from(ecx.cfg().chain_id()),
2796 },
2797 accessor: interpreter.input.target_address,
2798 account: address,
2799 kind,
2800 initialized,
2801 oldBalance: balance,
2802 newBalance: balance,
2803 oldNonce: nonce,
2804 newNonce: nonce, value: U256::ZERO,
2806 data: Bytes::new(),
2807 reverted: false,
2808 deployedCode: Bytes::new(),
2809 storageAccesses: vec![],
2810 depth: curr_depth,
2811 };
2812 if let Some(last) = account_accesses.last_mut() {
2815 last.push(account_access);
2816 } else {
2817 account_accesses.push(vec![account_access]);
2818 }
2819 }
2820 _ => {}
2821 }
2822 }
2823
2824 #[cold]
2829 fn check_mem_opcodes(&self, interpreter: &mut Interpreter, depth: u64) {
2830 let Some(ranges) = self.allowed_mem_writes.get(&depth) else {
2831 return;
2832 };
2833
2834 macro_rules! mem_opcode_match {
2843 ($(($opcode:ident, $offset_depth:expr, $size_depth:expr, $writes:expr)),* $(,)?) => {
2844 match interpreter.bytecode.opcode() {
2845 op::MSTORE => {
2850 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2852
2853 if !ranges.iter().any(|range| {
2856 range.contains(&offset) && range.contains(&(offset + 31))
2857 }) {
2858 let value = try_or_return!(interpreter.stack.peek(1)).to_be_bytes::<32>();
2863 if value[..SELECTOR_LEN] == stopExpectSafeMemoryCall::SELECTOR {
2864 return
2865 }
2866
2867 disallowed_mem_write(offset, 32, interpreter, ranges);
2868 return
2869 }
2870 }
2871 op::MSTORE8 => {
2872 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2874
2875 if !ranges.iter().any(|range| range.contains(&offset)) {
2878 disallowed_mem_write(offset, 1, interpreter, ranges);
2879 return
2880 }
2881 }
2882
2883 op::MLOAD => {
2888 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2890
2891 if offset >= interpreter.memory.size() as u64 && !ranges.iter().any(|range| {
2895 range.contains(&offset) && range.contains(&(offset + 31))
2896 }) {
2897 disallowed_mem_write(offset, 32, interpreter, ranges);
2898 return
2899 }
2900 }
2901
2902 op::CALL => {
2907 let dest_offset = try_or_return!(interpreter.stack.peek(5)).saturating_to::<u64>();
2909
2910 let size = try_or_return!(interpreter.stack.peek(6)).saturating_to::<u64>();
2912
2913 let fail_cond = !ranges.iter().any(|range| {
2917 range.contains(&dest_offset) &&
2918 range.contains(&(dest_offset + size.saturating_sub(1)))
2919 });
2920
2921 if fail_cond {
2924 let to = Address::from_word(try_or_return!(interpreter.stack.peek(1)).to_be_bytes::<32>().into());
2928 if to == CHEATCODE_ADDRESS {
2929 let args_offset = try_or_return!(interpreter.stack.peek(3)).saturating_to::<usize>();
2930 let args_size = try_or_return!(interpreter.stack.peek(4)).saturating_to::<usize>();
2931 let memory_word = interpreter.memory.slice_len(args_offset, args_size);
2932 if memory_word[..SELECTOR_LEN] == stopExpectSafeMemoryCall::SELECTOR {
2933 return
2934 }
2935 }
2936
2937 disallowed_mem_write(dest_offset, size, interpreter, ranges);
2938 return
2939 }
2940 }
2941
2942 $(op::$opcode => {
2943 let dest_offset = try_or_return!(interpreter.stack.peek($offset_depth)).saturating_to::<u64>();
2945
2946 let size = try_or_return!(interpreter.stack.peek($size_depth)).saturating_to::<u64>();
2948
2949 let fail_cond = !ranges.iter().any(|range| {
2953 range.contains(&dest_offset) &&
2954 range.contains(&(dest_offset + size.saturating_sub(1)))
2955 }) && ($writes ||
2956 [dest_offset, (dest_offset + size).saturating_sub(1)].into_iter().any(|offset| {
2957 offset >= interpreter.memory.size() as u64
2958 })
2959 );
2960
2961 if fail_cond {
2964 disallowed_mem_write(dest_offset, size, interpreter, ranges);
2965 return
2966 }
2967 })*
2968
2969 _ => {}
2970 }
2971 }
2972 }
2973
2974 mem_opcode_match!(
2977 (CALLDATACOPY, 0, 2, true),
2978 (CODECOPY, 0, 2, true),
2979 (RETURNDATACOPY, 0, 2, true),
2980 (EXTCODECOPY, 1, 3, true),
2981 (CALLCODE, 5, 6, true),
2982 (STATICCALL, 4, 5, true),
2983 (DELEGATECALL, 4, 5, true),
2984 (KECCAK256, 0, 1, false),
2985 (LOG0, 0, 1, false),
2986 (LOG1, 0, 1, false),
2987 (LOG2, 0, 1, false),
2988 (LOG3, 0, 1, false),
2989 (LOG4, 0, 1, false),
2990 (CREATE, 1, 2, false),
2991 (CREATE2, 1, 2, false),
2992 (RETURN, 0, 1, false),
2993 (REVERT, 0, 1, false),
2994 );
2995 }
2996
2997 #[cold]
2998 fn set_gas_limit_type(&mut self, interpreter: &mut Interpreter) {
2999 match interpreter.bytecode.opcode() {
3000 op::CREATE2 => self.dynamic_gas_limit = true,
3001 op::CALL => {
3002 self.dynamic_gas_limit =
3005 try_or_return!(interpreter.stack.peek(0)) >= interpreter.gas.remaining() - 100
3006 }
3007 _ => self.dynamic_gas_limit = false,
3008 }
3009 }
3010}
3011
3012fn disallowed_mem_write(
3018 dest_offset: u64,
3019 size: u64,
3020 interpreter: &mut Interpreter,
3021 ranges: &[Range<u64>],
3022) {
3023 let revert_string = format!(
3024 "memory write at offset 0x{:02X} of size 0x{:02X} not allowed; safe range: {}",
3025 dest_offset,
3026 size,
3027 ranges.iter().map(|r| format!("[0x{:02X}, 0x{:02X})", r.start, r.end)).join(" U ")
3028 );
3029
3030 interpreter.bytecode.set_action(InterpreterAction::new_return(
3031 InstructionResult::Revert,
3032 Bytes::from(revert_string.into_bytes()),
3033 interpreter.gas,
3034 ));
3035}
3036
3037const fn access_is_call(kind: crate::Vm::AccountAccessKind) -> bool {
3039 matches!(
3040 kind,
3041 crate::Vm::AccountAccessKind::Call
3042 | crate::Vm::AccountAccessKind::StaticCall
3043 | crate::Vm::AccountAccessKind::CallCode
3044 | crate::Vm::AccountAccessKind::DelegateCall
3045 )
3046}
3047
3048fn record_logs(recorded_logs: &mut Option<Vec<Vm::Log>>, log: &Log) {
3050 if let Some(storage_recorded_logs) = recorded_logs {
3051 storage_recorded_logs.push(Vm::Log {
3052 topics: log.data.topics().to_vec(),
3053 data: log.data.data.clone(),
3054 emitter: log.address,
3055 });
3056 }
3057}
3058
3059fn append_storage_access(
3061 last: &mut Vec<AccountAccess>,
3062 storage_access: crate::Vm::StorageAccess,
3063 storage_depth: u64,
3064) {
3065 if !last.is_empty() && last.first().unwrap().depth < storage_depth {
3067 if last.len() == 1 {
3073 last.first_mut().unwrap().storageAccesses.push(storage_access);
3074 } else {
3075 let last_record = last.last_mut().unwrap();
3076 if last_record.kind as u8 == crate::Vm::AccountAccessKind::Resume as u8 {
3077 last_record.storageAccesses.push(storage_access);
3078 } else {
3079 let entry = last.first().unwrap();
3080 let resume_record = crate::Vm::AccountAccess {
3081 chainInfo: crate::Vm::ChainInfo {
3082 forkId: entry.chainInfo.forkId,
3083 chainId: entry.chainInfo.chainId,
3084 },
3085 accessor: entry.accessor,
3086 account: entry.account,
3087 kind: crate::Vm::AccountAccessKind::Resume,
3088 initialized: entry.initialized,
3089 storageAccesses: vec![storage_access],
3090 reverted: entry.reverted,
3091 oldBalance: U256::ZERO,
3093 newBalance: U256::ZERO,
3094 oldNonce: 0,
3095 newNonce: 0,
3096 value: U256::ZERO,
3097 data: Bytes::new(),
3098 deployedCode: Bytes::new(),
3099 depth: entry.depth,
3100 };
3101 last.push(resume_record);
3102 }
3103 }
3104 }
3105}
3106
3107const fn cheatcode_of<T: spec::CheatcodeDef>(_: &T) -> &'static spec::Cheatcode<'static> {
3109 T::CHEATCODE
3110}
3111
3112fn cheatcode_name(cheat: &spec::Cheatcode<'static>) -> &'static str {
3113 cheat.func.signature.split('(').next().unwrap()
3114}
3115
3116const fn cheatcode_id(cheat: &spec::Cheatcode<'static>) -> &'static str {
3117 cheat.func.id
3118}
3119
3120const fn cheatcode_signature(cheat: &spec::Cheatcode<'static>) -> &'static str {
3121 cheat.func.signature
3122}
3123
3124fn apply_dispatch<FEN: FoundryEvmNetwork>(
3126 calls: &Vm::VmCalls,
3127 ccx: &mut CheatsCtxt<'_, '_, FEN>,
3128 executor: &mut dyn CheatcodesExecutor<FEN>,
3129) -> Result {
3130 macro_rules! get_cheatcode {
3132 ($($variant:ident),*) => {
3133 match calls {
3134 $(Vm::VmCalls::$variant(cheat) => cheatcode_of(cheat),)*
3135 }
3136 };
3137 }
3138 let cheat = vm_calls!(get_cheatcode);
3139
3140 let _guard = debug_span!(target: "cheatcodes", "apply", id = %cheatcode_id(cheat)).entered();
3141 trace!(target: "cheatcodes", cheat = %cheatcode_signature(cheat), "applying");
3142
3143 if let spec::Status::Deprecated(replacement) = cheat.status {
3144 ccx.state.deprecated.insert(cheatcode_signature(cheat), replacement);
3145 }
3146
3147 macro_rules! dispatch {
3149 ($($variant:ident),*) => {
3150 match calls {
3151 $(Vm::VmCalls::$variant(cheat) => Cheatcode::apply_full(cheat, ccx, executor),)*
3152 }
3153 };
3154 }
3155 let mut result = vm_calls!(dispatch);
3156
3157 if let Err(e) = &mut result
3159 && e.is_str()
3160 {
3161 let name = cheatcode_name(cheat);
3162 if !name.contains("assert") && name != "rpcUrl" {
3166 *e = fmt_err!("vm.{name}: {e}");
3167 }
3168 }
3169
3170 trace!(
3171 target: "cheatcodes",
3172 return = %match &result {
3173 Ok(b) => hex::encode(b),
3174 Err(e) => e.to_string(),
3175 }
3176 );
3177
3178 result
3179}
3180
3181const fn will_exit(action: &InterpreterAction) -> bool {
3183 match action {
3184 InterpreterAction::Return(result) => {
3185 result.result.is_ok_or_revert() || result.result.is_halt()
3186 }
3187 _ => false,
3188 }
3189}
3190
3191#[cfg(test)]
3192mod tests {
3193 use super::*;
3194
3195 fn cheats(flag: bool, broadcast: Option<Broadcast>) -> Cheatcodes {
3196 let config = CheatsConfig { batch_rewrite_creates: flag, ..Default::default() };
3197 let mut cheats = Cheatcodes::new(Arc::new(config));
3198 cheats.broadcast = broadcast;
3199 cheats
3200 }
3201
3202 fn create_inputs() -> CreateInputs {
3203 CreateInputs::new(Address::ZERO, CreateScheme::Create, U256::ZERO, Bytes::new(), 100_000, 0)
3204 }
3205
3206 fn broadcast_at(depth: usize) -> Broadcast {
3207 Broadcast { depth, ..Default::default() }
3208 }
3209
3210 #[test]
3211 fn flag_off_with_broadcast_returns_false() {
3212 let mut cheats = cheats(false, Some(broadcast_at(1)));
3213 assert!(!cheats.should_use_create2_factory(1, &create_inputs()));
3214 }
3215
3216 #[test]
3217 fn flag_on_without_broadcast_returns_false() {
3218 let mut cheats = cheats(true, None);
3219 assert!(!cheats.should_use_create2_factory(1, &create_inputs()));
3220 }
3221
3222 #[test]
3223 fn flag_on_with_broadcast_depth_mismatch_returns_false() {
3224 let mut cheats = cheats(true, Some(broadcast_at(2)));
3225 assert!(!cheats.should_use_create2_factory(1, &create_inputs()));
3226 }
3227
3228 #[test]
3229 fn flag_on_with_broadcast_depth_match_returns_true() {
3230 let mut cheats = cheats(true, Some(broadcast_at(1)));
3231 assert!(cheats.should_use_create2_factory(1, &create_inputs()));
3232 }
3233
3234 #[test]
3235 fn default_cheatcodes_have_no_opcode_hooks() {
3236 let cheats = Cheatcodes::<EthEvmNetwork>::new(Arc::default());
3237 assert!(!cheats.has_step_hooks());
3238 assert!(!cheats.has_step_end_hooks());
3239 assert!(!cheats.has_log_hooks());
3240 }
3241
3242 #[test]
3243 fn active_cheatcode_state_enables_opcode_hooks() {
3244 let mut cheats = Cheatcodes::<EthEvmNetwork>::new(Arc::default());
3245
3246 cheats.recording_accesses = true;
3247 assert!(cheats.has_step_hooks());
3248 assert!(!cheats.has_step_end_hooks());
3249 assert!(cheats.has_recording_accesses_only_step_hook());
3250
3251 cheats.recording_accesses = false;
3252 cheats.gas_metering.touched = true;
3253 assert!(!cheats.has_step_hooks());
3254 assert!(cheats.has_step_end_hooks());
3255 assert!(!cheats.has_recording_accesses_only_step_hook());
3256 }
3257
3258 #[test]
3259 fn mixed_step_hooks_disable_record_access_fast_path() {
3260 let mut cheats = Cheatcodes::<EthEvmNetwork>::new(Arc::default());
3261 cheats.recording_accesses = true;
3262
3263 cheats.gas_metering.reset = true;
3264 assert!(!cheats.has_recording_accesses_only_step_hook());
3265
3266 cheats.gas_metering.reset = false;
3267 cheats.env_overrides.insert(None, EnvOverrides { basefee: Some(1), ..Default::default() });
3268 assert!(!cheats.has_recording_accesses_only_step_hook());
3269 }
3270
3271 #[test]
3272 fn inactive_env_override_entries_do_not_enable_opcode_hooks() {
3273 let mut cheats = Cheatcodes::<EthEvmNetwork>::new(Arc::default());
3274 cheats.env_overrides.insert(None, EnvOverrides::default());
3275
3276 assert!(!cheats.has_step_hooks());
3277 assert!(!cheats.has_step_end_hooks());
3278
3279 cheats.env_overrides.get_mut(&None).unwrap().basefee = Some(1);
3280 assert!(cheats.has_step_hooks());
3281 assert!(cheats.has_step_end_hooks());
3282 }
3283
3284 #[test]
3285 fn active_log_state_enables_log_hooks() {
3286 let mut cheats = Cheatcodes::<EthEvmNetwork>::new(Arc::default());
3287
3288 cheats.recorded_logs = Some(Default::default());
3289 assert!(cheats.has_log_hooks());
3290
3291 cheats.recorded_logs = None;
3292 cheats.expected_emits.push_back((
3293 expect::ExpectedEmit {
3294 depth: 0,
3295 log: None,
3296 checks: [false; 5],
3297 address: None,
3298 anonymous: false,
3299 found: false,
3300 count: 1,
3301 mismatch_error: None,
3302 },
3303 Default::default(),
3304 ));
3305 assert!(cheats.has_log_hooks());
3306 }
3307
3308 #[test]
3309 fn arbitrary_storage_cache_value_routes_copied_targets_to_source() {
3310 let mut storage = ArbitraryStorage::default();
3311 let source = Address::repeat_byte(0x11);
3312 let copied = Address::repeat_byte(0x22);
3313 let slot = U256::from(7);
3314
3315 storage.mark_arbitrary(&source, false);
3316 storage.mark_copy(&source, &copied);
3317 storage.cache_value(copied, slot, U256::ZERO);
3318
3319 assert_eq!(storage.cached_value(source, slot), Some(U256::ZERO));
3320 }
3321}