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_sol_types::{SolCall, SolInterface, SolValue};
31use foundry_common::{
32 FoundryTransactionBuilder, SELECTOR_LEN, TransactionMaybeSigned,
33 mapping_slots::{MappingSlots, step as mapping_step},
34};
35use foundry_evm_core::{
36 Breakpoints, EvmEnv, FoundryTransaction, InspectorExt,
37 abi::Vm::stopExpectSafeMemoryCall,
38 backend::{DatabaseError, DatabaseExt, LocalForkId, RevertDiagnostic},
39 constants::{CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS, MAGIC_ASSUME},
40 env::FoundryContextExt,
41 evm::{
42 BlockEnvFor, EthEvmNetwork, FoundryContextFor, FoundryEvmFactory, FoundryEvmNetwork,
43 NestedEvmClosure, SpecFor, TransactionRequestFor, TxEnvFor, with_cloned_context,
44 },
45};
46use foundry_evm_traces::{
47 TracingInspector, TracingInspectorConfig, identifier::SignaturesIdentifier,
48};
49use foundry_wallets::wallet_multi::MultiWallet;
50use itertools::Itertools;
51use proptest::test_runner::{RngAlgorithm, TestRng, TestRunner};
52use rand::Rng;
53use revm::{
54 Inspector,
55 bytecode::opcode as op,
56 context::{Cfg, ContextTr, Host, JournalTr, Transaction, TransactionType, result::EVMError},
57 context_interface::{CreateScheme, transaction::SignedAuthorization},
58 handler::FrameResult,
59 inspector::JournalExt,
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 recording: bool,
361 pub last_gas_used: u64,
363 pub gas_records: Vec<GasRecord>,
365}
366
367impl GasMetering {
368 pub const fn start(&mut self) {
370 self.recording = true;
371 }
372
373 pub const fn stop(&mut self) {
375 self.recording = false;
376 }
377
378 pub fn resume(&mut self) {
380 if self.paused {
381 self.paused = false;
382 self.touched = true;
383 }
384 self.paused_frames.clear();
385 }
386
387 pub fn reset(&mut self) {
389 self.paused = false;
390 self.touched = true;
391 self.reset = true;
392 self.paused_frames.clear();
393 }
394}
395
396#[derive(Clone, Debug, Default)]
398pub struct ArbitraryStorage {
399 pub values: HashMap<Address, HashMap<U256, U256>>,
403 pub copies: HashMap<Address, Address>,
405 pub overwrites: HashSet<Address>,
407}
408
409impl ArbitraryStorage {
410 pub fn mark_arbitrary(&mut self, address: &Address, overwrite: bool) {
412 self.values.insert(*address, HashMap::default());
413 if overwrite {
414 self.overwrites.insert(*address);
415 } else {
416 self.overwrites.remove(address);
417 }
418 }
419
420 pub fn mark_copy(&mut self, from: &Address, to: &Address) {
422 if self.values.contains_key(from) {
423 self.copies.insert(*to, *from);
424 }
425 }
426
427 pub fn save<CTX: ContextTr>(
431 &mut self,
432 ecx: &mut CTX,
433 address: Address,
434 slot: U256,
435 data: U256,
436 ) {
437 self.values.get_mut(&address).expect("missing arbitrary address entry").insert(slot, data);
438 if ecx.journal_mut().load_account(address).is_ok() {
439 ecx.journal_mut()
440 .sstore(address, slot, data)
441 .expect("could not set arbitrary storage value");
442 }
443 }
444
445 pub fn copy<CTX: ContextTr>(
451 &mut self,
452 ecx: &mut CTX,
453 target: Address,
454 slot: U256,
455 new_value: U256,
456 ) -> U256 {
457 let source = self.copies.get(&target).expect("missing arbitrary copy target entry");
458 let storage_cache = self.values.get_mut(source).expect("missing arbitrary source storage");
459 let value = match storage_cache.get(&slot) {
460 Some(value) => *value,
461 None => {
462 storage_cache.insert(slot, new_value);
463 if ecx.journal_mut().load_account(*source).is_ok() {
465 ecx.journal_mut()
466 .sstore(*source, slot, new_value)
467 .expect("could not copy arbitrary storage value");
468 }
469 new_value
470 }
471 };
472 if ecx.journal_mut().load_account(target).is_ok() {
474 ecx.journal_mut().sstore(target, slot, value).expect("could not set storage");
475 }
476 value
477 }
478}
479
480pub type BroadcastableTransactions<N> = VecDeque<BroadcastableTransaction<N>>;
482
483#[derive(Clone, Debug)]
501pub struct Cheatcodes<FEN: FoundryEvmNetwork = EthEvmNetwork> {
502 pub analysis: Option<CheatcodeAnalysis>,
504
505 pub block: Option<BlockEnvFor<FEN>>,
510
511 pub active_delegations: Vec<SignedAuthorization>,
515
516 pub active_blob_sidecar: Option<BlobTransactionSidecarVariant>,
518
519 pub gas_price: Option<u128>,
524
525 pub labels: AddressHashMap<String>,
527
528 pub pranks: BTreeMap<usize, Prank>,
530
531 pub expected_revert: Option<ExpectedRevert>,
533
534 pub assume_no_revert: Option<AssumeNoRevert>,
536
537 pub fork_revert_diagnostic: Option<RevertDiagnostic>,
539
540 pub accesses: RecordAccess,
542
543 pub recording_accesses: bool,
545
546 pub recorded_account_diffs_stack: Option<Vec<Vec<AccountAccess>>>,
552
553 pub record_debug_steps_info: Option<RecordDebugStepInfo>,
555
556 pub recorded_logs: Option<Vec<crate::Vm::Log>>,
558
559 pub mocked_calls: HashMap<Address, BTreeMap<MockCallDataContext, VecDeque<MockCallReturnData>>>,
562
563 pub mocked_functions: HashMap<Address, HashMap<Bytes, Address>>,
565
566 pub expected_calls: ExpectedCallTracker,
568 pub expected_emits: ExpectedEmitTracker,
570 pub expected_creates: Vec<ExpectedCreate>,
572
573 pub allowed_mem_writes: HashMap<u64, Vec<Range<u64>>>,
575
576 pub broadcast: Option<Broadcast>,
578
579 pub broadcastable_transactions: BroadcastableTransactions<FEN::Network>,
581
582 pub access_list: Option<AccessList>,
584
585 pub config: Arc<CheatsConfig>,
587
588 pub test_context: TestContext,
590
591 pub fs_commit: bool,
594
595 pub serialized_jsons: BTreeMap<String, BTreeMap<String, Value>>,
598
599 pub eth_deals: Vec<DealRecord>,
601
602 pub gas_metering: GasMetering,
604
605 pub gas_snapshots: BTreeMap<String, BTreeMap<String, String>>,
608
609 pub mapping_slots: Option<AddressHashMap<MappingSlots>>,
611
612 pub pc: usize,
614 pub breakpoints: Breakpoints,
617
618 pub intercept_next_create_call: bool,
620
621 test_runner: Option<TestRunner>,
624
625 pub ignored_traces: IgnoredTraces,
627
628 pub arbitrary_storage: Option<ArbitraryStorage>,
630
631 pub deprecated: HashMap<&'static str, Option<&'static str>>,
633 pub wallets: Option<Wallets>,
635 signatures_identifier: OnceLock<Option<SignaturesIdentifier>>,
637 pub dynamic_gas_limit: bool,
639 pub execution_evm_version: Option<SpecFor<FEN>>,
641
642 pub env_overrides: HashMap<Option<LocalForkId>, EnvOverrides>,
652
653 pub env_overrides_snapshots: HashMap<U256, HashMap<Option<LocalForkId>, EnvOverrides>>,
663
664 pub in_isolation_context: bool,
674}
675
676impl Default for Cheatcodes {
680 fn default() -> Self {
681 Self::new(Arc::default())
682 }
683}
684
685impl<FEN: FoundryEvmNetwork> Cheatcodes<FEN> {
686 pub fn new(config: Arc<CheatsConfig>) -> Self {
688 Self {
689 analysis: None,
690 fs_commit: true,
691 labels: config.labels.clone(),
692 config,
693 block: Default::default(),
694 active_delegations: Default::default(),
695 active_blob_sidecar: Default::default(),
696 gas_price: Default::default(),
697 pranks: Default::default(),
698 expected_revert: Default::default(),
699 assume_no_revert: Default::default(),
700 fork_revert_diagnostic: Default::default(),
701 accesses: Default::default(),
702 recording_accesses: Default::default(),
703 recorded_account_diffs_stack: Default::default(),
704 recorded_logs: Default::default(),
705 record_debug_steps_info: Default::default(),
706 mocked_calls: Default::default(),
707 mocked_functions: Default::default(),
708 expected_calls: Default::default(),
709 expected_emits: Default::default(),
710 expected_creates: Default::default(),
711 allowed_mem_writes: Default::default(),
712 broadcast: Default::default(),
713 broadcastable_transactions: Default::default(),
714 access_list: Default::default(),
715 test_context: Default::default(),
716 serialized_jsons: Default::default(),
717 eth_deals: Default::default(),
718 gas_metering: Default::default(),
719 gas_snapshots: Default::default(),
720 mapping_slots: Default::default(),
721 pc: Default::default(),
722 breakpoints: Default::default(),
723 intercept_next_create_call: Default::default(),
724 test_runner: Default::default(),
725 ignored_traces: Default::default(),
726 arbitrary_storage: Default::default(),
727 deprecated: Default::default(),
728 wallets: Default::default(),
729 signatures_identifier: Default::default(),
730 dynamic_gas_limit: Default::default(),
731 execution_evm_version: None,
732 env_overrides: Default::default(),
733 env_overrides_snapshots: Default::default(),
734 in_isolation_context: false,
735 }
736 }
737
738 pub fn set_analysis(&mut self, analysis: CheatcodeAnalysis) {
740 self.analysis = Some(analysis);
741 }
742
743 pub fn env_overrides_for(&self, fork_id: Option<U256>) -> Option<&EnvOverrides> {
745 self.env_overrides.get(&fork_id).filter(|o| o.is_any_set())
746 }
747
748 pub fn env_overrides_for_mut(&mut self, fork_id: Option<U256>) -> &mut EnvOverrides {
751 self.env_overrides.entry(fork_id).or_default()
752 }
753
754 pub fn get_prank(&self, depth: usize) -> Option<&Prank> {
758 self.pranks.range(..=depth).last().map(|(_, prank)| prank)
759 }
760
761 pub fn wallets(&mut self) -> &Wallets {
763 self.wallets.get_or_insert_with(|| Wallets::new(MultiWallet::default(), None))
764 }
765
766 pub fn set_wallets(&mut self, wallets: Wallets) {
768 self.wallets = Some(wallets);
769 }
770
771 pub fn add_delegation(&mut self, authorization: SignedAuthorization) {
773 self.active_delegations.push(authorization);
774 }
775
776 pub fn signatures_identifier(&self) -> Option<&SignaturesIdentifier> {
778 self.signatures_identifier.get_or_init(|| SignaturesIdentifier::new(true).ok()).as_ref()
779 }
780
781 fn apply_cheatcode(
783 &mut self,
784 ecx: &mut FoundryContextFor<'_, FEN>,
785 call: &CallInputs,
786 executor: &mut dyn CheatcodesExecutor<FEN>,
787 ) -> Result {
788 let decoded = Vm::VmCalls::abi_decode(&call.input.bytes(ecx)).map_err(|e| {
790 if let alloy_sol_types::Error::UnknownSelector { name: _, selector } = e {
791 let msg = format!(
792 "unknown cheatcode with selector {selector}; \
793 you may have a mismatch between the `Vm` interface (likely in `forge-std`) \
794 and the `forge` version"
795 );
796 return alloy_sol_types::Error::Other(std::borrow::Cow::Owned(msg));
797 }
798 e
799 })?;
800
801 let caller = call.caller;
802
803 ecx.db_mut().ensure_cheatcode_access_forking_mode(&caller)?;
806
807 apply_dispatch(
808 &decoded,
809 &mut CheatsCtxt { state: self, ecx, gas_limit: call.gas_limit, caller },
810 executor,
811 )
812 }
813
814 fn allow_cheatcodes_on_create(
820 &self,
821 ecx: &mut FoundryContextFor<FEN>,
822 caller: Address,
823 created_address: Address,
824 ) {
825 if ecx.journal().depth() <= 1 || ecx.db().has_cheatcode_access(&caller) {
826 ecx.db_mut().allow_cheatcode_access(created_address);
827 }
828 }
829
830 fn apply_accesslist(&mut self, ecx: &mut FoundryContextFor<FEN>) {
836 if let Some(access_list) = &self.access_list {
837 ecx.tx_mut().set_access_list(access_list.clone());
838
839 if ecx.tx().tx_type() == TransactionType::Legacy as u8 {
840 ecx.tx_mut().set_tx_type(TransactionType::Eip2930 as u8);
841 }
842 }
843 }
844
845 pub fn on_revert(&mut self, ecx: &mut FoundryContextFor<FEN>) {
850 trace!(deals=?self.eth_deals.len(), "rolling back deals");
851
852 if self.expected_revert.is_some() {
854 return;
855 }
856
857 if ecx.journal().depth() > 0 {
859 return;
860 }
861
862 while let Some(record) = self.eth_deals.pop() {
866 if let Some(acc) = ecx.journal_mut().evm_state_mut().get_mut(&record.address) {
867 acc.info.balance = record.old_balance;
868 }
869 }
870 }
871
872 pub fn call_with_executor(
873 &mut self,
874 ecx: &mut FoundryContextFor<'_, FEN>,
875 call: &mut CallInputs,
876 executor: &mut dyn CheatcodesExecutor<FEN>,
877 ) -> Option<CallOutcome> {
878 if let Some(spec_id) = self.execution_evm_version {
880 ecx.cfg_mut().set_spec_and_mainnet_gas_params(spec_id);
881 }
882
883 let gas = Gas::new(call.gas_limit);
884 let curr_depth = ecx.journal().depth();
885
886 if curr_depth == 0 {
890 let sender = ecx.tx().caller();
891 let account = match super::evm::journaled_account(ecx, sender) {
892 Ok(account) => account,
893 Err(err) => {
894 return Some(CallOutcome {
895 result: InterpreterResult {
896 result: InstructionResult::Revert,
897 output: err.abi_encode().into(),
898 gas,
899 },
900 memory_offset: call.return_memory_offset.clone(),
901 was_precompile_called: false,
902 precompile_call_logs: vec![],
903 });
904 }
905 };
906 let prev = account.info.nonce;
907 account.info.nonce = prev.saturating_sub(1);
908
909 trace!(target: "cheatcodes", %sender, nonce=account.info.nonce, prev, "corrected nonce");
910 }
911
912 if call.target_address == CHEATCODE_ADDRESS {
913 return match self.apply_cheatcode(ecx, call, executor) {
914 Ok(retdata) => Some(CallOutcome {
915 result: InterpreterResult {
916 result: InstructionResult::Return,
917 output: retdata.into(),
918 gas,
919 },
920 memory_offset: call.return_memory_offset.clone(),
921 was_precompile_called: true,
922 precompile_call_logs: vec![],
923 }),
924 Err(err) => Some(CallOutcome {
925 result: InterpreterResult {
926 result: InstructionResult::Revert,
927 output: err.abi_encode().into(),
928 gas,
929 },
930 memory_offset: call.return_memory_offset.clone(),
931 was_precompile_called: false,
932 precompile_call_logs: vec![],
933 }),
934 };
935 }
936
937 if call.target_address == HARDHAT_CONSOLE_ADDRESS {
938 return None;
939 }
940
941 if let Some(expected) = &mut self.expected_revert {
945 expected.max_depth = max(curr_depth + 1, expected.max_depth);
946 }
947
948 if let Some(expected_calls_for_target) = self.expected_calls.get_mut(&call.bytecode_address)
952 {
953 for (calldata, (expected, actual_count)) in expected_calls_for_target {
955 if calldata.len() <= call.input.len() &&
958 *calldata == call.input.bytes(ecx)[..calldata.len()] &&
960 expected
962 .value.is_none_or(|value| Some(value) == call.transfer_value()) &&
963 expected.gas.is_none_or(|gas| gas == call.gas_limit) &&
965 expected.min_gas.is_none_or(|min_gas| min_gas <= call.gas_limit)
967 {
968 *actual_count += 1;
969 }
970 }
971 }
972
973 if let Some(prank) = &self.get_prank(curr_depth) {
975 if prank.delegate_call
977 && curr_depth == prank.depth
978 && call.scheme == CallScheme::DelegateCall
979 {
980 call.target_address = prank.new_caller;
981 call.caller = prank.new_caller;
982 if let Some(new_origin) = prank.new_origin {
983 ecx.tx_mut().set_caller(new_origin);
984 }
985 }
986
987 if curr_depth >= prank.depth && call.caller == prank.prank_caller {
988 let prank_applied = if curr_depth == prank.depth {
990 let _ = journaled_account(ecx, prank.new_caller);
992 call.caller = prank.new_caller;
993 true
994 } else {
995 false
996 };
997
998 let prank_applied = if let Some(new_origin) = prank.new_origin {
1000 ecx.tx_mut().set_caller(new_origin);
1001 true
1002 } else {
1003 prank_applied
1004 };
1005
1006 if prank_applied && let Some(applied_prank) = prank.first_time_applied() {
1008 self.pranks.insert(curr_depth, applied_prank);
1009 }
1010 }
1011 }
1012
1013 if let Some(mocks) = self.mocked_calls.get_mut(&call.bytecode_address) {
1015 let ctx = MockCallDataContext {
1016 calldata: call.input.bytes(ecx),
1017 value: call.transfer_value(),
1018 };
1019
1020 if let Some(return_data_queue) = match mocks.get_mut(&ctx) {
1021 Some(queue) => Some(queue),
1022 None => mocks
1023 .iter_mut()
1024 .find(|(mock, _)| {
1025 call.input.bytes(ecx).get(..mock.calldata.len()) == Some(&mock.calldata[..])
1026 && mock.value.is_none_or(|value| Some(value) == call.transfer_value())
1027 })
1028 .map(|(_, v)| v),
1029 } && let Some(return_data) = return_data_queue.front().map(|x| x.to_owned())
1030 {
1031 if let Some(value) = call.transfer_value() {
1032 let checkpoint = ecx.journal_mut().checkpoint();
1033 match ecx.journal_mut().transfer_loaded(
1034 call.transfer_from(),
1035 call.transfer_to(),
1036 value,
1037 ) {
1038 None => {
1039 if return_data.ret_type.is_ok() {
1040 ecx.journal_mut().checkpoint_commit();
1041 } else {
1042 ecx.journal_mut().checkpoint_revert(checkpoint);
1043 }
1044 }
1045 Some(err) => {
1046 ecx.journal_mut().checkpoint_revert(checkpoint);
1047 return Some(CallOutcome {
1048 result: InterpreterResult {
1049 result: err.into(),
1050 output: Bytes::new(),
1051 gas,
1052 },
1053 memory_offset: call.return_memory_offset.clone(),
1054 was_precompile_called: false,
1055 precompile_call_logs: vec![],
1056 });
1057 }
1058 }
1059 }
1060
1061 if return_data_queue.len() > 1 {
1063 return_data_queue.pop_front();
1064 }
1065
1066 return Some(CallOutcome {
1067 result: InterpreterResult {
1068 result: return_data.ret_type,
1069 output: return_data.data,
1070 gas,
1071 },
1072 memory_offset: call.return_memory_offset.clone(),
1073 was_precompile_called: true,
1074 precompile_call_logs: vec![],
1075 });
1076 }
1077 }
1078
1079 self.apply_accesslist(ecx);
1081
1082 if let Some(broadcast) = &self.broadcast {
1084 let is_fixed_gas_limit = call.gas_limit >= 21_000 && !self.dynamic_gas_limit;
1087 self.dynamic_gas_limit = false;
1088
1089 if curr_depth == broadcast.depth && call.caller == broadcast.original_caller {
1094 ecx.tx_mut().set_caller(broadcast.new_origin);
1098
1099 call.caller = broadcast.new_origin;
1100 if !call.is_static {
1105 if let Err(err) = ecx.journal_mut().load_account(broadcast.new_origin) {
1106 return Some(CallOutcome {
1107 result: InterpreterResult {
1108 result: InstructionResult::Revert,
1109 output: Error::encode(err),
1110 gas,
1111 },
1112 memory_offset: call.return_memory_offset.clone(),
1113 was_precompile_called: false,
1114 precompile_call_logs: vec![],
1115 });
1116 }
1117
1118 let input = call.input.bytes(ecx);
1119 let chain_id = ecx.cfg().chain_id();
1120 let rpc = ecx.db().active_fork_url();
1121 let account =
1122 ecx.journal_mut().evm_state_mut().get_mut(&broadcast.new_origin).unwrap();
1123
1124 let mut tx_req = TransactionRequestFor::<FEN>::default()
1125 .with_from(broadcast.new_origin)
1126 .with_to(call.target_address)
1127 .with_value(call.transfer_value().unwrap_or_default())
1128 .with_input(input)
1129 .with_nonce(account.info.nonce)
1130 .with_chain_id(chain_id);
1131 if is_fixed_gas_limit {
1132 tx_req.set_gas_limit(call.gas_limit)
1133 }
1134
1135 let active_delegations = std::mem::take(&mut self.active_delegations);
1136 if let Some(blob_sidecar) = self.active_blob_sidecar.take() {
1138 if !active_delegations.is_empty() {
1140 let msg = "both delegation and blob are active; `attachBlob` and `attachDelegation` are not compatible";
1141 return Some(CallOutcome {
1142 result: InterpreterResult {
1143 result: InstructionResult::Revert,
1144 output: Error::encode(msg),
1145 gas,
1146 },
1147 memory_offset: call.return_memory_offset.clone(),
1148 was_precompile_called: false,
1149 precompile_call_logs: vec![],
1150 });
1151 }
1152 tx_req.set_blob_sidecar(blob_sidecar);
1153 }
1154
1155 if !active_delegations.is_empty() {
1157 for auth in &active_delegations {
1158 let Ok(authority) = auth.recover_authority() else {
1159 continue;
1160 };
1161 if authority == broadcast.new_origin {
1162 account.info.nonce += 1;
1165 }
1166 }
1167 tx_req.set_authorization_list(active_delegations);
1168 }
1169 if let Some(fee_token) = self.config.fee_token {
1170 tx_req.set_fee_token(fee_token);
1171 }
1172 self.broadcastable_transactions.push_back(BroadcastableTransaction {
1173 rpc,
1174 transaction: TransactionMaybeSigned::new(tx_req),
1175 });
1176 debug!(target: "cheatcodes", tx=?self.broadcastable_transactions.back().unwrap(), "broadcastable call");
1177
1178 if !self.config.evm_opts.isolate {
1180 let prev = account.info.nonce;
1181 account.info.nonce += 1;
1182 debug!(target: "cheatcodes", address=%broadcast.new_origin, nonce=prev+1, prev, "incremented nonce");
1183 }
1184 } else if broadcast.single_call {
1185 let msg = "`staticcall`s are not allowed after `broadcast`; use `startBroadcast` instead";
1186 return Some(CallOutcome {
1187 result: InterpreterResult {
1188 result: InstructionResult::Revert,
1189 output: Error::encode(msg),
1190 gas,
1191 },
1192 memory_offset: call.return_memory_offset.clone(),
1193 was_precompile_called: false,
1194 precompile_call_logs: vec![],
1195 });
1196 }
1197 }
1198 }
1199
1200 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1202 let (initialized, old_balance, old_nonce) =
1205 if let Ok(acc) = ecx.journal_mut().load_account(call.target_address) {
1206 (acc.data.info.exists(), acc.data.info.balance, acc.data.info.nonce)
1207 } else {
1208 (false, U256::ZERO, 0)
1209 };
1210
1211 let kind = match call.scheme {
1212 CallScheme::Call => crate::Vm::AccountAccessKind::Call,
1213 CallScheme::CallCode => crate::Vm::AccountAccessKind::CallCode,
1214 CallScheme::DelegateCall => crate::Vm::AccountAccessKind::DelegateCall,
1215 CallScheme::StaticCall => crate::Vm::AccountAccessKind::StaticCall,
1216 };
1217
1218 recorded_account_diffs_stack.push(vec![AccountAccess {
1224 chainInfo: crate::Vm::ChainInfo {
1225 forkId: ecx.db().active_fork_id().unwrap_or_default(),
1226 chainId: U256::from(ecx.cfg().chain_id()),
1227 },
1228 accessor: call.caller,
1229 account: call.bytecode_address,
1230 kind,
1231 initialized,
1232 oldBalance: old_balance,
1233 newBalance: U256::ZERO, oldNonce: old_nonce,
1235 newNonce: 0, value: call.call_value(),
1237 data: call.input.bytes(ecx),
1238 reverted: false,
1239 deployedCode: Bytes::new(),
1240 storageAccesses: vec![], depth: ecx.journal().depth().try_into().expect("journaled state depth exceeds u64"),
1242 }]);
1243 }
1244
1245 None
1246 }
1247
1248 pub fn rng(&mut self) -> &mut impl Rng {
1249 self.test_runner().rng()
1250 }
1251
1252 pub fn test_runner(&mut self) -> &mut TestRunner {
1253 self.test_runner.get_or_insert_with(|| match self.config.seed {
1254 Some(seed) => TestRunner::new_with_rng(
1255 proptest::test_runner::Config::default(),
1256 TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>()),
1257 ),
1258 None => TestRunner::new(proptest::test_runner::Config::default()),
1259 })
1260 }
1261
1262 pub fn set_seed(&mut self, seed: U256) {
1263 self.test_runner = Some(TestRunner::new_with_rng(
1264 proptest::test_runner::Config::default(),
1265 TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>()),
1266 ));
1267 }
1268
1269 pub fn arbitrary_storage(&mut self) -> &mut ArbitraryStorage {
1272 self.arbitrary_storage.get_or_insert_with(ArbitraryStorage::default)
1273 }
1274
1275 pub fn has_arbitrary_storage(&self, address: &Address) -> bool {
1277 match &self.arbitrary_storage {
1278 Some(storage) => storage.values.contains_key(address),
1279 None => false,
1280 }
1281 }
1282
1283 pub fn should_overwrite_arbitrary_storage(
1287 &self,
1288 address: &Address,
1289 storage_slot: U256,
1290 ) -> bool {
1291 match &self.arbitrary_storage {
1292 Some(storage) => {
1293 storage.overwrites.contains(address)
1294 && storage
1295 .values
1296 .get(address)
1297 .and_then(|arbitrary_values| arbitrary_values.get(&storage_slot))
1298 .is_none()
1299 }
1300 None => false,
1301 }
1302 }
1303
1304 pub fn is_arbitrary_storage_copy(&self, address: &Address) -> bool {
1306 match &self.arbitrary_storage {
1307 Some(storage) => storage.copies.contains_key(address),
1308 None => false,
1309 }
1310 }
1311
1312 pub fn struct_defs(&self) -> Option<&foundry_common::fmt::StructDefinitions> {
1314 self.analysis.as_ref().and_then(|analysis| analysis.struct_defs().ok())
1315 }
1316}
1317
1318impl<FEN: FoundryEvmNetwork> Inspector<FoundryContextFor<'_, FEN>> for Cheatcodes<FEN> {
1319 fn initialize_interp(
1320 &mut self,
1321 interpreter: &mut Interpreter,
1322 ecx: &mut FoundryContextFor<'_, FEN>,
1323 ) {
1324 if let Some(block) = self.block.take() {
1327 ecx.set_block(block);
1328 }
1329 if let Some(gas_price) = self.gas_price.take() {
1330 ecx.tx_mut().set_gas_price(gas_price);
1331 }
1332
1333 if self.gas_metering.paused {
1335 self.gas_metering.paused_frames.push(interpreter.gas);
1336 }
1337
1338 if let Some(expected) = &mut self.expected_revert {
1340 expected.max_depth = max(ecx.journal().depth(), expected.max_depth);
1341 }
1342 }
1343
1344 fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut FoundryContextFor<'_, FEN>) {
1345 self.pc = interpreter.bytecode.pc();
1346
1347 if self.broadcast.is_some() {
1348 self.set_gas_limit_type(interpreter);
1349 }
1350
1351 if self.gas_metering.paused {
1353 self.meter_gas(interpreter);
1354 }
1355
1356 if self.gas_metering.reset {
1358 self.meter_gas_reset(interpreter);
1359 }
1360
1361 if self.recording_accesses {
1363 self.record_accesses(interpreter);
1364 }
1365
1366 if self.recorded_account_diffs_stack.is_some() {
1368 self.record_state_diffs(interpreter, ecx);
1369 }
1370
1371 if !self.allowed_mem_writes.is_empty() {
1373 self.check_mem_opcodes(
1374 interpreter,
1375 ecx.journal().depth().try_into().expect("journaled state depth exceeds u64"),
1376 );
1377 }
1378
1379 if let Some(mapping_slots) = &mut self.mapping_slots {
1381 mapping_step(mapping_slots, interpreter);
1382 }
1383
1384 if self.gas_metering.recording {
1386 self.meter_gas_record(interpreter, ecx);
1387 }
1388
1389 if !self.env_overrides.is_empty() {
1394 let fork_id = ecx.db().active_fork_id();
1395 if let Some(env_overrides) =
1396 self.env_overrides.get_mut(&fork_id).filter(|o| o.is_any_set())
1397 {
1398 env_overrides.pending_opcode = None;
1402 env_overrides.pending_blobhash_index = None;
1403
1404 let opcode = interpreter.bytecode.opcode();
1405 match opcode {
1406 op::BASEFEE | op::GASPRICE => {
1407 env_overrides.pending_opcode = Some(opcode);
1408 }
1409 op::BLOBHASH => {
1410 env_overrides.pending_opcode = Some(opcode);
1411 env_overrides.pending_blobhash_index =
1412 interpreter.stack.peek(0).ok().and_then(|index| index.try_into().ok());
1413 }
1414 _ => {}
1415 }
1416 }
1417 }
1418 }
1419
1420 fn step_end(&mut self, interpreter: &mut Interpreter, ecx: &mut FoundryContextFor<'_, FEN>) {
1421 if self.gas_metering.paused {
1422 self.meter_gas_end(interpreter);
1423 }
1424
1425 if self.gas_metering.touched {
1426 self.meter_gas_check(interpreter);
1427 }
1428
1429 if self.arbitrary_storage.is_some() {
1431 self.arbitrary_storage_end(interpreter, ecx);
1432 }
1433
1434 if !self.env_overrides.is_empty() {
1444 let fork_id = ecx.db().active_fork_id();
1445 if self.env_overrides.get(&fork_id).is_some_and(|o| o.is_any_set()) {
1446 let opcode_failed = interpreter
1452 .bytecode
1453 .action
1454 .as_ref()
1455 .and_then(|a| a.instruction_result())
1456 .is_some();
1457 if opcode_failed {
1458 if let Some(env_overrides) = self.env_overrides.get_mut(&fork_id) {
1459 env_overrides.pending_opcode = None;
1460 env_overrides.pending_blobhash_index = None;
1461 }
1462 } else {
1463 self.apply_env_overrides(interpreter, fork_id);
1464 }
1465 }
1466 }
1467 }
1468
1469 fn log(&mut self, _ecx: &mut FoundryContextFor<'_, FEN>, log: Log) {
1470 if !self.expected_emits.is_empty()
1471 && let Some(err) = expect::handle_expect_emit(self, &log, None)
1472 {
1473 let _ = sh_err!("{err:?}");
1477 }
1478
1479 record_logs(&mut self.recorded_logs, &log);
1481 }
1482
1483 fn log_full(
1484 &mut self,
1485 interpreter: &mut Interpreter,
1486 _ecx: &mut FoundryContextFor<'_, FEN>,
1487 log: Log,
1488 ) {
1489 if !self.expected_emits.is_empty() {
1490 expect::handle_expect_emit(self, &log, Some(interpreter));
1491 }
1492
1493 record_logs(&mut self.recorded_logs, &log);
1495 }
1496
1497 fn call(
1498 &mut self,
1499 ecx: &mut FoundryContextFor<'_, FEN>,
1500 inputs: &mut CallInputs,
1501 ) -> Option<CallOutcome> {
1502 Self::call_with_executor(self, ecx, inputs, &mut TransparentCheatcodesExecutor)
1503 }
1504
1505 fn call_end(
1506 &mut self,
1507 ecx: &mut FoundryContextFor<'_, FEN>,
1508 call: &CallInputs,
1509 outcome: &mut CallOutcome,
1510 ) {
1511 let cheatcode_call = call.target_address == CHEATCODE_ADDRESS
1512 || call.target_address == HARDHAT_CONSOLE_ADDRESS;
1513
1514 if !cheatcode_call {
1518 let curr_depth = ecx.journal().depth();
1520 if let Some(prank) = &self.get_prank(curr_depth)
1521 && curr_depth == prank.depth
1522 {
1523 ecx.tx_mut().set_caller(prank.prank_origin);
1524
1525 if prank.single_call {
1527 self.pranks.remove(&curr_depth);
1528 }
1529 }
1530
1531 if let Some(broadcast) = &self.broadcast
1533 && curr_depth == broadcast.depth
1534 {
1535 ecx.tx_mut().set_caller(broadcast.original_origin);
1536
1537 if broadcast.single_call {
1539 let _ = self.broadcast.take();
1540 }
1541 }
1542 }
1543
1544 if let Some(assume_no_revert) = &mut self.assume_no_revert {
1546 if outcome.result.is_revert() && assume_no_revert.reverted_by.is_none() {
1549 assume_no_revert.reverted_by = Some(call.target_address);
1550 }
1551
1552 let curr_depth = ecx.journal().depth();
1554 if curr_depth <= assume_no_revert.depth && !cheatcode_call {
1555 if outcome.result.is_revert() {
1558 let assume_no_revert = std::mem::take(&mut self.assume_no_revert).unwrap();
1559 return match revert_handlers::handle_assume_no_revert(
1560 &assume_no_revert,
1561 outcome.result.result,
1562 &outcome.result.output,
1563 &self.config.available_artifacts,
1564 ) {
1565 Ok(_) => {
1568 outcome.result.output = Error::from(MAGIC_ASSUME).abi_encode().into();
1569 }
1570 Err(error) => {
1573 trace!(expected=?assume_no_revert, ?error, status=?outcome.result.result, "Expected revert mismatch");
1574 outcome.result.result = InstructionResult::Revert;
1575 outcome.result.output = error.abi_encode().into();
1576 }
1577 };
1578 }
1579 self.assume_no_revert = None;
1581 }
1582 }
1583
1584 if let Some(expected_revert) = &mut self.expected_revert {
1586 let call_failed = !matches!(outcome.result.result, return_ok!());
1589 if call_failed {
1590 if expected_revert.reverter.is_some()
1594 && (expected_revert.reverted_by.is_none() || expected_revert.count > 1)
1595 {
1596 expected_revert.reverted_by = Some(call.target_address);
1597 }
1598 }
1599
1600 let curr_depth = ecx.journal().depth();
1601 if curr_depth <= expected_revert.depth {
1602 let internal = self.config.internal_expect_revert;
1607 let went_deeper = expected_revert.max_depth > expected_revert.depth;
1608 let needs_processing = match expected_revert.kind {
1609 ExpectedRevertKind::Default => (|| {
1610 if cheatcode_call {
1612 return false;
1613 }
1614 if call_failed {
1616 return true;
1617 }
1618 if !internal && went_deeper {
1620 return true;
1621 }
1622 if curr_depth == 0 {
1624 return true;
1625 }
1626 !internal
1629 })(),
1630 ExpectedRevertKind::Cheatcode { pending_processing } => {
1633 cheatcode_call && !pending_processing
1634 }
1635 };
1636
1637 if needs_processing {
1638 let mut expected_revert = std::mem::take(&mut self.expected_revert).unwrap();
1639 return match revert_handlers::handle_expect_revert(
1640 cheatcode_call,
1641 false,
1642 self.config.internal_expect_revert,
1643 &expected_revert,
1644 outcome.result.result,
1645 outcome.result.output.clone(),
1646 &self.config.available_artifacts,
1647 ) {
1648 Err(error) => {
1649 trace!(expected=?expected_revert, ?error, status=?outcome.result.result, "Expected revert mismatch");
1650 outcome.result.result = InstructionResult::Revert;
1651 outcome.result.output = error.abi_encode().into();
1652 }
1653 Ok((_, retdata)) => {
1654 expected_revert.actual_count += 1;
1655 if expected_revert.actual_count < expected_revert.count {
1656 self.expected_revert = Some(expected_revert);
1657 }
1658 outcome.result.result = InstructionResult::Return;
1659 outcome.result.output = retdata;
1660 }
1661 };
1662 }
1663
1664 if let ExpectedRevertKind::Cheatcode { pending_processing } =
1667 &mut self.expected_revert.as_mut().unwrap().kind
1668 {
1669 *pending_processing = false;
1670 }
1671 }
1672 }
1673
1674 if cheatcode_call {
1677 return;
1678 }
1679
1680 let gas = outcome.result.gas;
1683 self.gas_metering.last_call_gas = Some(crate::Vm::Gas {
1684 gasLimit: gas.limit(),
1685 gasTotalUsed: gas.total_gas_spent(),
1686 gasMemoryUsed: 0,
1687 gasRefunded: gas.refunded(),
1688 gasRemaining: gas.remaining(),
1689 });
1690
1691 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1694 if ecx.journal().depth() > 0
1696 && let Some(mut last_recorded_depth) = recorded_account_diffs_stack.pop()
1697 {
1698 if outcome.result.is_revert() {
1701 for element in &mut *last_recorded_depth {
1702 element.reverted = true;
1703 for storage_access in &mut element.storageAccesses {
1704 storage_access.reverted = true;
1705 }
1706 }
1707 }
1708
1709 if let Some(call_access) = last_recorded_depth.first_mut() {
1710 let curr_depth = ecx.journal().depth();
1715 if call_access.depth == curr_depth as u64
1716 && let Ok(acc) = ecx.journal_mut().load_account(call.target_address)
1717 {
1718 debug_assert!(access_is_call(call_access.kind));
1719 call_access.newBalance = acc.data.info.balance;
1720 call_access.newNonce = acc.data.info.nonce;
1721 }
1722 if let Some(last) = recorded_account_diffs_stack.last_mut() {
1727 last.extend(last_recorded_depth);
1728 } else {
1729 recorded_account_diffs_stack.push(last_recorded_depth);
1730 }
1731 }
1732 }
1733 }
1734
1735 let diag = self.fork_revert_diagnostic.take();
1738
1739 if outcome.result.is_revert() {
1742 if let Some(err) = diag {
1745 outcome.result.output = Error::encode(err.to_error_msg(&self.labels));
1746 }
1747 return;
1748 }
1749
1750 let should_check_emits = self
1762 .expected_emits
1763 .iter()
1764 .any(|(expected, _)| {
1765 let curr_depth = ecx.journal().depth();
1766 expected.depth == curr_depth
1767 }) &&
1768 !call.is_static;
1770 if should_check_emits {
1771 let expected_counts = self
1772 .expected_emits
1773 .iter()
1774 .filter_map(|(expected, count_map)| {
1775 let count = match expected.address {
1776 Some(emitter) => match count_map.get(&emitter) {
1777 Some(log_count) => expected
1778 .log
1779 .as_ref()
1780 .map(|l| log_count.count(l))
1781 .unwrap_or_else(|| log_count.count_unchecked()),
1782 None => 0,
1783 },
1784 None => match &expected.log {
1785 Some(log) => count_map.values().map(|logs| logs.count(log)).sum(),
1786 None => count_map.values().map(|logs| logs.count_unchecked()).sum(),
1787 },
1788 };
1789
1790 (count != expected.count).then_some((expected, count))
1791 })
1792 .collect::<Vec<_>>();
1793
1794 if let Some((expected, _)) = self
1796 .expected_emits
1797 .iter()
1798 .find(|(expected, _)| !expected.found && expected.count > 0)
1799 {
1800 outcome.result.result = InstructionResult::Revert;
1801 let error_msg = expected.mismatch_error.as_deref().unwrap_or("log != expected log");
1802 outcome.result.output = error_msg.abi_encode().into();
1803 return;
1804 }
1805
1806 if !expected_counts.is_empty() {
1807 let msg = if outcome.result.is_ok() {
1808 let (expected, count) = expected_counts.first().unwrap();
1809 format!("log emitted {count} times, expected {}", expected.count)
1810 } else {
1811 "expected an emit, but the call reverted instead. \
1812 ensure you're testing the happy path when using `expectEmit`"
1813 .to_string()
1814 };
1815
1816 outcome.result.result = InstructionResult::Revert;
1817 outcome.result.output = Error::encode(msg);
1818 return;
1819 }
1820
1821 self.expected_emits.clear()
1825 }
1826
1827 if let TxKind::Call(test_contract) = ecx.tx().kind() {
1830 if ecx.db().is_forked_mode()
1833 && outcome.result.result == InstructionResult::Stop
1834 && call.target_address != test_contract
1835 {
1836 self.fork_revert_diagnostic =
1837 ecx.db().diagnose_revert(call.target_address, ecx.journal().evm_state());
1838 }
1839 }
1840
1841 if ecx.journal().depth() == 0 {
1843 if outcome.result.is_revert() {
1847 return;
1848 }
1849
1850 for (address, calldatas) in &self.expected_calls {
1855 for (calldata, (expected, actual_count)) in calldatas {
1857 let ExpectedCallData { gas, min_gas, value, count, call_type } = expected;
1859
1860 let failed = match call_type {
1861 ExpectedCallType::Count => *count != *actual_count,
1865 ExpectedCallType::NonCount => *count > *actual_count,
1870 };
1871 if failed {
1872 let expected_values = [
1873 Some(format!("data {}", hex::encode_prefixed(calldata))),
1874 value.as_ref().map(|v| format!("value {v}")),
1875 gas.map(|g| format!("gas {g}")),
1876 min_gas.map(|g| format!("minimum gas {g}")),
1877 ]
1878 .into_iter()
1879 .flatten()
1880 .join(", ");
1881 let but = if outcome.result.is_ok() {
1882 let s = if *actual_count == 1 { "" } else { "s" };
1883 format!("was called {actual_count} time{s}")
1884 } else {
1885 "the call reverted instead; \
1886 ensure you're testing the happy path when using `expectCall`"
1887 .to_string()
1888 };
1889 let s = if *count == 1 { "" } else { "s" };
1890 let msg = format!(
1891 "expected call to {address} with {expected_values} \
1892 to be called {count} time{s}, but {but}"
1893 );
1894 outcome.result.result = InstructionResult::Revert;
1895 outcome.result.output = Error::encode(msg);
1896
1897 return;
1898 }
1899 }
1900 }
1901
1902 for (expected, _) in &mut self.expected_emits {
1906 if expected.count == 0 && !expected.found {
1907 expected.found = true;
1908 }
1909 }
1910 self.expected_emits.retain(|(expected, _)| !expected.found);
1911 if !self.expected_emits.is_empty() {
1913 let msg = if outcome.result.is_ok() {
1914 "expected an emit, but no logs were emitted afterwards. \
1915 you might have mismatched events or not enough events were emitted"
1916 } else {
1917 "expected an emit, but the call reverted instead. \
1918 ensure you're testing the happy path when using `expectEmit`"
1919 };
1920 outcome.result.result = InstructionResult::Revert;
1921 outcome.result.output = Error::encode(msg);
1922 return;
1923 }
1924
1925 if let Some(expected_create) = self.expected_creates.first() {
1927 let msg = format!(
1928 "expected {} call by address {} for bytecode {} but not found",
1929 expected_create.create_scheme,
1930 hex::encode_prefixed(expected_create.deployer),
1931 hex::encode_prefixed(&expected_create.bytecode),
1932 );
1933 outcome.result.result = InstructionResult::Revert;
1934 outcome.result.output = Error::encode(msg);
1935 }
1936 }
1937 }
1938
1939 fn create(
1940 &mut self,
1941 ecx: &mut FoundryContextFor<'_, FEN>,
1942 mut input: &mut CreateInputs,
1943 ) -> Option<CreateOutcome> {
1944 if let Some(spec_id) = self.execution_evm_version {
1946 ecx.cfg_mut().set_spec_and_mainnet_gas_params(spec_id);
1947 }
1948
1949 let gas = Gas::new(input.gas_limit());
1950 if self.intercept_next_create_call {
1952 self.intercept_next_create_call = false;
1954
1955 let output = input.init_code();
1957
1958 return Some(CreateOutcome {
1960 result: InterpreterResult { result: InstructionResult::Revert, output, gas },
1961 address: None,
1962 });
1963 }
1964
1965 let curr_depth = ecx.journal().depth();
1966
1967 if let Some(prank) = &self.get_prank(curr_depth)
1969 && curr_depth >= prank.depth
1970 && input.caller() == prank.prank_caller
1971 {
1972 let prank_applied = if curr_depth == prank.depth {
1974 let _ = journaled_account(ecx, prank.new_caller);
1976 input.set_caller(prank.new_caller);
1977 true
1978 } else {
1979 false
1980 };
1981
1982 let prank_applied = if let Some(new_origin) = prank.new_origin {
1984 ecx.tx_mut().set_caller(new_origin);
1985 true
1986 } else {
1987 prank_applied
1988 };
1989
1990 if prank_applied && let Some(applied_prank) = prank.first_time_applied() {
1992 self.pranks.insert(curr_depth, applied_prank);
1993 }
1994 }
1995
1996 self.apply_accesslist(ecx);
1998
1999 if let Some(broadcast) = &mut self.broadcast
2001 && curr_depth >= broadcast.depth
2002 && input.caller() == broadcast.original_caller
2003 {
2004 if let Err(err) = ecx.journal_mut().load_account(broadcast.new_origin) {
2005 return Some(CreateOutcome {
2006 result: InterpreterResult {
2007 result: InstructionResult::Revert,
2008 output: Error::encode(err),
2009 gas,
2010 },
2011 address: None,
2012 });
2013 }
2014
2015 ecx.tx_mut().set_caller(broadcast.new_origin);
2016
2017 if curr_depth == broadcast.depth || broadcast.deploy_from_code {
2018 broadcast.deploy_from_code = false;
2020
2021 input.set_caller(broadcast.new_origin);
2022
2023 let rpc = ecx.db().active_fork_url();
2024 let account = &ecx.journal().evm_state()[&broadcast.new_origin];
2025 let mut tx_req = TransactionRequestFor::<FEN>::default()
2026 .with_from(broadcast.new_origin)
2027 .with_kind(TxKind::Create)
2028 .with_value(input.value())
2029 .with_input(input.init_code())
2030 .with_nonce(account.info.nonce);
2031 if let Some(fee_token) = self.config.fee_token {
2032 tx_req.set_fee_token(fee_token);
2033 }
2034 self.broadcastable_transactions.push_back(BroadcastableTransaction {
2035 rpc,
2036 transaction: TransactionMaybeSigned::new(tx_req),
2037 });
2038
2039 input.log_debug(self, &input.scheme().unwrap_or(CreateScheme::Create));
2040 }
2041 }
2042
2043 let address = input.allow_cheatcodes(self, ecx);
2045
2046 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
2048 recorded_account_diffs_stack.push(vec![AccountAccess {
2049 chainInfo: crate::Vm::ChainInfo {
2050 forkId: ecx.db().active_fork_id().unwrap_or_default(),
2051 chainId: U256::from(ecx.cfg().chain_id()),
2052 },
2053 accessor: input.caller(),
2054 account: address,
2055 kind: crate::Vm::AccountAccessKind::Create,
2056 initialized: true,
2057 oldBalance: U256::ZERO, newBalance: U256::ZERO, oldNonce: 0, newNonce: 1, value: input.value(),
2062 data: input.init_code(),
2063 reverted: false,
2064 deployedCode: Bytes::new(), storageAccesses: vec![], depth: curr_depth as u64,
2067 }]);
2068 }
2069
2070 None
2071 }
2072
2073 fn create_end(
2074 &mut self,
2075 ecx: &mut FoundryContextFor<'_, FEN>,
2076 call: &CreateInputs,
2077 outcome: &mut CreateOutcome,
2078 ) {
2079 let call = Some(call);
2080 let curr_depth = ecx.journal().depth();
2081
2082 if let Some(prank) = &self.get_prank(curr_depth)
2084 && curr_depth == prank.depth
2085 {
2086 ecx.tx_mut().set_caller(prank.prank_origin);
2087
2088 if prank.single_call {
2090 std::mem::take(&mut self.pranks);
2091 }
2092 }
2093
2094 if let Some(broadcast) = &self.broadcast
2096 && curr_depth == broadcast.depth
2097 {
2098 ecx.tx_mut().set_caller(broadcast.original_origin);
2099
2100 if broadcast.single_call {
2102 std::mem::take(&mut self.broadcast);
2103 }
2104 }
2105
2106 if let Some(expected_revert) = &mut self.expected_revert {
2108 if outcome.result.is_revert()
2120 && expected_revert.reverter.is_some()
2121 && expected_revert.reverted_by.is_none()
2122 && let Some(addr) = outcome.address
2123 {
2124 expected_revert.reverted_by = Some(addr);
2125 }
2126
2127 if curr_depth <= expected_revert.depth
2128 && matches!(expected_revert.kind, ExpectedRevertKind::Default)
2129 {
2130 let mut expected_revert = std::mem::take(&mut self.expected_revert).unwrap();
2131 return match revert_handlers::handle_expect_revert(
2132 false,
2133 true,
2134 self.config.internal_expect_revert,
2135 &expected_revert,
2136 outcome.result.result,
2137 outcome.result.output.clone(),
2138 &self.config.available_artifacts,
2139 ) {
2140 Ok((address, retdata)) => {
2141 expected_revert.actual_count += 1;
2142 if expected_revert.actual_count < expected_revert.count {
2143 expected_revert.reverted_by = None;
2145 self.expected_revert = Some(expected_revert.clone());
2146 }
2147
2148 outcome.result.result = InstructionResult::Return;
2149 outcome.result.output = retdata;
2150 outcome.address = address;
2151 }
2152 Err(err) => {
2153 outcome.result.result = InstructionResult::Revert;
2154 outcome.result.output = err.abi_encode().into();
2155 }
2156 };
2157 }
2158 }
2159
2160 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
2163 if curr_depth > 0
2165 && let Some(last_depth) = &mut recorded_account_diffs_stack.pop()
2166 {
2167 if outcome.result.is_revert() {
2170 for element in &mut *last_depth {
2171 element.reverted = true;
2172 for storage_access in &mut element.storageAccesses {
2173 storage_access.reverted = true;
2174 }
2175 }
2176 }
2177
2178 if let Some(create_access) = last_depth.first_mut() {
2179 let depth = ecx.journal().depth();
2184 if create_access.depth == depth as u64 {
2185 debug_assert_eq!(
2186 create_access.kind as u8,
2187 crate::Vm::AccountAccessKind::Create as u8
2188 );
2189 if let Some(address) = outcome.address
2190 && let Ok(created_acc) = ecx.journal_mut().load_account(address)
2191 {
2192 create_access.newBalance = created_acc.data.info.balance;
2193 create_access.newNonce = created_acc.data.info.nonce;
2194 create_access.deployedCode = created_acc
2195 .data
2196 .info
2197 .code
2198 .clone()
2199 .unwrap_or_default()
2200 .original_bytes();
2201 }
2202 }
2203 if let Some(last) = recorded_account_diffs_stack.last_mut() {
2208 last.append(last_depth);
2209 } else {
2210 recorded_account_diffs_stack.push(last_depth.clone());
2211 }
2212 }
2213 }
2214 }
2215
2216 if !self.expected_creates.is_empty()
2218 && let (Some(address), Some(call)) = (outcome.address, call)
2219 && let Ok(created_acc) = ecx.journal_mut().load_account(address)
2220 {
2221 let bytecode = created_acc.data.info.code.clone().unwrap_or_default().original_bytes();
2222 if let Some((index, _)) =
2223 self.expected_creates.iter().find_position(|expected_create| {
2224 expected_create.deployer == call.caller()
2225 && expected_create.create_scheme.eq(call.scheme().into())
2226 && expected_create.bytecode == bytecode
2227 })
2228 {
2229 self.expected_creates.swap_remove(index);
2230 }
2231 }
2232 }
2233}
2234
2235impl<FEN: FoundryEvmNetwork> InspectorExt for Cheatcodes<FEN> {
2236 fn should_use_create2_factory(&mut self, depth: usize, inputs: &CreateInputs) -> bool {
2237 if let CreateScheme::Create2 { .. } = inputs.scheme() {
2238 let target_depth = if let Some(prank) = &self.get_prank(depth) {
2239 prank.depth
2240 } else if let Some(broadcast) = &self.broadcast {
2241 broadcast.depth
2242 } else {
2243 1
2244 };
2245
2246 depth == target_depth
2247 && (self.broadcast.is_some() || self.config.always_use_create_2_factory)
2248 } else {
2249 false
2250 }
2251 }
2252
2253 fn create2_deployer(&self) -> Address {
2254 self.config.evm_opts.create2_deployer
2255 }
2256}
2257
2258impl<FEN: FoundryEvmNetwork> Cheatcodes<FEN> {
2259 #[cold]
2260 fn meter_gas(&mut self, interpreter: &mut Interpreter) {
2261 if let Some(paused_gas) = self.gas_metering.paused_frames.last() {
2262 let memory = *interpreter.gas.memory();
2265 interpreter.gas = *paused_gas;
2266 interpreter.gas.memory_mut().words_num = memory.words_num;
2267 interpreter.gas.memory_mut().expansion_cost = memory.expansion_cost;
2268 } else {
2269 self.gas_metering.paused_frames.push(interpreter.gas);
2271 }
2272 }
2273
2274 #[cold]
2275 fn meter_gas_record(
2276 &mut self,
2277 interpreter: &mut Interpreter,
2278 ecx: &mut FoundryContextFor<'_, FEN>,
2279 ) {
2280 if interpreter.bytecode.action.as_ref().and_then(|i| i.instruction_result()).is_none() {
2281 self.gas_metering.gas_records.iter_mut().for_each(|record| {
2282 let curr_depth = ecx.journal().depth();
2283 if curr_depth == record.depth {
2284 if self.gas_metering.last_gas_used != 0 {
2287 let gas_diff = interpreter
2288 .gas
2289 .total_gas_spent()
2290 .saturating_sub(self.gas_metering.last_gas_used);
2291 record.gas_used = record.gas_used.saturating_add(gas_diff);
2292 }
2293
2294 self.gas_metering.last_gas_used = interpreter.gas.total_gas_spent();
2297 }
2298 });
2299 }
2300 }
2301
2302 #[cold]
2303 fn meter_gas_end(&mut self, interpreter: &mut Interpreter) {
2304 if let Some(interpreter_action) = interpreter.bytecode.action.as_ref()
2306 && will_exit(interpreter_action)
2307 {
2308 self.gas_metering.paused_frames.pop();
2309 }
2310 }
2311
2312 #[cold]
2313 fn meter_gas_reset(&mut self, interpreter: &mut Interpreter) {
2314 let mut gas = Gas::new(interpreter.gas.limit());
2315 gas.memory_mut().words_num = interpreter.gas.memory().words_num;
2316 gas.memory_mut().expansion_cost = interpreter.gas.memory().expansion_cost;
2317 interpreter.gas = gas;
2318 self.gas_metering.reset = false;
2319 }
2320
2321 #[cold]
2322 fn meter_gas_check(&mut self, interpreter: &mut Interpreter) {
2323 if let Some(interpreter_action) = interpreter.bytecode.action.as_ref()
2324 && will_exit(interpreter_action)
2325 {
2326 if interpreter.gas.total_gas_spent()
2330 < u64::try_from(interpreter.gas.refunded()).unwrap_or_default()
2331 {
2332 interpreter.gas = Gas::new(interpreter.gas.limit());
2333 }
2334 }
2335 }
2336
2337 #[cold]
2351 fn apply_env_overrides(&mut self, interpreter: &mut Interpreter, fork_id: Option<U256>) {
2352 let Some(env_overrides) = self.env_overrides.get_mut(&fork_id) else { return };
2353 let Some(opcode) = env_overrides.pending_opcode.take() else { return };
2354 match opcode {
2355 op::BASEFEE => {
2356 if let Some(basefee) = env_overrides.basefee {
2357 Self::replace_top_of_stack(interpreter, U256::from(basefee));
2359 }
2360 }
2361 op::GASPRICE => {
2362 if let Some(gas_price) = env_overrides.gas_price {
2363 Self::replace_top_of_stack(interpreter, U256::from(gas_price));
2365 }
2366 }
2367 op::BLOBHASH => {
2368 let blob_hashes = env_overrides.blob_hashes.clone();
2369 let blobhash_index = env_overrides.pending_blobhash_index.take();
2370 if let Some(ref blob_hashes) = blob_hashes
2371 && let Some(index) = blobhash_index
2372 {
2373 let hash = blob_hashes.get(index as usize).copied().unwrap_or_default();
2376 Self::replace_top_of_stack(interpreter, hash.into());
2377 }
2378 }
2379 _ => {}
2380 }
2381 }
2382
2383 fn replace_top_of_stack(interpreter: &mut Interpreter, value: U256) {
2391 if interpreter.stack.pop().is_err() {
2392 debug_assert!(false, "env override expected opcode result on stack");
2393 return;
2394 }
2395 let _ = interpreter.stack.push(value);
2396 }
2397
2398 #[cold]
2406 fn arbitrary_storage_end(
2407 &mut self,
2408 interpreter: &mut Interpreter,
2409 ecx: &mut FoundryContextFor<'_, FEN>,
2410 ) {
2411 let (key, target_address) = if interpreter.bytecode.opcode() == op::SLOAD {
2412 (try_or_return!(interpreter.stack.peek(0)), interpreter.input.target_address)
2413 } else {
2414 return;
2415 };
2416
2417 let Some(value) = ecx.sload(target_address, key) else {
2418 return;
2419 };
2420
2421 if (value.is_cold && value.data.is_zero())
2422 || self.should_overwrite_arbitrary_storage(&target_address, key)
2423 {
2424 if self.has_arbitrary_storage(&target_address) {
2425 let arbitrary_value = self.rng().random();
2426 self.arbitrary_storage.as_mut().unwrap().save(
2427 ecx,
2428 target_address,
2429 key,
2430 arbitrary_value,
2431 );
2432 } else if self.is_arbitrary_storage_copy(&target_address) {
2433 let arbitrary_value = self.rng().random();
2434 self.arbitrary_storage.as_mut().unwrap().copy(
2435 ecx,
2436 target_address,
2437 key,
2438 arbitrary_value,
2439 );
2440 }
2441 }
2442 }
2443
2444 #[cold]
2446 fn record_accesses(&mut self, interpreter: &mut Interpreter) {
2447 let access = &mut self.accesses;
2448 match interpreter.bytecode.opcode() {
2449 op::SLOAD => {
2450 let key = try_or_return!(interpreter.stack.peek(0));
2451 access.record_read(interpreter.input.target_address, key);
2452 }
2453 op::SSTORE => {
2454 let key = try_or_return!(interpreter.stack.peek(0));
2455 access.record_write(interpreter.input.target_address, key);
2456 }
2457 _ => {}
2458 }
2459 }
2460
2461 #[cold]
2462 fn record_state_diffs(
2463 &mut self,
2464 interpreter: &mut Interpreter,
2465 ecx: &mut FoundryContextFor<'_, FEN>,
2466 ) {
2467 let Some(account_accesses) = &mut self.recorded_account_diffs_stack else { return };
2468 match interpreter.bytecode.opcode() {
2469 op::SELFDESTRUCT => {
2470 let Some(last) = account_accesses.last_mut() else { return };
2472
2473 let target = try_or_return!(interpreter.stack.peek(0));
2475 let target = Address::from_word(B256::from(target));
2476 let (initialized, old_balance, old_nonce) = ecx
2477 .journal_mut()
2478 .load_account(target)
2479 .map(|account| {
2480 (
2481 account.data.info.exists(),
2482 account.data.info.balance,
2483 account.data.info.nonce,
2484 )
2485 })
2486 .unwrap_or_default();
2487
2488 let value = ecx
2490 .balance(interpreter.input.target_address)
2491 .map(|b| b.data)
2492 .unwrap_or(U256::ZERO);
2493
2494 last.push(crate::Vm::AccountAccess {
2496 chainInfo: crate::Vm::ChainInfo {
2497 forkId: ecx.db().active_fork_id().unwrap_or_default(),
2498 chainId: U256::from(ecx.cfg().chain_id()),
2499 },
2500 accessor: interpreter.input.target_address,
2501 account: target,
2502 kind: crate::Vm::AccountAccessKind::SelfDestruct,
2503 initialized,
2504 oldBalance: old_balance,
2505 newBalance: old_balance + value,
2506 oldNonce: old_nonce,
2507 newNonce: old_nonce, value,
2509 data: Bytes::new(),
2510 reverted: false,
2511 deployedCode: Bytes::new(),
2512 storageAccesses: vec![],
2513 depth: ecx
2514 .journal()
2515 .depth()
2516 .try_into()
2517 .expect("journaled state depth exceeds u64"),
2518 });
2519 }
2520
2521 op::SLOAD => {
2522 let Some(last) = account_accesses.last_mut() else { return };
2523
2524 let key = try_or_return!(interpreter.stack.peek(0));
2525 let address = interpreter.input.target_address;
2526
2527 let present_value = if ecx.journal_mut().load_account(address).is_ok()
2531 && let Some(previous) = ecx.sload(address, key)
2532 {
2533 previous.data
2534 } else {
2535 U256::ZERO
2536 };
2537 let access = crate::Vm::StorageAccess {
2538 account: interpreter.input.target_address,
2539 slot: key.into(),
2540 isWrite: false,
2541 previousValue: present_value.into(),
2542 newValue: present_value.into(),
2543 reverted: false,
2544 };
2545 let curr_depth =
2546 ecx.journal().depth().try_into().expect("journaled state depth exceeds u64");
2547 append_storage_access(last, access, curr_depth);
2548 }
2549 op::SSTORE => {
2550 let Some(last) = account_accesses.last_mut() else { return };
2551
2552 let key = try_or_return!(interpreter.stack.peek(0));
2553 let value = try_or_return!(interpreter.stack.peek(1));
2554 let address = interpreter.input.target_address;
2555 let previous_value = if ecx.journal_mut().load_account(address).is_ok()
2558 && let Some(previous) = ecx.sload(address, key)
2559 {
2560 previous.data
2561 } else {
2562 U256::ZERO
2563 };
2564
2565 let access = crate::Vm::StorageAccess {
2566 account: address,
2567 slot: key.into(),
2568 isWrite: true,
2569 previousValue: previous_value.into(),
2570 newValue: value.into(),
2571 reverted: false,
2572 };
2573 let curr_depth =
2574 ecx.journal().depth().try_into().expect("journaled state depth exceeds u64");
2575 append_storage_access(last, access, curr_depth);
2576 }
2577
2578 op::EXTCODECOPY | op::EXTCODESIZE | op::EXTCODEHASH | op::BALANCE => {
2580 let kind = match interpreter.bytecode.opcode() {
2581 op::EXTCODECOPY => crate::Vm::AccountAccessKind::Extcodecopy,
2582 op::EXTCODESIZE => crate::Vm::AccountAccessKind::Extcodesize,
2583 op::EXTCODEHASH => crate::Vm::AccountAccessKind::Extcodehash,
2584 op::BALANCE => crate::Vm::AccountAccessKind::Balance,
2585 _ => unreachable!(),
2586 };
2587 let address =
2588 Address::from_word(B256::from(try_or_return!(interpreter.stack.peek(0))));
2589 let (initialized, balance, nonce) =
2590 if let Ok(acc) = ecx.journal_mut().load_account(address) {
2591 (acc.data.info.exists(), acc.data.info.balance, acc.data.info.nonce)
2592 } else {
2593 (false, U256::ZERO, 0)
2594 };
2595 let curr_depth =
2596 ecx.journal().depth().try_into().expect("journaled state depth exceeds u64");
2597 let account_access = crate::Vm::AccountAccess {
2598 chainInfo: crate::Vm::ChainInfo {
2599 forkId: ecx.db().active_fork_id().unwrap_or_default(),
2600 chainId: U256::from(ecx.cfg().chain_id()),
2601 },
2602 accessor: interpreter.input.target_address,
2603 account: address,
2604 kind,
2605 initialized,
2606 oldBalance: balance,
2607 newBalance: balance,
2608 oldNonce: nonce,
2609 newNonce: nonce, value: U256::ZERO,
2611 data: Bytes::new(),
2612 reverted: false,
2613 deployedCode: Bytes::new(),
2614 storageAccesses: vec![],
2615 depth: curr_depth,
2616 };
2617 if let Some(last) = account_accesses.last_mut() {
2620 last.push(account_access);
2621 } else {
2622 account_accesses.push(vec![account_access]);
2623 }
2624 }
2625 _ => {}
2626 }
2627 }
2628
2629 #[cold]
2634 fn check_mem_opcodes(&self, interpreter: &mut Interpreter, depth: u64) {
2635 let Some(ranges) = self.allowed_mem_writes.get(&depth) else {
2636 return;
2637 };
2638
2639 macro_rules! mem_opcode_match {
2648 ($(($opcode:ident, $offset_depth:expr, $size_depth:expr, $writes:expr)),* $(,)?) => {
2649 match interpreter.bytecode.opcode() {
2650 op::MSTORE => {
2655 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2657
2658 if !ranges.iter().any(|range| {
2661 range.contains(&offset) && range.contains(&(offset + 31))
2662 }) {
2663 let value = try_or_return!(interpreter.stack.peek(1)).to_be_bytes::<32>();
2668 if value[..SELECTOR_LEN] == stopExpectSafeMemoryCall::SELECTOR {
2669 return
2670 }
2671
2672 disallowed_mem_write(offset, 32, interpreter, ranges);
2673 return
2674 }
2675 }
2676 op::MSTORE8 => {
2677 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2679
2680 if !ranges.iter().any(|range| range.contains(&offset)) {
2683 disallowed_mem_write(offset, 1, interpreter, ranges);
2684 return
2685 }
2686 }
2687
2688 op::MLOAD => {
2693 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2695
2696 if offset >= interpreter.memory.size() as u64 && !ranges.iter().any(|range| {
2700 range.contains(&offset) && range.contains(&(offset + 31))
2701 }) {
2702 disallowed_mem_write(offset, 32, interpreter, ranges);
2703 return
2704 }
2705 }
2706
2707 op::CALL => {
2712 let dest_offset = try_or_return!(interpreter.stack.peek(5)).saturating_to::<u64>();
2714
2715 let size = try_or_return!(interpreter.stack.peek(6)).saturating_to::<u64>();
2717
2718 let fail_cond = !ranges.iter().any(|range| {
2722 range.contains(&dest_offset) &&
2723 range.contains(&(dest_offset + size.saturating_sub(1)))
2724 });
2725
2726 if fail_cond {
2729 let to = Address::from_word(try_or_return!(interpreter.stack.peek(1)).to_be_bytes::<32>().into());
2733 if to == CHEATCODE_ADDRESS {
2734 let args_offset = try_or_return!(interpreter.stack.peek(3)).saturating_to::<usize>();
2735 let args_size = try_or_return!(interpreter.stack.peek(4)).saturating_to::<usize>();
2736 let memory_word = interpreter.memory.slice_len(args_offset, args_size);
2737 if memory_word[..SELECTOR_LEN] == stopExpectSafeMemoryCall::SELECTOR {
2738 return
2739 }
2740 }
2741
2742 disallowed_mem_write(dest_offset, size, interpreter, ranges);
2743 return
2744 }
2745 }
2746
2747 $(op::$opcode => {
2748 let dest_offset = try_or_return!(interpreter.stack.peek($offset_depth)).saturating_to::<u64>();
2750
2751 let size = try_or_return!(interpreter.stack.peek($size_depth)).saturating_to::<u64>();
2753
2754 let fail_cond = !ranges.iter().any(|range| {
2758 range.contains(&dest_offset) &&
2759 range.contains(&(dest_offset + size.saturating_sub(1)))
2760 }) && ($writes ||
2761 [dest_offset, (dest_offset + size).saturating_sub(1)].into_iter().any(|offset| {
2762 offset >= interpreter.memory.size() as u64
2763 })
2764 );
2765
2766 if fail_cond {
2769 disallowed_mem_write(dest_offset, size, interpreter, ranges);
2770 return
2771 }
2772 })*
2773
2774 _ => {}
2775 }
2776 }
2777 }
2778
2779 mem_opcode_match!(
2782 (CALLDATACOPY, 0, 2, true),
2783 (CODECOPY, 0, 2, true),
2784 (RETURNDATACOPY, 0, 2, true),
2785 (EXTCODECOPY, 1, 3, true),
2786 (CALLCODE, 5, 6, true),
2787 (STATICCALL, 4, 5, true),
2788 (DELEGATECALL, 4, 5, true),
2789 (KECCAK256, 0, 1, false),
2790 (LOG0, 0, 1, false),
2791 (LOG1, 0, 1, false),
2792 (LOG2, 0, 1, false),
2793 (LOG3, 0, 1, false),
2794 (LOG4, 0, 1, false),
2795 (CREATE, 1, 2, false),
2796 (CREATE2, 1, 2, false),
2797 (RETURN, 0, 1, false),
2798 (REVERT, 0, 1, false),
2799 );
2800 }
2801
2802 #[cold]
2803 fn set_gas_limit_type(&mut self, interpreter: &mut Interpreter) {
2804 match interpreter.bytecode.opcode() {
2805 op::CREATE2 => self.dynamic_gas_limit = true,
2806 op::CALL => {
2807 self.dynamic_gas_limit =
2810 try_or_return!(interpreter.stack.peek(0)) >= interpreter.gas.remaining() - 100
2811 }
2812 _ => self.dynamic_gas_limit = false,
2813 }
2814 }
2815}
2816
2817fn disallowed_mem_write(
2823 dest_offset: u64,
2824 size: u64,
2825 interpreter: &mut Interpreter,
2826 ranges: &[Range<u64>],
2827) {
2828 let revert_string = format!(
2829 "memory write at offset 0x{:02X} of size 0x{:02X} not allowed; safe range: {}",
2830 dest_offset,
2831 size,
2832 ranges.iter().map(|r| format!("[0x{:02X}, 0x{:02X})", r.start, r.end)).join(" U ")
2833 );
2834
2835 interpreter.bytecode.set_action(InterpreterAction::new_return(
2836 InstructionResult::Revert,
2837 Bytes::from(revert_string.into_bytes()),
2838 interpreter.gas,
2839 ));
2840}
2841
2842const fn access_is_call(kind: crate::Vm::AccountAccessKind) -> bool {
2844 matches!(
2845 kind,
2846 crate::Vm::AccountAccessKind::Call
2847 | crate::Vm::AccountAccessKind::StaticCall
2848 | crate::Vm::AccountAccessKind::CallCode
2849 | crate::Vm::AccountAccessKind::DelegateCall
2850 )
2851}
2852
2853fn record_logs(recorded_logs: &mut Option<Vec<Vm::Log>>, log: &Log) {
2855 if let Some(storage_recorded_logs) = recorded_logs {
2856 storage_recorded_logs.push(Vm::Log {
2857 topics: log.data.topics().to_vec(),
2858 data: log.data.data.clone(),
2859 emitter: log.address,
2860 });
2861 }
2862}
2863
2864fn append_storage_access(
2866 last: &mut Vec<AccountAccess>,
2867 storage_access: crate::Vm::StorageAccess,
2868 storage_depth: u64,
2869) {
2870 if !last.is_empty() && last.first().unwrap().depth < storage_depth {
2872 if last.len() == 1 {
2878 last.first_mut().unwrap().storageAccesses.push(storage_access);
2879 } else {
2880 let last_record = last.last_mut().unwrap();
2881 if last_record.kind as u8 == crate::Vm::AccountAccessKind::Resume as u8 {
2882 last_record.storageAccesses.push(storage_access);
2883 } else {
2884 let entry = last.first().unwrap();
2885 let resume_record = crate::Vm::AccountAccess {
2886 chainInfo: crate::Vm::ChainInfo {
2887 forkId: entry.chainInfo.forkId,
2888 chainId: entry.chainInfo.chainId,
2889 },
2890 accessor: entry.accessor,
2891 account: entry.account,
2892 kind: crate::Vm::AccountAccessKind::Resume,
2893 initialized: entry.initialized,
2894 storageAccesses: vec![storage_access],
2895 reverted: entry.reverted,
2896 oldBalance: U256::ZERO,
2898 newBalance: U256::ZERO,
2899 oldNonce: 0,
2900 newNonce: 0,
2901 value: U256::ZERO,
2902 data: Bytes::new(),
2903 deployedCode: Bytes::new(),
2904 depth: entry.depth,
2905 };
2906 last.push(resume_record);
2907 }
2908 }
2909 }
2910}
2911
2912const fn cheatcode_of<T: spec::CheatcodeDef>(_: &T) -> &'static spec::Cheatcode<'static> {
2914 T::CHEATCODE
2915}
2916
2917fn cheatcode_name(cheat: &spec::Cheatcode<'static>) -> &'static str {
2918 cheat.func.signature.split('(').next().unwrap()
2919}
2920
2921const fn cheatcode_id(cheat: &spec::Cheatcode<'static>) -> &'static str {
2922 cheat.func.id
2923}
2924
2925const fn cheatcode_signature(cheat: &spec::Cheatcode<'static>) -> &'static str {
2926 cheat.func.signature
2927}
2928
2929fn apply_dispatch<FEN: FoundryEvmNetwork>(
2931 calls: &Vm::VmCalls,
2932 ccx: &mut CheatsCtxt<'_, '_, FEN>,
2933 executor: &mut dyn CheatcodesExecutor<FEN>,
2934) -> Result {
2935 macro_rules! get_cheatcode {
2937 ($($variant:ident),*) => {
2938 match calls {
2939 $(Vm::VmCalls::$variant(cheat) => cheatcode_of(cheat),)*
2940 }
2941 };
2942 }
2943 let cheat = vm_calls!(get_cheatcode);
2944
2945 let _guard = debug_span!(target: "cheatcodes", "apply", id = %cheatcode_id(cheat)).entered();
2946 trace!(target: "cheatcodes", cheat = %cheatcode_signature(cheat), "applying");
2947
2948 if let spec::Status::Deprecated(replacement) = cheat.status {
2949 ccx.state.deprecated.insert(cheatcode_signature(cheat), replacement);
2950 }
2951
2952 macro_rules! dispatch {
2954 ($($variant:ident),*) => {
2955 match calls {
2956 $(Vm::VmCalls::$variant(cheat) => Cheatcode::apply_full(cheat, ccx, executor),)*
2957 }
2958 };
2959 }
2960 let mut result = vm_calls!(dispatch);
2961
2962 if let Err(e) = &mut result
2964 && e.is_str()
2965 {
2966 let name = cheatcode_name(cheat);
2967 if !name.contains("assert") && name != "rpcUrl" {
2971 *e = fmt_err!("vm.{name}: {e}");
2972 }
2973 }
2974
2975 trace!(
2976 target: "cheatcodes",
2977 return = %match &result {
2978 Ok(b) => hex::encode(b),
2979 Err(e) => e.to_string(),
2980 }
2981 );
2982
2983 result
2984}
2985
2986const fn will_exit(action: &InterpreterAction) -> bool {
2988 match action {
2989 InterpreterAction::Return(result) => {
2990 result.result.is_ok_or_revert() || result.result.is_error()
2991 }
2992 _ => false,
2993 }
2994}