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 interpreter::{
60 CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, FrameInput, Gas,
61 InstructionResult, Interpreter, InterpreterAction, InterpreterResult,
62 interpreter_types::{Jumps, LoopControl, MemoryTr},
63 return_ok,
64 },
65};
66use serde_json::Value;
67use std::{
68 cmp::max,
69 collections::{BTreeMap, VecDeque},
70 fmt::Debug,
71 fs::File,
72 io::BufReader,
73 ops::Range,
74 path::PathBuf,
75 sync::{Arc, OnceLock},
76};
77
78mod utils;
79
80pub mod analysis;
81pub use analysis::CheatcodeAnalysis;
82
83pub trait CheatcodesExecutor<FEN: FoundryEvmNetwork> {
85 fn with_nested_evm(
88 &mut self,
89 cheats: &mut Cheatcodes<FEN>,
90 ecx: &mut FoundryContextFor<'_, FEN>,
91 f: NestedEvmClosure<'_, SpecFor<FEN>, BlockEnvFor<FEN>, TxEnvFor<FEN>>,
92 ) -> Result<(), EVMError<DatabaseError>>;
93
94 fn transact_on_db(
96 &mut self,
97 cheats: &mut Cheatcodes<FEN>,
98 ecx: &mut FoundryContextFor<'_, FEN>,
99 fork_id: Option<U256>,
100 transaction: B256,
101 ) -> eyre::Result<()>;
102
103 fn transact_from_tx_on_db(
105 &mut self,
106 cheats: &mut Cheatcodes<FEN>,
107 ecx: &mut FoundryContextFor<'_, FEN>,
108 tx: TxEnvFor<FEN>,
109 ) -> eyre::Result<()>;
110
111 #[allow(clippy::type_complexity)]
116 fn with_fresh_nested_evm(
117 &mut self,
118 cheats: &mut Cheatcodes<FEN>,
119 db: &mut <FoundryContextFor<'_, FEN> as ContextTr>::Db,
120 evm_env: EvmEnv<SpecFor<FEN>, BlockEnvFor<FEN>>,
121 f: NestedEvmClosure<'_, SpecFor<FEN>, BlockEnvFor<FEN>, TxEnvFor<FEN>>,
122 ) -> Result<EvmEnv<SpecFor<FEN>, BlockEnvFor<FEN>>, EVMError<DatabaseError>>;
123
124 fn console_log(&mut self, msg: &str);
126
127 fn tracing_inspector(&mut self) -> Option<&mut TracingInspector> {
129 None
130 }
131
132 fn set_in_inner_context(&mut self, _enabled: bool, _original_origin: Option<Address>) {}
136}
137
138pub(crate) fn exec_create<FEN: FoundryEvmNetwork>(
140 executor: &mut dyn CheatcodesExecutor<FEN>,
141 inputs: CreateInputs,
142 ccx: &mut CheatsCtxt<'_, '_, FEN>,
143) -> std::result::Result<CreateOutcome, EVMError<DatabaseError>> {
144 let mut inputs = Some(inputs);
145 let mut outcome = None;
146 executor.with_nested_evm(ccx.state, ccx.ecx, &mut |evm| {
147 let inputs = inputs.take().unwrap();
148 evm.journal_inner_mut().depth += 1;
149
150 let frame = FrameInput::Create(Box::new(inputs));
151
152 let result = match evm.run_execution(frame)? {
153 FrameResult::Call(_) => unreachable!(),
154 FrameResult::Create(create) => create,
155 };
156
157 evm.journal_inner_mut().depth -= 1;
158
159 outcome = Some(result);
160 Ok(())
161 })?;
162 Ok(outcome.unwrap())
163}
164
165#[derive(Debug, Default, Clone, Copy)]
168struct TransparentCheatcodesExecutor;
169
170impl<FEN: FoundryEvmNetwork> CheatcodesExecutor<FEN> for TransparentCheatcodesExecutor {
171 fn with_nested_evm(
172 &mut self,
173 cheats: &mut Cheatcodes<FEN>,
174 ecx: &mut FoundryContextFor<'_, FEN>,
175 f: NestedEvmClosure<'_, SpecFor<FEN>, BlockEnvFor<FEN>, TxEnvFor<FEN>>,
176 ) -> Result<(), EVMError<DatabaseError>> {
177 with_cloned_context(ecx, |db, evm_env, journal_inner| {
178 let mut evm = FEN::EvmFactory::default().create_foundry_nested_evm(db, evm_env, cheats);
179 *evm.journal_inner_mut() = journal_inner;
180 f(&mut *evm)?;
181 let sub_inner = evm.journal_inner_mut().clone();
182 let sub_evm_env = evm.to_evm_env();
183 Ok((sub_evm_env, sub_inner))
184 })
185 }
186
187 fn with_fresh_nested_evm(
188 &mut self,
189 cheats: &mut Cheatcodes<FEN>,
190 db: &mut <FoundryContextFor<'_, FEN> as ContextTr>::Db,
191 evm_env: EvmEnv<SpecFor<FEN>, BlockEnvFor<FEN>>,
192 f: NestedEvmClosure<'_, SpecFor<FEN>, BlockEnvFor<FEN>, TxEnvFor<FEN>>,
193 ) -> Result<EvmEnv<SpecFor<FEN>, BlockEnvFor<FEN>>, EVMError<DatabaseError>> {
194 let mut evm = FEN::EvmFactory::default().create_foundry_nested_evm(db, evm_env, cheats);
195 f(&mut *evm)?;
196 Ok(evm.to_evm_env())
197 }
198
199 fn transact_on_db(
200 &mut self,
201 cheats: &mut Cheatcodes<FEN>,
202 ecx: &mut FoundryContextFor<'_, FEN>,
203 fork_id: Option<U256>,
204 transaction: B256,
205 ) -> eyre::Result<()> {
206 let evm_env = ecx.evm_clone();
207 let (db, inner) = ecx.db_journal_inner_mut();
208 db.transact(fork_id, transaction, evm_env, inner, cheats)
209 }
210
211 fn transact_from_tx_on_db(
212 &mut self,
213 cheats: &mut Cheatcodes<FEN>,
214 ecx: &mut FoundryContextFor<'_, FEN>,
215 tx: TxEnvFor<FEN>,
216 ) -> eyre::Result<()> {
217 let evm_env = ecx.evm_clone();
218 let (db, inner) = ecx.db_journal_inner_mut();
219 db.transact_from_tx(tx, evm_env, inner, cheats)
220 }
221
222 fn console_log(&mut self, _msg: &str) {}
223}
224
225macro_rules! try_or_return {
226 ($e:expr) => {
227 match $e {
228 Ok(v) => v,
229 Err(_) => return,
230 }
231 };
232}
233
234#[derive(Debug, Default)]
236pub struct TestContext {
237 pub opened_read_files: HashMap<PathBuf, BufReader<File>>,
239}
240
241impl Clone for TestContext {
243 fn clone(&self) -> Self {
244 Default::default()
245 }
246}
247
248impl TestContext {
249 pub fn clear(&mut self) {
251 self.opened_read_files.clear();
252 }
253}
254
255#[derive(Clone, Debug)]
257pub struct BroadcastableTransaction<N: Network = Ethereum> {
258 pub rpc: Option<String>,
260 pub transaction: TransactionMaybeSigned<N>,
262}
263
264#[derive(Clone, Debug, Copy)]
265pub struct RecordDebugStepInfo {
266 pub start_node_idx: usize,
268 pub original_tracer_config: TracingInspectorConfig,
270}
271
272#[derive(Clone, Debug, Default)]
301pub struct EnvOverrides {
302 pub basefee: Option<u64>,
304 pub gas_price: Option<u128>,
306 pub blob_hashes: Option<Vec<B256>>,
308 pub pre_override_gas_price: Option<u128>,
312 pub pre_override_tx_type: Option<u8>,
316 pub pre_override_blob_hashes: Option<Vec<B256>>,
319 pending_opcode: Option<u8>,
324 pending_blobhash_index: Option<u64>,
328}
329
330impl EnvOverrides {
331 #[inline]
333 pub const fn is_any_set(&self) -> bool {
334 self.basefee.is_some() || self.gas_price.is_some() || self.blob_hashes.is_some()
335 }
336}
337
338#[derive(Clone, Debug, Default)]
340pub struct GasMetering {
341 pub paused: bool,
343 pub touched: bool,
346 pub reset: bool,
348 pub paused_frames: Vec<Gas>,
350
351 pub active_gas_snapshot: Option<(String, String)>,
353
354 pub last_call_gas: Option<crate::Vm::Gas>,
357
358 pub recording: bool,
360 pub last_gas_used: u64,
362 pub gas_records: Vec<GasRecord>,
364}
365
366impl GasMetering {
367 pub const fn start(&mut self) {
369 self.recording = true;
370 }
371
372 pub const fn stop(&mut self) {
374 self.recording = false;
375 }
376
377 pub fn resume(&mut self) {
379 if self.paused {
380 self.paused = false;
381 self.touched = true;
382 }
383 self.paused_frames.clear();
384 }
385
386 pub fn reset(&mut self) {
388 self.paused = false;
389 self.touched = true;
390 self.reset = true;
391 self.paused_frames.clear();
392 }
393}
394
395#[derive(Clone, Debug, Default)]
397pub struct ArbitraryStorage {
398 pub values: HashMap<Address, HashMap<U256, U256>>,
402 pub copies: HashMap<Address, Address>,
404 pub overwrites: HashSet<Address>,
406}
407
408impl ArbitraryStorage {
409 pub fn mark_arbitrary(&mut self, address: &Address, overwrite: bool) {
411 self.values.insert(*address, HashMap::default());
412 if overwrite {
413 self.overwrites.insert(*address);
414 } else {
415 self.overwrites.remove(address);
416 }
417 }
418
419 pub fn mark_copy(&mut self, from: &Address, to: &Address) {
421 if self.values.contains_key(from) {
422 self.copies.insert(*to, *from);
423 }
424 }
425
426 pub fn save<CTX: ContextTr>(
430 &mut self,
431 ecx: &mut CTX,
432 address: Address,
433 slot: U256,
434 data: U256,
435 ) {
436 self.values.get_mut(&address).expect("missing arbitrary address entry").insert(slot, data);
437 if ecx.journal_mut().load_account(address).is_ok() {
438 ecx.journal_mut()
439 .sstore(address, slot, data)
440 .expect("could not set arbitrary storage value");
441 }
442 }
443
444 pub fn copy<CTX: ContextTr>(
450 &mut self,
451 ecx: &mut CTX,
452 target: Address,
453 slot: U256,
454 new_value: U256,
455 ) -> U256 {
456 let source = self.copies.get(&target).expect("missing arbitrary copy target entry");
457 let storage_cache = self.values.get_mut(source).expect("missing arbitrary source storage");
458 let value = match storage_cache.get(&slot) {
459 Some(value) => *value,
460 None => {
461 storage_cache.insert(slot, new_value);
462 if ecx.journal_mut().load_account(*source).is_ok() {
464 ecx.journal_mut()
465 .sstore(*source, slot, new_value)
466 .expect("could not copy arbitrary storage value");
467 }
468 new_value
469 }
470 };
471 if ecx.journal_mut().load_account(target).is_ok() {
473 ecx.journal_mut().sstore(target, slot, value).expect("could not set storage");
474 }
475 value
476 }
477}
478
479pub type BroadcastableTransactions<N> = VecDeque<BroadcastableTransaction<N>>;
481
482#[derive(Clone, Debug)]
500pub struct Cheatcodes<FEN: FoundryEvmNetwork = EthEvmNetwork> {
501 pub analysis: Option<CheatcodeAnalysis>,
503
504 pub block: Option<BlockEnvFor<FEN>>,
509
510 pub active_delegations: Vec<SignedAuthorization>,
514
515 pub active_blob_sidecar: Option<BlobTransactionSidecarVariant>,
517
518 pub gas_price: Option<u128>,
523
524 pub labels: AddressHashMap<String>,
526
527 pub pranks: BTreeMap<usize, Prank>,
529
530 pub expected_revert: Option<ExpectedRevert>,
532
533 pub assume_no_revert: Option<AssumeNoRevert>,
535
536 pub fork_revert_diagnostic: Option<RevertDiagnostic>,
538
539 pub accesses: RecordAccess,
541
542 pub recording_accesses: bool,
544
545 pub recorded_account_diffs_stack: Option<Vec<Vec<AccountAccess>>>,
551
552 pub record_debug_steps_info: Option<RecordDebugStepInfo>,
554
555 pub recorded_logs: Option<Vec<crate::Vm::Log>>,
557
558 pub mocked_calls: HashMap<Address, BTreeMap<MockCallDataContext, VecDeque<MockCallReturnData>>>,
561
562 pub mocked_functions: HashMap<Address, HashMap<Bytes, Address>>,
564
565 pub expected_calls: ExpectedCallTracker,
567 pub expected_emits: ExpectedEmitTracker,
569 pub expected_creates: Vec<ExpectedCreate>,
571
572 pub allowed_mem_writes: HashMap<u64, Vec<Range<u64>>>,
574
575 pub broadcast: Option<Broadcast>,
577
578 pub broadcastable_transactions: BroadcastableTransactions<FEN::Network>,
580
581 pub access_list: Option<AccessList>,
583
584 pub config: Arc<CheatsConfig>,
586
587 pub test_context: TestContext,
589
590 pub fs_commit: bool,
593
594 pub serialized_jsons: BTreeMap<String, BTreeMap<String, Value>>,
597
598 pub eth_deals: Vec<DealRecord>,
600
601 pub gas_metering: GasMetering,
603
604 pub gas_snapshots: BTreeMap<String, BTreeMap<String, String>>,
607
608 pub mapping_slots: Option<AddressHashMap<MappingSlots>>,
610
611 pub pc: usize,
613 pub breakpoints: Breakpoints,
616
617 pub intercept_next_create_call: bool,
619
620 test_runner: Option<TestRunner>,
623
624 pub ignored_traces: IgnoredTraces,
626
627 pub arbitrary_storage: Option<ArbitraryStorage>,
629
630 pub deprecated: HashMap<&'static str, Option<&'static str>>,
632 pub wallets: Option<Wallets>,
634 signatures_identifier: OnceLock<Option<SignaturesIdentifier>>,
636 pub dynamic_gas_limit: bool,
638 pub execution_evm_version: Option<SpecFor<FEN>>,
640
641 pub env_overrides: HashMap<Option<LocalForkId>, EnvOverrides>,
651
652 pub env_overrides_snapshots: HashMap<U256, HashMap<Option<LocalForkId>, EnvOverrides>>,
662
663 pub in_isolation_context: bool,
673}
674
675impl Default for Cheatcodes {
679 fn default() -> Self {
680 Self::new(Arc::default())
681 }
682}
683
684impl<FEN: FoundryEvmNetwork> Cheatcodes<FEN> {
685 pub fn new(config: Arc<CheatsConfig>) -> Self {
687 Self {
688 analysis: None,
689 fs_commit: true,
690 labels: config.labels.clone(),
691 config,
692 block: Default::default(),
693 active_delegations: Default::default(),
694 active_blob_sidecar: Default::default(),
695 gas_price: Default::default(),
696 pranks: Default::default(),
697 expected_revert: Default::default(),
698 assume_no_revert: Default::default(),
699 fork_revert_diagnostic: Default::default(),
700 accesses: Default::default(),
701 recording_accesses: Default::default(),
702 recorded_account_diffs_stack: Default::default(),
703 recorded_logs: Default::default(),
704 record_debug_steps_info: Default::default(),
705 mocked_calls: Default::default(),
706 mocked_functions: Default::default(),
707 expected_calls: Default::default(),
708 expected_emits: Default::default(),
709 expected_creates: Default::default(),
710 allowed_mem_writes: Default::default(),
711 broadcast: Default::default(),
712 broadcastable_transactions: Default::default(),
713 access_list: Default::default(),
714 test_context: Default::default(),
715 serialized_jsons: Default::default(),
716 eth_deals: Default::default(),
717 gas_metering: Default::default(),
718 gas_snapshots: Default::default(),
719 mapping_slots: Default::default(),
720 pc: Default::default(),
721 breakpoints: Default::default(),
722 intercept_next_create_call: Default::default(),
723 test_runner: Default::default(),
724 ignored_traces: Default::default(),
725 arbitrary_storage: Default::default(),
726 deprecated: Default::default(),
727 wallets: Default::default(),
728 signatures_identifier: Default::default(),
729 dynamic_gas_limit: Default::default(),
730 execution_evm_version: None,
731 env_overrides: Default::default(),
732 env_overrides_snapshots: Default::default(),
733 in_isolation_context: false,
734 }
735 }
736
737 pub fn set_analysis(&mut self, analysis: CheatcodeAnalysis) {
739 self.analysis = Some(analysis);
740 }
741
742 pub fn env_overrides_for(&self, fork_id: Option<U256>) -> Option<&EnvOverrides> {
744 self.env_overrides.get(&fork_id).filter(|o| o.is_any_set())
745 }
746
747 pub fn env_overrides_for_mut(&mut self, fork_id: Option<U256>) -> &mut EnvOverrides {
750 self.env_overrides.entry(fork_id).or_default()
751 }
752
753 pub fn get_prank(&self, depth: usize) -> Option<&Prank> {
757 self.pranks.range(..=depth).last().map(|(_, prank)| prank)
758 }
759
760 pub fn wallets(&mut self) -> &Wallets {
762 self.wallets.get_or_insert_with(|| Wallets::new(MultiWallet::default(), None))
763 }
764
765 pub fn set_wallets(&mut self, wallets: Wallets) {
767 self.wallets = Some(wallets);
768 }
769
770 pub fn add_delegation(&mut self, authorization: SignedAuthorization) {
772 self.active_delegations.push(authorization);
773 }
774
775 pub fn signatures_identifier(&self) -> Option<&SignaturesIdentifier> {
777 self.signatures_identifier.get_or_init(|| SignaturesIdentifier::new(true).ok()).as_ref()
778 }
779
780 fn apply_cheatcode(
782 &mut self,
783 ecx: &mut FoundryContextFor<'_, FEN>,
784 call: &CallInputs,
785 executor: &mut dyn CheatcodesExecutor<FEN>,
786 ) -> Result {
787 let decoded = Vm::VmCalls::abi_decode(&call.input.bytes(ecx)).map_err(|e| {
789 if let alloy_sol_types::Error::UnknownSelector { name: _, selector } = e {
790 let msg = format!(
791 "unknown cheatcode with selector {selector}; \
792 you may have a mismatch between the `Vm` interface (likely in `forge-std`) \
793 and the `forge` version"
794 );
795 return alloy_sol_types::Error::Other(std::borrow::Cow::Owned(msg));
796 }
797 e
798 })?;
799
800 let caller = call.caller;
801
802 ecx.db_mut().ensure_cheatcode_access_forking_mode(&caller)?;
805
806 apply_dispatch(
807 &decoded,
808 &mut CheatsCtxt { state: self, ecx, gas_limit: call.gas_limit, caller },
809 executor,
810 )
811 }
812
813 fn allow_cheatcodes_on_create(
819 &self,
820 ecx: &mut FoundryContextFor<FEN>,
821 caller: Address,
822 created_address: Address,
823 ) {
824 if ecx.journal().depth() <= 1 || ecx.db().has_cheatcode_access(&caller) {
825 ecx.db_mut().allow_cheatcode_access(created_address);
826 }
827 }
828
829 fn apply_accesslist(&mut self, ecx: &mut FoundryContextFor<FEN>) {
835 if let Some(access_list) = &self.access_list {
836 ecx.tx_mut().set_access_list(access_list.clone());
837
838 if ecx.tx().tx_type() == TransactionType::Legacy as u8 {
839 ecx.tx_mut().set_tx_type(TransactionType::Eip2930 as u8);
840 }
841 }
842 }
843
844 pub fn on_revert(&mut self, ecx: &mut FoundryContextFor<FEN>) {
849 trace!(deals=?self.eth_deals.len(), "rolling back deals");
850
851 if self.expected_revert.is_some() {
853 return;
854 }
855
856 if ecx.journal().depth() > 0 {
858 return;
859 }
860
861 while let Some(record) = self.eth_deals.pop() {
865 if let Some(acc) = ecx.journal_mut().evm_state_mut().get_mut(&record.address) {
866 acc.info.balance = record.old_balance;
867 }
868 }
869 }
870
871 pub fn call_with_executor(
872 &mut self,
873 ecx: &mut FoundryContextFor<'_, FEN>,
874 call: &mut CallInputs,
875 executor: &mut dyn CheatcodesExecutor<FEN>,
876 ) -> Option<CallOutcome> {
877 if let Some(spec_id) = self.execution_evm_version {
879 ecx.cfg_mut().set_spec_and_mainnet_gas_params(spec_id);
880 }
881
882 let gas = Gas::new(call.gas_limit);
883 let curr_depth = ecx.journal().depth();
884
885 if curr_depth == 0 {
889 let sender = ecx.tx().caller();
890 let account = match super::evm::journaled_account(ecx, sender) {
891 Ok(account) => account,
892 Err(err) => {
893 return Some(CallOutcome {
894 result: InterpreterResult {
895 result: InstructionResult::Revert,
896 output: err.abi_encode().into(),
897 gas,
898 },
899 memory_offset: call.return_memory_offset.clone(),
900 was_precompile_called: false,
901 precompile_call_logs: vec![],
902 charged_new_account_state_gas: false,
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 charged_new_account_state_gas: false,
924 }),
925 Err(err) => Some(CallOutcome {
926 result: InterpreterResult {
927 result: InstructionResult::Revert,
928 output: err.abi_encode().into(),
929 gas,
930 },
931 memory_offset: call.return_memory_offset.clone(),
932 was_precompile_called: false,
933 precompile_call_logs: vec![],
934 charged_new_account_state_gas: false,
935 }),
936 };
937 }
938
939 if call.target_address == HARDHAT_CONSOLE_ADDRESS {
940 return None;
941 }
942
943 if let Some(expected) = &mut self.expected_revert {
947 expected.max_depth = max(curr_depth + 1, expected.max_depth);
948 }
949
950 if let Some(expected_calls_for_target) = self.expected_calls.get_mut(&call.bytecode_address)
954 {
955 for (calldata, (expected, actual_count)) in expected_calls_for_target {
957 if calldata.len() <= call.input.len() &&
960 *calldata == call.input.bytes(ecx)[..calldata.len()] &&
962 expected
964 .value.is_none_or(|value| Some(value) == call.transfer_value()) &&
965 expected.gas.is_none_or(|gas| gas == call.gas_limit) &&
967 expected.min_gas.is_none_or(|min_gas| min_gas <= call.gas_limit)
969 {
970 *actual_count += 1;
971 }
972 }
973 }
974
975 if let Some(prank) = &self.get_prank(curr_depth) {
977 if prank.delegate_call
979 && curr_depth == prank.depth
980 && call.scheme == CallScheme::DelegateCall
981 {
982 call.target_address = prank.new_caller;
983 call.caller = prank.new_caller;
984 if let Some(new_origin) = prank.new_origin {
985 ecx.tx_mut().set_caller(new_origin);
986 }
987 }
988
989 if curr_depth >= prank.depth && call.caller == prank.prank_caller {
990 let prank_applied = if curr_depth == prank.depth {
992 let _ = journaled_account(ecx, prank.new_caller);
994 call.caller = prank.new_caller;
995 true
996 } else {
997 false
998 };
999
1000 let prank_applied = if let Some(new_origin) = prank.new_origin {
1002 ecx.tx_mut().set_caller(new_origin);
1003 true
1004 } else {
1005 prank_applied
1006 };
1007
1008 if prank_applied && let Some(applied_prank) = prank.first_time_applied() {
1010 self.pranks.insert(curr_depth, applied_prank);
1011 }
1012 }
1013 }
1014
1015 if let Some(mocks) = self.mocked_calls.get_mut(&call.bytecode_address) {
1017 let ctx = MockCallDataContext {
1018 calldata: call.input.bytes(ecx),
1019 value: call.transfer_value(),
1020 };
1021
1022 if let Some(return_data_queue) = match mocks.get_mut(&ctx) {
1023 Some(queue) => Some(queue),
1024 None => mocks
1025 .iter_mut()
1026 .find(|(mock, _)| {
1027 call.input.bytes(ecx).get(..mock.calldata.len()) == Some(&mock.calldata[..])
1028 && mock.value.is_none_or(|value| Some(value) == call.transfer_value())
1029 })
1030 .map(|(_, v)| v),
1031 } && let Some(return_data) = return_data_queue.front().map(|x| x.to_owned())
1032 {
1033 if let Some(value) = call.transfer_value() {
1034 let checkpoint = ecx.journal_mut().checkpoint();
1035 match ecx.journal_mut().transfer_loaded(
1036 call.transfer_from(),
1037 call.transfer_to(),
1038 value,
1039 ) {
1040 None => {
1041 if return_data.ret_type.is_ok() {
1042 ecx.journal_mut().checkpoint_commit();
1043 } else {
1044 ecx.journal_mut().checkpoint_revert(checkpoint);
1045 }
1046 }
1047 Some(err) => {
1048 ecx.journal_mut().checkpoint_revert(checkpoint);
1049 return Some(CallOutcome {
1050 result: InterpreterResult {
1051 result: err.into(),
1052 output: Bytes::new(),
1053 gas,
1054 },
1055 memory_offset: call.return_memory_offset.clone(),
1056 was_precompile_called: false,
1057 precompile_call_logs: vec![],
1058 charged_new_account_state_gas: false,
1059 });
1060 }
1061 }
1062 }
1063
1064 if return_data_queue.len() > 1 {
1066 return_data_queue.pop_front();
1067 }
1068
1069 return Some(CallOutcome {
1070 result: InterpreterResult {
1071 result: return_data.ret_type,
1072 output: return_data.data,
1073 gas,
1074 },
1075 memory_offset: call.return_memory_offset.clone(),
1076 was_precompile_called: true,
1077 precompile_call_logs: vec![],
1078 charged_new_account_state_gas: false,
1079 });
1080 }
1081 }
1082
1083 self.apply_accesslist(ecx);
1085
1086 if let Some(broadcast) = &self.broadcast {
1088 let is_fixed_gas_limit = call.gas_limit >= 21_000 && !self.dynamic_gas_limit;
1091 self.dynamic_gas_limit = false;
1092
1093 if curr_depth == broadcast.depth && call.caller == broadcast.original_caller {
1098 ecx.tx_mut().set_caller(broadcast.new_origin);
1102
1103 call.caller = broadcast.new_origin;
1104 if !call.is_static {
1109 if let Err(err) = ecx.journal_mut().load_account(broadcast.new_origin) {
1110 return Some(CallOutcome {
1111 result: InterpreterResult {
1112 result: InstructionResult::Revert,
1113 output: Error::encode(err),
1114 gas,
1115 },
1116 memory_offset: call.return_memory_offset.clone(),
1117 was_precompile_called: false,
1118 precompile_call_logs: vec![],
1119 charged_new_account_state_gas: false,
1120 });
1121 }
1122
1123 let input = call.input.bytes(ecx);
1124 let chain_id = ecx.cfg().chain_id();
1125 let rpc = ecx.db().active_fork_url();
1126 let account =
1127 ecx.journal_mut().evm_state_mut().get_mut(&broadcast.new_origin).unwrap();
1128
1129 let mut tx_req = TransactionRequestFor::<FEN>::default()
1130 .with_from(broadcast.new_origin)
1131 .with_to(call.target_address)
1132 .with_value(call.transfer_value().unwrap_or_default())
1133 .with_input(input)
1134 .with_nonce(account.info.nonce)
1135 .with_chain_id(chain_id);
1136 if is_fixed_gas_limit {
1137 tx_req.set_gas_limit(call.gas_limit)
1138 }
1139
1140 let active_delegations = std::mem::take(&mut self.active_delegations);
1141 if let Some(blob_sidecar) = self.active_blob_sidecar.take() {
1143 if !active_delegations.is_empty() {
1145 let msg = "both delegation and blob are active; `attachBlob` and `attachDelegation` are not compatible";
1146 return Some(CallOutcome {
1147 result: InterpreterResult {
1148 result: InstructionResult::Revert,
1149 output: Error::encode(msg),
1150 gas,
1151 },
1152 memory_offset: call.return_memory_offset.clone(),
1153 was_precompile_called: false,
1154 precompile_call_logs: vec![],
1155 charged_new_account_state_gas: false,
1156 });
1157 }
1158 tx_req.set_blob_sidecar(blob_sidecar);
1159 }
1160
1161 if !active_delegations.is_empty() {
1163 for auth in &active_delegations {
1164 let Ok(authority) = auth.recover_authority() else {
1165 continue;
1166 };
1167 if authority == broadcast.new_origin {
1168 account.info.nonce += 1;
1171 }
1172 }
1173 tx_req.set_authorization_list(active_delegations);
1174 }
1175 if let Some(fee_token) = self.config.fee_token {
1176 tx_req.set_fee_token(fee_token);
1177 }
1178 self.broadcastable_transactions.push_back(BroadcastableTransaction {
1179 rpc,
1180 transaction: TransactionMaybeSigned::new(tx_req),
1181 });
1182 debug!(target: "cheatcodes", tx=?self.broadcastable_transactions.back().unwrap(), "broadcastable call");
1183
1184 if !self.config.evm_opts.isolate {
1186 let prev = account.info.nonce;
1187 account.info.nonce += 1;
1188 debug!(target: "cheatcodes", address=%broadcast.new_origin, nonce=prev+1, prev, "incremented nonce");
1189 }
1190 } else if broadcast.single_call {
1191 let msg = "`staticcall`s are not allowed after `broadcast`; use `startBroadcast` instead";
1192 return Some(CallOutcome {
1193 result: InterpreterResult {
1194 result: InstructionResult::Revert,
1195 output: Error::encode(msg),
1196 gas,
1197 },
1198 memory_offset: call.return_memory_offset.clone(),
1199 was_precompile_called: false,
1200 precompile_call_logs: vec![],
1201 charged_new_account_state_gas: false,
1202 });
1203 }
1204 }
1205 }
1206
1207 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1209 let (initialized, old_balance, old_nonce) =
1212 if let Ok(acc) = ecx.journal_mut().load_account(call.target_address) {
1213 (acc.data.info.exists(), acc.data.info.balance, acc.data.info.nonce)
1214 } else {
1215 (false, U256::ZERO, 0)
1216 };
1217
1218 let kind = match call.scheme {
1219 CallScheme::Call => crate::Vm::AccountAccessKind::Call,
1220 CallScheme::CallCode => crate::Vm::AccountAccessKind::CallCode,
1221 CallScheme::DelegateCall => crate::Vm::AccountAccessKind::DelegateCall,
1222 CallScheme::StaticCall => crate::Vm::AccountAccessKind::StaticCall,
1223 };
1224
1225 recorded_account_diffs_stack.push(vec![AccountAccess {
1231 chainInfo: crate::Vm::ChainInfo {
1232 forkId: ecx.db().active_fork_id().unwrap_or_default(),
1233 chainId: U256::from(ecx.cfg().chain_id()),
1234 },
1235 accessor: call.caller,
1236 account: call.bytecode_address,
1237 kind,
1238 initialized,
1239 oldBalance: old_balance,
1240 newBalance: U256::ZERO, oldNonce: old_nonce,
1242 newNonce: 0, value: call.call_value(),
1244 data: call.input.bytes(ecx),
1245 reverted: false,
1246 deployedCode: Bytes::new(),
1247 storageAccesses: vec![], depth: ecx.journal().depth().try_into().expect("journaled state depth exceeds u64"),
1249 }]);
1250 }
1251
1252 None
1253 }
1254
1255 pub fn rng(&mut self) -> &mut impl Rng {
1256 self.test_runner().rng()
1257 }
1258
1259 pub fn test_runner(&mut self) -> &mut TestRunner {
1260 self.test_runner.get_or_insert_with(|| match self.config.seed {
1261 Some(seed) => TestRunner::new_with_rng(
1262 proptest::test_runner::Config::default(),
1263 TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>()),
1264 ),
1265 None => TestRunner::new(proptest::test_runner::Config::default()),
1266 })
1267 }
1268
1269 pub fn set_seed(&mut self, seed: U256) {
1270 self.test_runner = Some(TestRunner::new_with_rng(
1271 proptest::test_runner::Config::default(),
1272 TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>()),
1273 ));
1274 }
1275
1276 pub fn arbitrary_storage(&mut self) -> &mut ArbitraryStorage {
1279 self.arbitrary_storage.get_or_insert_with(ArbitraryStorage::default)
1280 }
1281
1282 pub fn has_arbitrary_storage(&self, address: &Address) -> bool {
1284 match &self.arbitrary_storage {
1285 Some(storage) => storage.values.contains_key(address),
1286 None => false,
1287 }
1288 }
1289
1290 pub fn should_overwrite_arbitrary_storage(
1294 &self,
1295 address: &Address,
1296 storage_slot: U256,
1297 ) -> bool {
1298 match &self.arbitrary_storage {
1299 Some(storage) => {
1300 storage.overwrites.contains(address)
1301 && storage
1302 .values
1303 .get(address)
1304 .and_then(|arbitrary_values| arbitrary_values.get(&storage_slot))
1305 .is_none()
1306 }
1307 None => false,
1308 }
1309 }
1310
1311 pub fn is_arbitrary_storage_copy(&self, address: &Address) -> bool {
1313 match &self.arbitrary_storage {
1314 Some(storage) => storage.copies.contains_key(address),
1315 None => false,
1316 }
1317 }
1318
1319 pub fn struct_defs(&self) -> Option<&foundry_common::fmt::StructDefinitions> {
1321 self.analysis.as_ref().and_then(|analysis| analysis.struct_defs().ok())
1322 }
1323}
1324
1325impl<FEN: FoundryEvmNetwork> Inspector<FoundryContextFor<'_, FEN>> for Cheatcodes<FEN> {
1326 fn initialize_interp(
1327 &mut self,
1328 interpreter: &mut Interpreter,
1329 ecx: &mut FoundryContextFor<'_, FEN>,
1330 ) {
1331 if let Some(block) = self.block.take() {
1334 ecx.set_block(block);
1335 }
1336 if let Some(gas_price) = self.gas_price.take() {
1337 ecx.tx_mut().set_gas_price(gas_price);
1338 }
1339
1340 if self.gas_metering.paused {
1342 self.gas_metering.paused_frames.push(interpreter.gas);
1343 }
1344
1345 if let Some(expected) = &mut self.expected_revert {
1347 expected.max_depth = max(ecx.journal().depth(), expected.max_depth);
1348 }
1349 }
1350
1351 fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut FoundryContextFor<'_, FEN>) {
1352 self.pc = interpreter.bytecode.pc();
1353
1354 if self.broadcast.is_some() {
1355 self.set_gas_limit_type(interpreter);
1356 }
1357
1358 if self.gas_metering.paused {
1360 self.meter_gas(interpreter);
1361 }
1362
1363 if self.gas_metering.reset {
1365 self.meter_gas_reset(interpreter);
1366 }
1367
1368 if self.recording_accesses {
1370 self.record_accesses(interpreter);
1371 }
1372
1373 if self.recorded_account_diffs_stack.is_some() {
1375 self.record_state_diffs(interpreter, ecx);
1376 }
1377
1378 if !self.allowed_mem_writes.is_empty() {
1380 self.check_mem_opcodes(
1381 interpreter,
1382 ecx.journal().depth().try_into().expect("journaled state depth exceeds u64"),
1383 );
1384 }
1385
1386 if let Some(mapping_slots) = &mut self.mapping_slots {
1388 mapping_step(mapping_slots, interpreter);
1389 }
1390
1391 if self.gas_metering.recording {
1393 self.meter_gas_record(interpreter, ecx);
1394 }
1395
1396 if !self.env_overrides.is_empty() {
1401 let fork_id = ecx.db().active_fork_id();
1402 if let Some(env_overrides) =
1403 self.env_overrides.get_mut(&fork_id).filter(|o| o.is_any_set())
1404 {
1405 env_overrides.pending_opcode = None;
1409 env_overrides.pending_blobhash_index = None;
1410
1411 let opcode = interpreter.bytecode.opcode();
1412 match opcode {
1413 op::BASEFEE | op::GASPRICE => {
1414 env_overrides.pending_opcode = Some(opcode);
1415 }
1416 op::BLOBHASH => {
1417 env_overrides.pending_opcode = Some(opcode);
1418 env_overrides.pending_blobhash_index =
1419 interpreter.stack.peek(0).ok().and_then(|index| index.try_into().ok());
1420 }
1421 _ => {}
1422 }
1423 }
1424 }
1425 }
1426
1427 fn step_end(&mut self, interpreter: &mut Interpreter, ecx: &mut FoundryContextFor<'_, FEN>) {
1428 if self.gas_metering.paused {
1429 self.meter_gas_end(interpreter);
1430 }
1431
1432 if self.gas_metering.touched {
1433 self.meter_gas_check(interpreter);
1434 }
1435
1436 if self.arbitrary_storage.is_some() {
1438 self.arbitrary_storage_end(interpreter, ecx);
1439 }
1440
1441 if !self.env_overrides.is_empty() {
1451 let fork_id = ecx.db().active_fork_id();
1452 if self.env_overrides.get(&fork_id).is_some_and(|o| o.is_any_set()) {
1453 let opcode_failed = interpreter
1459 .bytecode
1460 .action
1461 .as_ref()
1462 .and_then(|a| a.instruction_result())
1463 .is_some();
1464 if opcode_failed {
1465 if let Some(env_overrides) = self.env_overrides.get_mut(&fork_id) {
1466 env_overrides.pending_opcode = None;
1467 env_overrides.pending_blobhash_index = None;
1468 }
1469 } else {
1470 self.apply_env_overrides(interpreter, fork_id);
1471 }
1472 }
1473 }
1474 }
1475
1476 fn log(&mut self, _ecx: &mut FoundryContextFor<'_, FEN>, log: Log) {
1477 if !self.expected_emits.is_empty()
1478 && let Some(err) = expect::handle_expect_emit(self, &log, None)
1479 {
1480 let _ = sh_err!("{err:?}");
1484 }
1485
1486 record_logs(&mut self.recorded_logs, &log);
1488 }
1489
1490 fn log_full(
1491 &mut self,
1492 interpreter: &mut Interpreter,
1493 _ecx: &mut FoundryContextFor<'_, FEN>,
1494 log: Log,
1495 ) {
1496 if !self.expected_emits.is_empty() {
1497 expect::handle_expect_emit(self, &log, Some(interpreter));
1498 }
1499
1500 record_logs(&mut self.recorded_logs, &log);
1502 }
1503
1504 fn call(
1505 &mut self,
1506 ecx: &mut FoundryContextFor<'_, FEN>,
1507 inputs: &mut CallInputs,
1508 ) -> Option<CallOutcome> {
1509 Self::call_with_executor(self, ecx, inputs, &mut TransparentCheatcodesExecutor)
1510 }
1511
1512 fn call_end(
1513 &mut self,
1514 ecx: &mut FoundryContextFor<'_, FEN>,
1515 call: &CallInputs,
1516 outcome: &mut CallOutcome,
1517 ) {
1518 let cheatcode_call = call.target_address == CHEATCODE_ADDRESS
1519 || call.target_address == HARDHAT_CONSOLE_ADDRESS;
1520
1521 if !cheatcode_call {
1525 let curr_depth = ecx.journal().depth();
1527 if let Some(prank) = &self.get_prank(curr_depth)
1528 && curr_depth == prank.depth
1529 {
1530 ecx.tx_mut().set_caller(prank.prank_origin);
1531
1532 if prank.single_call {
1534 self.pranks.remove(&curr_depth);
1535 }
1536 }
1537
1538 if let Some(broadcast) = &self.broadcast
1540 && curr_depth == broadcast.depth
1541 {
1542 ecx.tx_mut().set_caller(broadcast.original_origin);
1543
1544 if broadcast.single_call {
1546 let _ = self.broadcast.take();
1547 }
1548 }
1549 }
1550
1551 if let Some(assume_no_revert) = &mut self.assume_no_revert {
1553 if outcome.result.is_revert() && assume_no_revert.reverted_by.is_none() {
1556 assume_no_revert.reverted_by = Some(call.target_address);
1557 }
1558
1559 let curr_depth = ecx.journal().depth();
1561 if curr_depth <= assume_no_revert.depth && !cheatcode_call {
1562 if outcome.result.is_revert() {
1565 let assume_no_revert = std::mem::take(&mut self.assume_no_revert).unwrap();
1566 return match revert_handlers::handle_assume_no_revert(
1567 &assume_no_revert,
1568 outcome.result.result,
1569 &outcome.result.output,
1570 &self.config.available_artifacts,
1571 ) {
1572 Ok(_) => {
1575 outcome.result.output = Error::from(MAGIC_ASSUME).abi_encode().into();
1576 }
1577 Err(error) => {
1580 trace!(expected=?assume_no_revert, ?error, status=?outcome.result.result, "Expected revert mismatch");
1581 outcome.result.result = InstructionResult::Revert;
1582 outcome.result.output = error.abi_encode().into();
1583 }
1584 };
1585 }
1586 self.assume_no_revert = None;
1588 }
1589 }
1590
1591 if let Some(expected_revert) = &mut self.expected_revert {
1593 let call_failed = !matches!(outcome.result.result, return_ok!());
1596 if call_failed {
1597 if expected_revert.reverter.is_some()
1601 && (expected_revert.reverted_by.is_none() || expected_revert.count > 1)
1602 {
1603 expected_revert.reverted_by = Some(call.target_address);
1604 }
1605 }
1606
1607 let curr_depth = ecx.journal().depth();
1608 if curr_depth <= expected_revert.depth {
1609 let internal = self.config.internal_expect_revert;
1614 let went_deeper = expected_revert.max_depth > expected_revert.depth;
1615 let needs_processing = match expected_revert.kind {
1616 ExpectedRevertKind::Default => (|| {
1617 if cheatcode_call {
1619 return false;
1620 }
1621 if call_failed {
1623 return true;
1624 }
1625 if !internal && went_deeper {
1627 return true;
1628 }
1629 if curr_depth == 0 {
1631 return true;
1632 }
1633 !internal
1636 })(),
1637 ExpectedRevertKind::Cheatcode { pending_processing } => {
1640 cheatcode_call && !pending_processing
1641 }
1642 };
1643
1644 if needs_processing {
1645 let mut expected_revert = std::mem::take(&mut self.expected_revert).unwrap();
1646 return match revert_handlers::handle_expect_revert(
1647 cheatcode_call,
1648 false,
1649 self.config.internal_expect_revert,
1650 &expected_revert,
1651 outcome.result.result,
1652 outcome.result.output.clone(),
1653 &self.config.available_artifacts,
1654 ) {
1655 Err(error) => {
1656 trace!(expected=?expected_revert, ?error, status=?outcome.result.result, "Expected revert mismatch");
1657 outcome.result.result = InstructionResult::Revert;
1658 outcome.result.output = error.abi_encode().into();
1659 }
1660 Ok((_, retdata)) => {
1661 expected_revert.actual_count += 1;
1662 if expected_revert.actual_count < expected_revert.count {
1663 self.expected_revert = Some(expected_revert);
1664 }
1665 outcome.result.result = InstructionResult::Return;
1666 outcome.result.output = retdata;
1667 }
1668 };
1669 }
1670
1671 if let ExpectedRevertKind::Cheatcode { pending_processing } =
1674 &mut self.expected_revert.as_mut().unwrap().kind
1675 {
1676 *pending_processing = false;
1677 }
1678 }
1679 }
1680
1681 if cheatcode_call {
1684 return;
1685 }
1686
1687 let gas = outcome.result.gas;
1690 self.gas_metering.last_call_gas = Some(crate::Vm::Gas {
1691 gasLimit: gas.limit(),
1692 gasTotalUsed: gas.total_gas_spent(),
1693 gasMemoryUsed: 0,
1694 gasRefunded: gas.refunded(),
1695 gasRemaining: gas.remaining(),
1696 });
1697
1698 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1701 if ecx.journal().depth() > 0
1703 && let Some(mut last_recorded_depth) = recorded_account_diffs_stack.pop()
1704 {
1705 if outcome.result.is_revert() {
1708 for element in &mut *last_recorded_depth {
1709 element.reverted = true;
1710 for storage_access in &mut element.storageAccesses {
1711 storage_access.reverted = true;
1712 }
1713 }
1714 }
1715
1716 if let Some(call_access) = last_recorded_depth.first_mut() {
1717 let curr_depth = ecx.journal().depth();
1722 if call_access.depth == curr_depth as u64
1723 && let Ok(acc) = ecx.journal_mut().load_account(call.target_address)
1724 {
1725 debug_assert!(access_is_call(call_access.kind));
1726 call_access.newBalance = acc.data.info.balance;
1727 call_access.newNonce = acc.data.info.nonce;
1728 }
1729 if let Some(last) = recorded_account_diffs_stack.last_mut() {
1734 last.extend(last_recorded_depth);
1735 } else {
1736 recorded_account_diffs_stack.push(last_recorded_depth);
1737 }
1738 }
1739 }
1740 }
1741
1742 let diag = self.fork_revert_diagnostic.take();
1745
1746 if outcome.result.is_revert() {
1749 if let Some(err) = diag {
1752 outcome.result.output = Error::encode(err.to_error_msg(&self.labels));
1753 }
1754 return;
1755 }
1756
1757 let should_check_emits = self
1769 .expected_emits
1770 .iter()
1771 .any(|(expected, _)| {
1772 let curr_depth = ecx.journal().depth();
1773 expected.depth == curr_depth
1774 }) &&
1775 !call.is_static;
1777 if should_check_emits {
1778 let expected_counts = self
1779 .expected_emits
1780 .iter()
1781 .filter_map(|(expected, count_map)| {
1782 let count = match expected.address {
1783 Some(emitter) => match count_map.get(&emitter) {
1784 Some(log_count) => expected
1785 .log
1786 .as_ref()
1787 .map(|l| log_count.count(l))
1788 .unwrap_or_else(|| log_count.count_unchecked()),
1789 None => 0,
1790 },
1791 None => match &expected.log {
1792 Some(log) => count_map.values().map(|logs| logs.count(log)).sum(),
1793 None => count_map.values().map(|logs| logs.count_unchecked()).sum(),
1794 },
1795 };
1796
1797 (count != expected.count).then_some((expected, count))
1798 })
1799 .collect::<Vec<_>>();
1800
1801 if let Some((expected, _)) = self
1803 .expected_emits
1804 .iter()
1805 .find(|(expected, _)| !expected.found && expected.count > 0)
1806 {
1807 outcome.result.result = InstructionResult::Revert;
1808 let error_msg = expected.mismatch_error.as_deref().unwrap_or("log != expected log");
1809 outcome.result.output = error_msg.abi_encode().into();
1810 return;
1811 }
1812
1813 if !expected_counts.is_empty() {
1814 let msg = if outcome.result.is_ok() {
1815 let (expected, count) = expected_counts.first().unwrap();
1816 format!("log emitted {count} times, expected {}", expected.count)
1817 } else {
1818 "expected an emit, but the call reverted instead. \
1819 ensure you're testing the happy path when using `expectEmit`"
1820 .to_string()
1821 };
1822
1823 outcome.result.result = InstructionResult::Revert;
1824 outcome.result.output = Error::encode(msg);
1825 return;
1826 }
1827
1828 self.expected_emits.clear()
1832 }
1833
1834 if let TxKind::Call(test_contract) = ecx.tx().kind() {
1837 if ecx.db().is_forked_mode()
1840 && outcome.result.result == InstructionResult::Stop
1841 && call.target_address != test_contract
1842 {
1843 self.fork_revert_diagnostic =
1844 ecx.db().diagnose_revert(call.target_address, ecx.journal().evm_state());
1845 }
1846 }
1847
1848 if ecx.journal().depth() == 0 {
1850 if outcome.result.is_revert() {
1854 return;
1855 }
1856
1857 for (address, calldatas) in &self.expected_calls {
1862 for (calldata, (expected, actual_count)) in calldatas {
1864 let ExpectedCallData { gas, min_gas, value, count, call_type } = expected;
1866
1867 let failed = match call_type {
1868 ExpectedCallType::Count => *count != *actual_count,
1872 ExpectedCallType::NonCount => *count > *actual_count,
1877 };
1878 if failed {
1879 let expected_values = [
1880 Some(format!("data {}", hex::encode_prefixed(calldata))),
1881 value.as_ref().map(|v| format!("value {v}")),
1882 gas.map(|g| format!("gas {g}")),
1883 min_gas.map(|g| format!("minimum gas {g}")),
1884 ]
1885 .into_iter()
1886 .flatten()
1887 .join(", ");
1888 let but = if outcome.result.is_ok() {
1889 let s = if *actual_count == 1 { "" } else { "s" };
1890 format!("was called {actual_count} time{s}")
1891 } else {
1892 "the call reverted instead; \
1893 ensure you're testing the happy path when using `expectCall`"
1894 .to_string()
1895 };
1896 let s = if *count == 1 { "" } else { "s" };
1897 let msg = format!(
1898 "expected call to {address} with {expected_values} \
1899 to be called {count} time{s}, but {but}"
1900 );
1901 outcome.result.result = InstructionResult::Revert;
1902 outcome.result.output = Error::encode(msg);
1903
1904 return;
1905 }
1906 }
1907 }
1908
1909 for (expected, _) in &mut self.expected_emits {
1913 if expected.count == 0 && !expected.found {
1914 expected.found = true;
1915 }
1916 }
1917 self.expected_emits.retain(|(expected, _)| !expected.found);
1918 if !self.expected_emits.is_empty() {
1920 let msg = if outcome.result.is_ok() {
1921 "expected an emit, but no logs were emitted afterwards. \
1922 you might have mismatched events or not enough events were emitted"
1923 } else {
1924 "expected an emit, but the call reverted instead. \
1925 ensure you're testing the happy path when using `expectEmit`"
1926 };
1927 outcome.result.result = InstructionResult::Revert;
1928 outcome.result.output = Error::encode(msg);
1929 return;
1930 }
1931
1932 if let Some(expected_create) = self.expected_creates.first() {
1934 let msg = format!(
1935 "expected {} call by address {} for bytecode {} but not found",
1936 expected_create.create_scheme,
1937 hex::encode_prefixed(expected_create.deployer),
1938 hex::encode_prefixed(&expected_create.bytecode),
1939 );
1940 outcome.result.result = InstructionResult::Revert;
1941 outcome.result.output = Error::encode(msg);
1942 }
1943 }
1944 }
1945
1946 fn create(
1947 &mut self,
1948 ecx: &mut FoundryContextFor<'_, FEN>,
1949 mut input: &mut CreateInputs,
1950 ) -> Option<CreateOutcome> {
1951 if let Some(spec_id) = self.execution_evm_version {
1953 ecx.cfg_mut().set_spec_and_mainnet_gas_params(spec_id);
1954 }
1955
1956 let gas = Gas::new(input.gas_limit());
1957 if self.intercept_next_create_call {
1959 self.intercept_next_create_call = false;
1961
1962 let output = input.init_code();
1964
1965 return Some(CreateOutcome {
1967 result: InterpreterResult { result: InstructionResult::Revert, output, gas },
1968 address: None,
1969 });
1970 }
1971
1972 let curr_depth = ecx.journal().depth();
1973
1974 if let Some(prank) = &self.get_prank(curr_depth)
1976 && curr_depth >= prank.depth
1977 && input.caller() == prank.prank_caller
1978 {
1979 let prank_applied = if curr_depth == prank.depth {
1981 let _ = journaled_account(ecx, prank.new_caller);
1983 input.set_caller(prank.new_caller);
1984 true
1985 } else {
1986 false
1987 };
1988
1989 let prank_applied = if let Some(new_origin) = prank.new_origin {
1991 ecx.tx_mut().set_caller(new_origin);
1992 true
1993 } else {
1994 prank_applied
1995 };
1996
1997 if prank_applied && let Some(applied_prank) = prank.first_time_applied() {
1999 self.pranks.insert(curr_depth, applied_prank);
2000 }
2001 }
2002
2003 self.apply_accesslist(ecx);
2005
2006 if let Some(broadcast) = &mut self.broadcast
2008 && curr_depth >= broadcast.depth
2009 && input.caller() == broadcast.original_caller
2010 {
2011 if let Err(err) = ecx.journal_mut().load_account(broadcast.new_origin) {
2012 return Some(CreateOutcome {
2013 result: InterpreterResult {
2014 result: InstructionResult::Revert,
2015 output: Error::encode(err),
2016 gas,
2017 },
2018 address: None,
2019 });
2020 }
2021
2022 ecx.tx_mut().set_caller(broadcast.new_origin);
2023
2024 if curr_depth == broadcast.depth || broadcast.deploy_from_code {
2025 broadcast.deploy_from_code = false;
2027
2028 input.set_caller(broadcast.new_origin);
2029
2030 let rpc = ecx.db().active_fork_url();
2031 let account = &ecx.journal().evm_state()[&broadcast.new_origin];
2032 let mut tx_req = TransactionRequestFor::<FEN>::default()
2033 .with_from(broadcast.new_origin)
2034 .with_kind(TxKind::Create)
2035 .with_value(input.value())
2036 .with_input(input.init_code())
2037 .with_nonce(account.info.nonce);
2038 if let Some(fee_token) = self.config.fee_token {
2039 tx_req.set_fee_token(fee_token);
2040 }
2041 self.broadcastable_transactions.push_back(BroadcastableTransaction {
2042 rpc,
2043 transaction: TransactionMaybeSigned::new(tx_req),
2044 });
2045
2046 input.log_debug(self, &input.scheme().unwrap_or(CreateScheme::Create));
2047 }
2048 }
2049
2050 let address = input.allow_cheatcodes(self, ecx);
2052
2053 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
2055 recorded_account_diffs_stack.push(vec![AccountAccess {
2056 chainInfo: crate::Vm::ChainInfo {
2057 forkId: ecx.db().active_fork_id().unwrap_or_default(),
2058 chainId: U256::from(ecx.cfg().chain_id()),
2059 },
2060 accessor: input.caller(),
2061 account: address,
2062 kind: crate::Vm::AccountAccessKind::Create,
2063 initialized: true,
2064 oldBalance: U256::ZERO, newBalance: U256::ZERO, oldNonce: 0, newNonce: 1, value: input.value(),
2069 data: input.init_code(),
2070 reverted: false,
2071 deployedCode: Bytes::new(), storageAccesses: vec![], depth: curr_depth as u64,
2074 }]);
2075 }
2076
2077 None
2078 }
2079
2080 fn create_end(
2081 &mut self,
2082 ecx: &mut FoundryContextFor<'_, FEN>,
2083 call: &CreateInputs,
2084 outcome: &mut CreateOutcome,
2085 ) {
2086 let call = Some(call);
2087 let curr_depth = ecx.journal().depth();
2088
2089 if let Some(prank) = &self.get_prank(curr_depth)
2091 && curr_depth == prank.depth
2092 {
2093 ecx.tx_mut().set_caller(prank.prank_origin);
2094
2095 if prank.single_call {
2097 std::mem::take(&mut self.pranks);
2098 }
2099 }
2100
2101 if let Some(broadcast) = &self.broadcast
2103 && curr_depth == broadcast.depth
2104 {
2105 ecx.tx_mut().set_caller(broadcast.original_origin);
2106
2107 if broadcast.single_call {
2109 std::mem::take(&mut self.broadcast);
2110 }
2111 }
2112
2113 if let Some(expected_revert) = &mut self.expected_revert {
2115 if outcome.result.is_revert()
2127 && expected_revert.reverter.is_some()
2128 && expected_revert.reverted_by.is_none()
2129 && let Some(addr) = outcome.address
2130 {
2131 expected_revert.reverted_by = Some(addr);
2132 }
2133
2134 if curr_depth <= expected_revert.depth
2135 && matches!(expected_revert.kind, ExpectedRevertKind::Default)
2136 {
2137 let mut expected_revert = std::mem::take(&mut self.expected_revert).unwrap();
2138 return match revert_handlers::handle_expect_revert(
2139 false,
2140 true,
2141 self.config.internal_expect_revert,
2142 &expected_revert,
2143 outcome.result.result,
2144 outcome.result.output.clone(),
2145 &self.config.available_artifacts,
2146 ) {
2147 Ok((address, retdata)) => {
2148 expected_revert.actual_count += 1;
2149 if expected_revert.actual_count < expected_revert.count {
2150 expected_revert.reverted_by = None;
2152 self.expected_revert = Some(expected_revert.clone());
2153 }
2154
2155 outcome.result.result = InstructionResult::Return;
2156 outcome.result.output = retdata;
2157 outcome.address = address;
2158 }
2159 Err(err) => {
2160 outcome.result.result = InstructionResult::Revert;
2161 outcome.result.output = err.abi_encode().into();
2162 }
2163 };
2164 }
2165 }
2166
2167 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
2170 if curr_depth > 0
2172 && let Some(last_depth) = &mut recorded_account_diffs_stack.pop()
2173 {
2174 if outcome.result.is_revert() {
2177 for element in &mut *last_depth {
2178 element.reverted = true;
2179 for storage_access in &mut element.storageAccesses {
2180 storage_access.reverted = true;
2181 }
2182 }
2183 }
2184
2185 if let Some(create_access) = last_depth.first_mut() {
2186 let depth = ecx.journal().depth();
2191 if create_access.depth == depth as u64 {
2192 debug_assert_eq!(
2193 create_access.kind as u8,
2194 crate::Vm::AccountAccessKind::Create as u8
2195 );
2196 if let Some(address) = outcome.address
2197 && let Ok(created_acc) = ecx.journal_mut().load_account(address)
2198 {
2199 create_access.newBalance = created_acc.data.info.balance;
2200 create_access.newNonce = created_acc.data.info.nonce;
2201 create_access.deployedCode = created_acc
2202 .data
2203 .info
2204 .code
2205 .clone()
2206 .unwrap_or_default()
2207 .original_bytes();
2208 }
2209 }
2210 if let Some(last) = recorded_account_diffs_stack.last_mut() {
2215 last.append(last_depth);
2216 } else {
2217 recorded_account_diffs_stack.push(last_depth.clone());
2218 }
2219 }
2220 }
2221 }
2222
2223 if !self.expected_creates.is_empty()
2225 && let (Some(address), Some(call)) = (outcome.address, call)
2226 && let Ok(created_acc) = ecx.journal_mut().load_account(address)
2227 {
2228 let bytecode = created_acc.data.info.code.clone().unwrap_or_default().original_bytes();
2229 if let Some((index, _)) =
2230 self.expected_creates.iter().find_position(|expected_create| {
2231 expected_create.deployer == call.caller()
2232 && expected_create.create_scheme.eq(call.scheme().into())
2233 && expected_create.bytecode == bytecode
2234 })
2235 {
2236 self.expected_creates.swap_remove(index);
2237 }
2238 }
2239 }
2240}
2241
2242impl<FEN: FoundryEvmNetwork> InspectorExt for Cheatcodes<FEN> {
2243 fn should_use_create2_factory(&mut self, depth: usize, inputs: &CreateInputs) -> bool {
2244 let target_depth = if let Some(prank) = &self.get_prank(depth) {
2245 prank.depth
2246 } else if let Some(broadcast) = &self.broadcast {
2247 broadcast.depth
2248 } else {
2249 1
2250 };
2251
2252 if depth != target_depth {
2253 return false;
2254 }
2255
2256 match inputs.scheme() {
2257 CreateScheme::Create2 { .. } => {
2258 self.broadcast.is_some() || self.config.always_use_create_2_factory
2259 }
2260 CreateScheme::Create => self.config.batch_rewrite_creates && self.broadcast.is_some(),
2261 _ => false,
2262 }
2263 }
2264
2265 fn create2_deployer(&self) -> Address {
2266 self.config.evm_opts.create2_deployer
2267 }
2268}
2269
2270impl<FEN: FoundryEvmNetwork> Cheatcodes<FEN> {
2271 #[cold]
2272 fn meter_gas(&mut self, interpreter: &mut Interpreter) {
2273 if let Some(paused_gas) = self.gas_metering.paused_frames.last() {
2274 let memory = *interpreter.gas.memory();
2277 interpreter.gas = *paused_gas;
2278 interpreter.gas.memory_mut().words_num = memory.words_num;
2279 interpreter.gas.memory_mut().expansion_cost = memory.expansion_cost;
2280 } else {
2281 self.gas_metering.paused_frames.push(interpreter.gas);
2283 }
2284 }
2285
2286 #[cold]
2287 fn meter_gas_record(
2288 &mut self,
2289 interpreter: &mut Interpreter,
2290 ecx: &mut FoundryContextFor<'_, FEN>,
2291 ) {
2292 if interpreter.bytecode.action.as_ref().and_then(|i| i.instruction_result()).is_none() {
2293 self.gas_metering.gas_records.iter_mut().for_each(|record| {
2294 let curr_depth = ecx.journal().depth();
2295 if curr_depth == record.depth {
2296 if self.gas_metering.last_gas_used != 0 {
2299 let gas_diff = interpreter
2300 .gas
2301 .total_gas_spent()
2302 .saturating_sub(self.gas_metering.last_gas_used);
2303 record.gas_used = record.gas_used.saturating_add(gas_diff);
2304 }
2305
2306 self.gas_metering.last_gas_used = interpreter.gas.total_gas_spent();
2309 }
2310 });
2311 }
2312 }
2313
2314 #[cold]
2315 fn meter_gas_end(&mut self, interpreter: &mut Interpreter) {
2316 if let Some(interpreter_action) = interpreter.bytecode.action.as_ref()
2318 && will_exit(interpreter_action)
2319 {
2320 self.gas_metering.paused_frames.pop();
2321 }
2322 }
2323
2324 #[cold]
2325 const fn meter_gas_reset(&mut self, interpreter: &mut Interpreter) {
2326 let mut gas = Gas::new(interpreter.gas.limit());
2327 gas.memory_mut().words_num = interpreter.gas.memory().words_num;
2328 gas.memory_mut().expansion_cost = interpreter.gas.memory().expansion_cost;
2329 interpreter.gas = gas;
2330 self.gas_metering.reset = false;
2331 }
2332
2333 #[cold]
2334 fn meter_gas_check(&mut self, interpreter: &mut Interpreter) {
2335 if let Some(interpreter_action) = interpreter.bytecode.action.as_ref()
2336 && will_exit(interpreter_action)
2337 {
2338 if interpreter.gas.total_gas_spent()
2342 < u64::try_from(interpreter.gas.refunded()).unwrap_or_default()
2343 {
2344 interpreter.gas = Gas::new(interpreter.gas.limit());
2345 }
2346 }
2347 }
2348
2349 #[cold]
2363 fn apply_env_overrides(&mut self, interpreter: &mut Interpreter, fork_id: Option<U256>) {
2364 let Some(env_overrides) = self.env_overrides.get_mut(&fork_id) else { return };
2365 let Some(opcode) = env_overrides.pending_opcode.take() else { return };
2366 match opcode {
2367 op::BASEFEE => {
2368 if let Some(basefee) = env_overrides.basefee {
2369 Self::replace_top_of_stack(interpreter, U256::from(basefee));
2371 }
2372 }
2373 op::GASPRICE => {
2374 if let Some(gas_price) = env_overrides.gas_price {
2375 Self::replace_top_of_stack(interpreter, U256::from(gas_price));
2377 }
2378 }
2379 op::BLOBHASH => {
2380 let blob_hashes = env_overrides.blob_hashes.clone();
2381 let blobhash_index = env_overrides.pending_blobhash_index.take();
2382 if let Some(ref blob_hashes) = blob_hashes
2383 && let Some(index) = blobhash_index
2384 {
2385 let hash = blob_hashes.get(index as usize).copied().unwrap_or_default();
2388 Self::replace_top_of_stack(interpreter, hash.into());
2389 }
2390 }
2391 _ => {}
2392 }
2393 }
2394
2395 fn replace_top_of_stack(interpreter: &mut Interpreter, value: U256) {
2403 if interpreter.stack.pop().is_err() {
2404 debug_assert!(false, "env override expected opcode result on stack");
2405 return;
2406 }
2407 let _ = interpreter.stack.push(value);
2408 }
2409
2410 #[cold]
2418 fn arbitrary_storage_end(
2419 &mut self,
2420 interpreter: &mut Interpreter,
2421 ecx: &mut FoundryContextFor<'_, FEN>,
2422 ) {
2423 let (key, target_address) = if interpreter.bytecode.opcode() == op::SLOAD {
2424 (try_or_return!(interpreter.stack.peek(0)), interpreter.input.target_address)
2425 } else {
2426 return;
2427 };
2428
2429 let Some(value) = ecx.sload(target_address, key) else {
2430 return;
2431 };
2432
2433 if (value.is_cold && value.data.is_zero())
2434 || self.should_overwrite_arbitrary_storage(&target_address, key)
2435 {
2436 if self.has_arbitrary_storage(&target_address) {
2437 let arbitrary_value = self.rng().random();
2438 self.arbitrary_storage.as_mut().unwrap().save(
2439 ecx,
2440 target_address,
2441 key,
2442 arbitrary_value,
2443 );
2444 } else if self.is_arbitrary_storage_copy(&target_address) {
2445 let arbitrary_value = self.rng().random();
2446 self.arbitrary_storage.as_mut().unwrap().copy(
2447 ecx,
2448 target_address,
2449 key,
2450 arbitrary_value,
2451 );
2452 }
2453 }
2454 }
2455
2456 #[cold]
2458 fn record_accesses(&mut self, interpreter: &mut Interpreter) {
2459 let access = &mut self.accesses;
2460 match interpreter.bytecode.opcode() {
2461 op::SLOAD => {
2462 let key = try_or_return!(interpreter.stack.peek(0));
2463 access.record_read(interpreter.input.target_address, key);
2464 }
2465 op::SSTORE => {
2466 let key = try_or_return!(interpreter.stack.peek(0));
2467 access.record_write(interpreter.input.target_address, key);
2468 }
2469 _ => {}
2470 }
2471 }
2472
2473 #[cold]
2474 fn record_state_diffs(
2475 &mut self,
2476 interpreter: &mut Interpreter,
2477 ecx: &mut FoundryContextFor<'_, FEN>,
2478 ) {
2479 let Some(account_accesses) = &mut self.recorded_account_diffs_stack else { return };
2480 match interpreter.bytecode.opcode() {
2481 op::SELFDESTRUCT => {
2482 let Some(last) = account_accesses.last_mut() else { return };
2484
2485 let target = try_or_return!(interpreter.stack.peek(0));
2487 let target = Address::from_word(B256::from(target));
2488 let (initialized, old_balance, old_nonce) = ecx
2489 .journal_mut()
2490 .load_account(target)
2491 .map(|account| {
2492 (
2493 account.data.info.exists(),
2494 account.data.info.balance,
2495 account.data.info.nonce,
2496 )
2497 })
2498 .unwrap_or_default();
2499
2500 let value = ecx
2502 .balance(interpreter.input.target_address)
2503 .map(|b| b.data)
2504 .unwrap_or(U256::ZERO);
2505
2506 last.push(crate::Vm::AccountAccess {
2508 chainInfo: crate::Vm::ChainInfo {
2509 forkId: ecx.db().active_fork_id().unwrap_or_default(),
2510 chainId: U256::from(ecx.cfg().chain_id()),
2511 },
2512 accessor: interpreter.input.target_address,
2513 account: target,
2514 kind: crate::Vm::AccountAccessKind::SelfDestruct,
2515 initialized,
2516 oldBalance: old_balance,
2517 newBalance: old_balance + value,
2518 oldNonce: old_nonce,
2519 newNonce: old_nonce, value,
2521 data: Bytes::new(),
2522 reverted: false,
2523 deployedCode: Bytes::new(),
2524 storageAccesses: vec![],
2525 depth: ecx
2526 .journal()
2527 .depth()
2528 .try_into()
2529 .expect("journaled state depth exceeds u64"),
2530 });
2531 }
2532
2533 op::SLOAD => {
2534 let Some(last) = account_accesses.last_mut() else { return };
2535
2536 let key = try_or_return!(interpreter.stack.peek(0));
2537 let address = interpreter.input.target_address;
2538
2539 let present_value = if ecx.journal_mut().load_account(address).is_ok()
2543 && let Some(previous) = ecx.sload(address, key)
2544 {
2545 previous.data
2546 } else {
2547 U256::ZERO
2548 };
2549 let access = crate::Vm::StorageAccess {
2550 account: interpreter.input.target_address,
2551 slot: key.into(),
2552 isWrite: false,
2553 previousValue: present_value.into(),
2554 newValue: present_value.into(),
2555 reverted: false,
2556 };
2557 let curr_depth =
2558 ecx.journal().depth().try_into().expect("journaled state depth exceeds u64");
2559 append_storage_access(last, access, curr_depth);
2560 }
2561 op::SSTORE => {
2562 let Some(last) = account_accesses.last_mut() else { return };
2563
2564 let key = try_or_return!(interpreter.stack.peek(0));
2565 let value = try_or_return!(interpreter.stack.peek(1));
2566 let address = interpreter.input.target_address;
2567 let previous_value = if ecx.journal_mut().load_account(address).is_ok()
2570 && let Some(previous) = ecx.sload(address, key)
2571 {
2572 previous.data
2573 } else {
2574 U256::ZERO
2575 };
2576
2577 let access = crate::Vm::StorageAccess {
2578 account: address,
2579 slot: key.into(),
2580 isWrite: true,
2581 previousValue: previous_value.into(),
2582 newValue: value.into(),
2583 reverted: false,
2584 };
2585 let curr_depth =
2586 ecx.journal().depth().try_into().expect("journaled state depth exceeds u64");
2587 append_storage_access(last, access, curr_depth);
2588 }
2589
2590 op::EXTCODECOPY | op::EXTCODESIZE | op::EXTCODEHASH | op::BALANCE => {
2592 let kind = match interpreter.bytecode.opcode() {
2593 op::EXTCODECOPY => crate::Vm::AccountAccessKind::Extcodecopy,
2594 op::EXTCODESIZE => crate::Vm::AccountAccessKind::Extcodesize,
2595 op::EXTCODEHASH => crate::Vm::AccountAccessKind::Extcodehash,
2596 op::BALANCE => crate::Vm::AccountAccessKind::Balance,
2597 _ => unreachable!(),
2598 };
2599 let address =
2600 Address::from_word(B256::from(try_or_return!(interpreter.stack.peek(0))));
2601 let (initialized, balance, nonce) =
2602 if let Ok(acc) = ecx.journal_mut().load_account(address) {
2603 (acc.data.info.exists(), acc.data.info.balance, acc.data.info.nonce)
2604 } else {
2605 (false, U256::ZERO, 0)
2606 };
2607 let curr_depth =
2608 ecx.journal().depth().try_into().expect("journaled state depth exceeds u64");
2609 let account_access = crate::Vm::AccountAccess {
2610 chainInfo: crate::Vm::ChainInfo {
2611 forkId: ecx.db().active_fork_id().unwrap_or_default(),
2612 chainId: U256::from(ecx.cfg().chain_id()),
2613 },
2614 accessor: interpreter.input.target_address,
2615 account: address,
2616 kind,
2617 initialized,
2618 oldBalance: balance,
2619 newBalance: balance,
2620 oldNonce: nonce,
2621 newNonce: nonce, value: U256::ZERO,
2623 data: Bytes::new(),
2624 reverted: false,
2625 deployedCode: Bytes::new(),
2626 storageAccesses: vec![],
2627 depth: curr_depth,
2628 };
2629 if let Some(last) = account_accesses.last_mut() {
2632 last.push(account_access);
2633 } else {
2634 account_accesses.push(vec![account_access]);
2635 }
2636 }
2637 _ => {}
2638 }
2639 }
2640
2641 #[cold]
2646 fn check_mem_opcodes(&self, interpreter: &mut Interpreter, depth: u64) {
2647 let Some(ranges) = self.allowed_mem_writes.get(&depth) else {
2648 return;
2649 };
2650
2651 macro_rules! mem_opcode_match {
2660 ($(($opcode:ident, $offset_depth:expr, $size_depth:expr, $writes:expr)),* $(,)?) => {
2661 match interpreter.bytecode.opcode() {
2662 op::MSTORE => {
2667 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2669
2670 if !ranges.iter().any(|range| {
2673 range.contains(&offset) && range.contains(&(offset + 31))
2674 }) {
2675 let value = try_or_return!(interpreter.stack.peek(1)).to_be_bytes::<32>();
2680 if value[..SELECTOR_LEN] == stopExpectSafeMemoryCall::SELECTOR {
2681 return
2682 }
2683
2684 disallowed_mem_write(offset, 32, interpreter, ranges);
2685 return
2686 }
2687 }
2688 op::MSTORE8 => {
2689 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2691
2692 if !ranges.iter().any(|range| range.contains(&offset)) {
2695 disallowed_mem_write(offset, 1, interpreter, ranges);
2696 return
2697 }
2698 }
2699
2700 op::MLOAD => {
2705 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2707
2708 if offset >= interpreter.memory.size() as u64 && !ranges.iter().any(|range| {
2712 range.contains(&offset) && range.contains(&(offset + 31))
2713 }) {
2714 disallowed_mem_write(offset, 32, interpreter, ranges);
2715 return
2716 }
2717 }
2718
2719 op::CALL => {
2724 let dest_offset = try_or_return!(interpreter.stack.peek(5)).saturating_to::<u64>();
2726
2727 let size = try_or_return!(interpreter.stack.peek(6)).saturating_to::<u64>();
2729
2730 let fail_cond = !ranges.iter().any(|range| {
2734 range.contains(&dest_offset) &&
2735 range.contains(&(dest_offset + size.saturating_sub(1)))
2736 });
2737
2738 if fail_cond {
2741 let to = Address::from_word(try_or_return!(interpreter.stack.peek(1)).to_be_bytes::<32>().into());
2745 if to == CHEATCODE_ADDRESS {
2746 let args_offset = try_or_return!(interpreter.stack.peek(3)).saturating_to::<usize>();
2747 let args_size = try_or_return!(interpreter.stack.peek(4)).saturating_to::<usize>();
2748 let memory_word = interpreter.memory.slice_len(args_offset, args_size);
2749 if memory_word[..SELECTOR_LEN] == stopExpectSafeMemoryCall::SELECTOR {
2750 return
2751 }
2752 }
2753
2754 disallowed_mem_write(dest_offset, size, interpreter, ranges);
2755 return
2756 }
2757 }
2758
2759 $(op::$opcode => {
2760 let dest_offset = try_or_return!(interpreter.stack.peek($offset_depth)).saturating_to::<u64>();
2762
2763 let size = try_or_return!(interpreter.stack.peek($size_depth)).saturating_to::<u64>();
2765
2766 let fail_cond = !ranges.iter().any(|range| {
2770 range.contains(&dest_offset) &&
2771 range.contains(&(dest_offset + size.saturating_sub(1)))
2772 }) && ($writes ||
2773 [dest_offset, (dest_offset + size).saturating_sub(1)].into_iter().any(|offset| {
2774 offset >= interpreter.memory.size() as u64
2775 })
2776 );
2777
2778 if fail_cond {
2781 disallowed_mem_write(dest_offset, size, interpreter, ranges);
2782 return
2783 }
2784 })*
2785
2786 _ => {}
2787 }
2788 }
2789 }
2790
2791 mem_opcode_match!(
2794 (CALLDATACOPY, 0, 2, true),
2795 (CODECOPY, 0, 2, true),
2796 (RETURNDATACOPY, 0, 2, true),
2797 (EXTCODECOPY, 1, 3, true),
2798 (CALLCODE, 5, 6, true),
2799 (STATICCALL, 4, 5, true),
2800 (DELEGATECALL, 4, 5, true),
2801 (KECCAK256, 0, 1, false),
2802 (LOG0, 0, 1, false),
2803 (LOG1, 0, 1, false),
2804 (LOG2, 0, 1, false),
2805 (LOG3, 0, 1, false),
2806 (LOG4, 0, 1, false),
2807 (CREATE, 1, 2, false),
2808 (CREATE2, 1, 2, false),
2809 (RETURN, 0, 1, false),
2810 (REVERT, 0, 1, false),
2811 );
2812 }
2813
2814 #[cold]
2815 fn set_gas_limit_type(&mut self, interpreter: &mut Interpreter) {
2816 match interpreter.bytecode.opcode() {
2817 op::CREATE2 => self.dynamic_gas_limit = true,
2818 op::CALL => {
2819 self.dynamic_gas_limit =
2822 try_or_return!(interpreter.stack.peek(0)) >= interpreter.gas.remaining() - 100
2823 }
2824 _ => self.dynamic_gas_limit = false,
2825 }
2826 }
2827}
2828
2829fn disallowed_mem_write(
2835 dest_offset: u64,
2836 size: u64,
2837 interpreter: &mut Interpreter,
2838 ranges: &[Range<u64>],
2839) {
2840 let revert_string = format!(
2841 "memory write at offset 0x{:02X} of size 0x{:02X} not allowed; safe range: {}",
2842 dest_offset,
2843 size,
2844 ranges.iter().map(|r| format!("[0x{:02X}, 0x{:02X})", r.start, r.end)).join(" U ")
2845 );
2846
2847 interpreter.bytecode.set_action(InterpreterAction::new_return(
2848 InstructionResult::Revert,
2849 Bytes::from(revert_string.into_bytes()),
2850 interpreter.gas,
2851 ));
2852}
2853
2854const fn access_is_call(kind: crate::Vm::AccountAccessKind) -> bool {
2856 matches!(
2857 kind,
2858 crate::Vm::AccountAccessKind::Call
2859 | crate::Vm::AccountAccessKind::StaticCall
2860 | crate::Vm::AccountAccessKind::CallCode
2861 | crate::Vm::AccountAccessKind::DelegateCall
2862 )
2863}
2864
2865fn record_logs(recorded_logs: &mut Option<Vec<Vm::Log>>, log: &Log) {
2867 if let Some(storage_recorded_logs) = recorded_logs {
2868 storage_recorded_logs.push(Vm::Log {
2869 topics: log.data.topics().to_vec(),
2870 data: log.data.data.clone(),
2871 emitter: log.address,
2872 });
2873 }
2874}
2875
2876fn append_storage_access(
2878 last: &mut Vec<AccountAccess>,
2879 storage_access: crate::Vm::StorageAccess,
2880 storage_depth: u64,
2881) {
2882 if !last.is_empty() && last.first().unwrap().depth < storage_depth {
2884 if last.len() == 1 {
2890 last.first_mut().unwrap().storageAccesses.push(storage_access);
2891 } else {
2892 let last_record = last.last_mut().unwrap();
2893 if last_record.kind as u8 == crate::Vm::AccountAccessKind::Resume as u8 {
2894 last_record.storageAccesses.push(storage_access);
2895 } else {
2896 let entry = last.first().unwrap();
2897 let resume_record = crate::Vm::AccountAccess {
2898 chainInfo: crate::Vm::ChainInfo {
2899 forkId: entry.chainInfo.forkId,
2900 chainId: entry.chainInfo.chainId,
2901 },
2902 accessor: entry.accessor,
2903 account: entry.account,
2904 kind: crate::Vm::AccountAccessKind::Resume,
2905 initialized: entry.initialized,
2906 storageAccesses: vec![storage_access],
2907 reverted: entry.reverted,
2908 oldBalance: U256::ZERO,
2910 newBalance: U256::ZERO,
2911 oldNonce: 0,
2912 newNonce: 0,
2913 value: U256::ZERO,
2914 data: Bytes::new(),
2915 deployedCode: Bytes::new(),
2916 depth: entry.depth,
2917 };
2918 last.push(resume_record);
2919 }
2920 }
2921 }
2922}
2923
2924const fn cheatcode_of<T: spec::CheatcodeDef>(_: &T) -> &'static spec::Cheatcode<'static> {
2926 T::CHEATCODE
2927}
2928
2929fn cheatcode_name(cheat: &spec::Cheatcode<'static>) -> &'static str {
2930 cheat.func.signature.split('(').next().unwrap()
2931}
2932
2933const fn cheatcode_id(cheat: &spec::Cheatcode<'static>) -> &'static str {
2934 cheat.func.id
2935}
2936
2937const fn cheatcode_signature(cheat: &spec::Cheatcode<'static>) -> &'static str {
2938 cheat.func.signature
2939}
2940
2941fn apply_dispatch<FEN: FoundryEvmNetwork>(
2943 calls: &Vm::VmCalls,
2944 ccx: &mut CheatsCtxt<'_, '_, FEN>,
2945 executor: &mut dyn CheatcodesExecutor<FEN>,
2946) -> Result {
2947 macro_rules! get_cheatcode {
2949 ($($variant:ident),*) => {
2950 match calls {
2951 $(Vm::VmCalls::$variant(cheat) => cheatcode_of(cheat),)*
2952 }
2953 };
2954 }
2955 let cheat = vm_calls!(get_cheatcode);
2956
2957 let _guard = debug_span!(target: "cheatcodes", "apply", id = %cheatcode_id(cheat)).entered();
2958 trace!(target: "cheatcodes", cheat = %cheatcode_signature(cheat), "applying");
2959
2960 if let spec::Status::Deprecated(replacement) = cheat.status {
2961 ccx.state.deprecated.insert(cheatcode_signature(cheat), replacement);
2962 }
2963
2964 macro_rules! dispatch {
2966 ($($variant:ident),*) => {
2967 match calls {
2968 $(Vm::VmCalls::$variant(cheat) => Cheatcode::apply_full(cheat, ccx, executor),)*
2969 }
2970 };
2971 }
2972 let mut result = vm_calls!(dispatch);
2973
2974 if let Err(e) = &mut result
2976 && e.is_str()
2977 {
2978 let name = cheatcode_name(cheat);
2979 if !name.contains("assert") && name != "rpcUrl" {
2983 *e = fmt_err!("vm.{name}: {e}");
2984 }
2985 }
2986
2987 trace!(
2988 target: "cheatcodes",
2989 return = %match &result {
2990 Ok(b) => hex::encode(b),
2991 Err(e) => e.to_string(),
2992 }
2993 );
2994
2995 result
2996}
2997
2998const fn will_exit(action: &InterpreterAction) -> bool {
3000 match action {
3001 InterpreterAction::Return(result) => {
3002 result.result.is_ok_or_revert() || result.result.is_halt()
3003 }
3004 _ => false,
3005 }
3006}
3007
3008#[cfg(test)]
3009mod tests {
3010 use super::*;
3011
3012 fn cheats(flag: bool, broadcast: Option<Broadcast>) -> Cheatcodes {
3013 let config = CheatsConfig { batch_rewrite_creates: flag, ..Default::default() };
3014 let mut cheats = Cheatcodes::new(Arc::new(config));
3015 cheats.broadcast = broadcast;
3016 cheats
3017 }
3018
3019 fn create_inputs() -> CreateInputs {
3020 CreateInputs::new(Address::ZERO, CreateScheme::Create, U256::ZERO, Bytes::new(), 100_000, 0)
3021 }
3022
3023 fn broadcast_at(depth: usize) -> Broadcast {
3024 Broadcast { depth, ..Default::default() }
3025 }
3026
3027 #[test]
3028 fn flag_off_with_broadcast_returns_false() {
3029 let mut cheats = cheats(false, Some(broadcast_at(1)));
3030 assert!(!cheats.should_use_create2_factory(1, &create_inputs()));
3031 }
3032
3033 #[test]
3034 fn flag_on_without_broadcast_returns_false() {
3035 let mut cheats = cheats(true, None);
3036 assert!(!cheats.should_use_create2_factory(1, &create_inputs()));
3037 }
3038
3039 #[test]
3040 fn flag_on_with_broadcast_depth_mismatch_returns_false() {
3041 let mut cheats = cheats(true, Some(broadcast_at(2)));
3042 assert!(!cheats.should_use_create2_factory(1, &create_inputs()));
3043 }
3044
3045 #[test]
3046 fn flag_on_with_broadcast_depth_match_returns_true() {
3047 let mut cheats = cheats(true, Some(broadcast_at(1)));
3048 assert!(cheats.should_use_create2_factory(1, &create_inputs()));
3049 }
3050}