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
778 .get_or_init(|| {
779 if let Some(artifacts) = &self.config.available_artifacts {
780 return SignaturesIdentifier::new_offline_with_abis(
781 artifacts.values().map(|contract| &contract.abi),
782 )
783 .ok();
784 }
785 SignaturesIdentifier::new(true).ok()
786 })
787 .as_ref()
788 }
789
790 fn apply_cheatcode(
792 &mut self,
793 ecx: &mut FoundryContextFor<'_, FEN>,
794 call: &CallInputs,
795 executor: &mut dyn CheatcodesExecutor<FEN>,
796 ) -> Result {
797 let decoded = Vm::VmCalls::abi_decode(&call.input.bytes(ecx)).map_err(|e| {
799 if let alloy_sol_types::Error::UnknownSelector { name: _, selector } = e {
800 let msg = format!(
801 "unknown cheatcode with selector {selector}; \
802 you may have a mismatch between the `Vm` interface (likely in `forge-std`) \
803 and the `forge` version"
804 );
805 return alloy_sol_types::Error::Other(std::borrow::Cow::Owned(msg));
806 }
807 e
808 })?;
809
810 let caller = call.caller;
811
812 ecx.db_mut().ensure_cheatcode_access_forking_mode(&caller)?;
815
816 apply_dispatch(
817 &decoded,
818 &mut CheatsCtxt { state: self, ecx, gas_limit: call.gas_limit, caller },
819 executor,
820 )
821 }
822
823 fn allow_cheatcodes_on_create(
829 &self,
830 ecx: &mut FoundryContextFor<FEN>,
831 caller: Address,
832 created_address: Address,
833 ) {
834 if ecx.journal().depth() <= 1 || ecx.db().has_cheatcode_access(&caller) {
835 ecx.db_mut().allow_cheatcode_access(created_address);
836 }
837 }
838
839 fn apply_accesslist(&mut self, ecx: &mut FoundryContextFor<FEN>) {
845 if let Some(access_list) = &self.access_list {
846 ecx.tx_mut().set_access_list(access_list.clone());
847
848 if ecx.tx().tx_type() == TransactionType::Legacy as u8 {
849 ecx.tx_mut().set_tx_type(TransactionType::Eip2930 as u8);
850 }
851 }
852 }
853
854 pub fn on_revert(&mut self, ecx: &mut FoundryContextFor<FEN>) {
859 trace!(deals=?self.eth_deals.len(), "rolling back deals");
860
861 if self.expected_revert.is_some() {
863 return;
864 }
865
866 if ecx.journal().depth() > 0 {
868 return;
869 }
870
871 while let Some(record) = self.eth_deals.pop() {
875 if let Some(acc) = ecx.journal_mut().evm_state_mut().get_mut(&record.address) {
876 acc.info.balance = record.old_balance;
877 }
878 }
879 }
880
881 pub fn call_with_executor(
882 &mut self,
883 ecx: &mut FoundryContextFor<'_, FEN>,
884 call: &mut CallInputs,
885 executor: &mut dyn CheatcodesExecutor<FEN>,
886 ) -> Option<CallOutcome> {
887 if let Some(spec_id) = self.execution_evm_version {
889 ecx.cfg_mut().set_spec_and_mainnet_gas_params(spec_id);
890 }
891
892 let gas = Gas::new(call.gas_limit);
893 let curr_depth = ecx.journal().depth();
894
895 if curr_depth == 0 {
899 let sender = ecx.tx().caller();
900 let account = match super::evm::journaled_account(ecx, sender) {
901 Ok(account) => account,
902 Err(err) => {
903 return Some(CallOutcome {
904 result: InterpreterResult {
905 result: InstructionResult::Revert,
906 output: err.abi_encode().into(),
907 gas,
908 },
909 memory_offset: call.return_memory_offset.clone(),
910 was_precompile_called: false,
911 precompile_call_logs: vec![],
912 charged_new_account_state_gas: false,
913 });
914 }
915 };
916 let prev = account.info.nonce;
917 account.info.nonce = prev.saturating_sub(1);
918
919 trace!(target: "cheatcodes", %sender, nonce=account.info.nonce, prev, "corrected nonce");
920 }
921
922 if call.target_address == CHEATCODE_ADDRESS {
923 return match self.apply_cheatcode(ecx, call, executor) {
924 Ok(retdata) => Some(CallOutcome {
925 result: InterpreterResult {
926 result: InstructionResult::Return,
927 output: retdata.into(),
928 gas,
929 },
930 memory_offset: call.return_memory_offset.clone(),
931 was_precompile_called: true,
932 precompile_call_logs: vec![],
933 charged_new_account_state_gas: false,
934 }),
935 Err(err) => Some(CallOutcome {
936 result: InterpreterResult {
937 result: InstructionResult::Revert,
938 output: err.abi_encode().into(),
939 gas,
940 },
941 memory_offset: call.return_memory_offset.clone(),
942 was_precompile_called: false,
943 precompile_call_logs: vec![],
944 charged_new_account_state_gas: false,
945 }),
946 };
947 }
948
949 if call.target_address == HARDHAT_CONSOLE_ADDRESS {
950 return None;
951 }
952
953 if let Some(expected) = &mut self.expected_revert {
957 expected.max_depth = max(curr_depth + 1, expected.max_depth);
958 }
959
960 if let Some(expected_calls_for_target) = self.expected_calls.get_mut(&call.bytecode_address)
964 {
965 for (calldata, (expected, actual_count)) in expected_calls_for_target {
967 if calldata.len() <= call.input.len() &&
970 *calldata == call.input.bytes(ecx)[..calldata.len()] &&
972 expected
974 .value.is_none_or(|value| Some(value) == call.transfer_value()) &&
975 expected.gas.is_none_or(|gas| gas == call.gas_limit) &&
977 expected.min_gas.is_none_or(|min_gas| min_gas <= call.gas_limit)
979 {
980 *actual_count += 1;
981 }
982 }
983 }
984
985 if let Some(prank) = &self.get_prank(curr_depth) {
987 if prank.delegate_call
989 && curr_depth == prank.depth
990 && call.scheme == CallScheme::DelegateCall
991 {
992 call.target_address = prank.new_caller;
993 call.caller = prank.new_caller;
994 if let Some(new_origin) = prank.new_origin {
995 ecx.tx_mut().set_caller(new_origin);
996 }
997 }
998
999 if curr_depth >= prank.depth && call.caller == prank.prank_caller {
1000 let prank_applied = if curr_depth == prank.depth {
1002 let _ = journaled_account(ecx, prank.new_caller);
1004 call.caller = prank.new_caller;
1005 true
1006 } else {
1007 false
1008 };
1009
1010 let prank_applied = if let Some(new_origin) = prank.new_origin {
1012 ecx.tx_mut().set_caller(new_origin);
1013 true
1014 } else {
1015 prank_applied
1016 };
1017
1018 if prank_applied && let Some(applied_prank) = prank.first_time_applied() {
1020 self.pranks.insert(curr_depth, applied_prank);
1021 }
1022 }
1023 }
1024
1025 if let Some(mocks) = self.mocked_calls.get_mut(&call.bytecode_address) {
1027 let ctx = MockCallDataContext {
1028 calldata: call.input.bytes(ecx),
1029 value: call.transfer_value(),
1030 };
1031
1032 if let Some(return_data_queue) = match mocks.get_mut(&ctx) {
1033 Some(queue) => Some(queue),
1034 None => mocks
1035 .iter_mut()
1036 .find(|(mock, _)| {
1037 call.input.bytes(ecx).get(..mock.calldata.len()) == Some(&mock.calldata[..])
1038 && mock.value.is_none_or(|value| Some(value) == call.transfer_value())
1039 })
1040 .map(|(_, v)| v),
1041 } && let Some(return_data) = return_data_queue.front().map(|x| x.to_owned())
1042 {
1043 if let Some(value) = call.transfer_value() {
1044 let checkpoint = ecx.journal_mut().checkpoint();
1045 match ecx.journal_mut().transfer_loaded(
1046 call.transfer_from(),
1047 call.transfer_to(),
1048 value,
1049 ) {
1050 None => {
1051 if return_data.ret_type.is_ok() {
1052 ecx.journal_mut().checkpoint_commit();
1053 } else {
1054 ecx.journal_mut().checkpoint_revert(checkpoint);
1055 }
1056 }
1057 Some(err) => {
1058 ecx.journal_mut().checkpoint_revert(checkpoint);
1059 return Some(CallOutcome {
1060 result: InterpreterResult {
1061 result: err.into(),
1062 output: Bytes::new(),
1063 gas,
1064 },
1065 memory_offset: call.return_memory_offset.clone(),
1066 was_precompile_called: false,
1067 precompile_call_logs: vec![],
1068 charged_new_account_state_gas: false,
1069 });
1070 }
1071 }
1072 }
1073
1074 if return_data_queue.len() > 1 {
1076 return_data_queue.pop_front();
1077 }
1078
1079 return Some(CallOutcome {
1080 result: InterpreterResult {
1081 result: return_data.ret_type,
1082 output: return_data.data,
1083 gas,
1084 },
1085 memory_offset: call.return_memory_offset.clone(),
1086 was_precompile_called: true,
1087 precompile_call_logs: vec![],
1088 charged_new_account_state_gas: false,
1089 });
1090 }
1091 }
1092
1093 self.apply_accesslist(ecx);
1095
1096 if let Some(broadcast) = &self.broadcast {
1098 let is_fixed_gas_limit = call.gas_limit >= 21_000 && !self.dynamic_gas_limit;
1101 self.dynamic_gas_limit = false;
1102
1103 if curr_depth == broadcast.depth && call.caller == broadcast.original_caller {
1108 ecx.tx_mut().set_caller(broadcast.new_origin);
1112
1113 call.caller = broadcast.new_origin;
1114 if !call.is_static {
1119 if let Err(err) = ecx.journal_mut().load_account(broadcast.new_origin) {
1120 return Some(CallOutcome {
1121 result: InterpreterResult {
1122 result: InstructionResult::Revert,
1123 output: Error::encode(err),
1124 gas,
1125 },
1126 memory_offset: call.return_memory_offset.clone(),
1127 was_precompile_called: false,
1128 precompile_call_logs: vec![],
1129 charged_new_account_state_gas: false,
1130 });
1131 }
1132
1133 let input = call.input.bytes(ecx);
1134 let chain_id = ecx.cfg().chain_id();
1135 let rpc = ecx.db().active_fork_url();
1136 let account =
1137 ecx.journal_mut().evm_state_mut().get_mut(&broadcast.new_origin).unwrap();
1138
1139 let mut tx_req = TransactionRequestFor::<FEN>::default()
1140 .with_from(broadcast.new_origin)
1141 .with_to(call.target_address)
1142 .with_value(call.transfer_value().unwrap_or_default())
1143 .with_input(input)
1144 .with_nonce(account.info.nonce)
1145 .with_chain_id(chain_id);
1146 if is_fixed_gas_limit {
1147 tx_req.set_gas_limit(call.gas_limit)
1148 }
1149
1150 let active_delegations = std::mem::take(&mut self.active_delegations);
1151 if let Some(blob_sidecar) = self.active_blob_sidecar.take() {
1153 if !active_delegations.is_empty() {
1155 let msg = "both delegation and blob are active; `attachBlob` and `attachDelegation` are not compatible";
1156 return Some(CallOutcome {
1157 result: InterpreterResult {
1158 result: InstructionResult::Revert,
1159 output: Error::encode(msg),
1160 gas,
1161 },
1162 memory_offset: call.return_memory_offset.clone(),
1163 was_precompile_called: false,
1164 precompile_call_logs: vec![],
1165 charged_new_account_state_gas: false,
1166 });
1167 }
1168 tx_req.set_blob_sidecar(blob_sidecar);
1169 }
1170
1171 if !active_delegations.is_empty() {
1173 for auth in &active_delegations {
1174 let Ok(authority) = auth.recover_authority() else {
1175 continue;
1176 };
1177 if authority == broadcast.new_origin {
1178 account.info.nonce += 1;
1181 }
1182 }
1183 tx_req.set_authorization_list(active_delegations);
1184 }
1185 if let Some(fee_token) = self.config.fee_token {
1186 tx_req.set_fee_token(fee_token);
1187 }
1188 self.broadcastable_transactions.push_back(BroadcastableTransaction {
1189 rpc,
1190 transaction: TransactionMaybeSigned::new(tx_req),
1191 });
1192 debug!(target: "cheatcodes", tx=?self.broadcastable_transactions.back().unwrap(), "broadcastable call");
1193
1194 if !self.config.evm_opts.isolate {
1196 let prev = account.info.nonce;
1197 account.info.nonce += 1;
1198 debug!(target: "cheatcodes", address=%broadcast.new_origin, nonce=prev+1, prev, "incremented nonce");
1199 }
1200 } else if broadcast.single_call {
1201 let msg = "`staticcall`s are not allowed after `broadcast`; use `startBroadcast` instead";
1202 return Some(CallOutcome {
1203 result: InterpreterResult {
1204 result: InstructionResult::Revert,
1205 output: Error::encode(msg),
1206 gas,
1207 },
1208 memory_offset: call.return_memory_offset.clone(),
1209 was_precompile_called: false,
1210 precompile_call_logs: vec![],
1211 charged_new_account_state_gas: false,
1212 });
1213 }
1214 }
1215 }
1216
1217 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1219 let (initialized, old_balance, old_nonce) =
1222 if let Ok(acc) = ecx.journal_mut().load_account(call.target_address) {
1223 (acc.data.info.exists(), acc.data.info.balance, acc.data.info.nonce)
1224 } else {
1225 (false, U256::ZERO, 0)
1226 };
1227
1228 let kind = match call.scheme {
1229 CallScheme::Call => crate::Vm::AccountAccessKind::Call,
1230 CallScheme::CallCode => crate::Vm::AccountAccessKind::CallCode,
1231 CallScheme::DelegateCall => crate::Vm::AccountAccessKind::DelegateCall,
1232 CallScheme::StaticCall => crate::Vm::AccountAccessKind::StaticCall,
1233 };
1234
1235 recorded_account_diffs_stack.push(vec![AccountAccess {
1241 chainInfo: crate::Vm::ChainInfo {
1242 forkId: ecx.db().active_fork_id().unwrap_or_default(),
1243 chainId: U256::from(ecx.cfg().chain_id()),
1244 },
1245 accessor: call.caller,
1246 account: call.bytecode_address,
1247 kind,
1248 initialized,
1249 oldBalance: old_balance,
1250 newBalance: U256::ZERO, oldNonce: old_nonce,
1252 newNonce: 0, value: call.call_value(),
1254 data: call.input.bytes(ecx),
1255 reverted: false,
1256 deployedCode: Bytes::new(),
1257 storageAccesses: vec![], depth: ecx.journal().depth().try_into().expect("journaled state depth exceeds u64"),
1259 }]);
1260 }
1261
1262 None
1263 }
1264
1265 pub fn rng(&mut self) -> &mut impl Rng {
1266 self.test_runner().rng()
1267 }
1268
1269 pub fn test_runner(&mut self) -> &mut TestRunner {
1270 self.test_runner.get_or_insert_with(|| match self.config.seed {
1271 Some(seed) => TestRunner::new_with_rng(
1272 proptest::test_runner::Config::default(),
1273 TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>()),
1274 ),
1275 None => TestRunner::new(proptest::test_runner::Config::default()),
1276 })
1277 }
1278
1279 pub fn set_seed(&mut self, seed: U256) {
1280 self.test_runner = Some(TestRunner::new_with_rng(
1281 proptest::test_runner::Config::default(),
1282 TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>()),
1283 ));
1284 }
1285
1286 pub fn arbitrary_storage(&mut self) -> &mut ArbitraryStorage {
1289 self.arbitrary_storage.get_or_insert_with(ArbitraryStorage::default)
1290 }
1291
1292 pub fn has_arbitrary_storage(&self, address: &Address) -> bool {
1294 match &self.arbitrary_storage {
1295 Some(storage) => storage.values.contains_key(address),
1296 None => false,
1297 }
1298 }
1299
1300 pub fn should_overwrite_arbitrary_storage(
1304 &self,
1305 address: &Address,
1306 storage_slot: U256,
1307 ) -> bool {
1308 match &self.arbitrary_storage {
1309 Some(storage) => {
1310 storage.overwrites.contains(address)
1311 && storage
1312 .values
1313 .get(address)
1314 .and_then(|arbitrary_values| arbitrary_values.get(&storage_slot))
1315 .is_none()
1316 }
1317 None => false,
1318 }
1319 }
1320
1321 pub fn is_arbitrary_storage_copy(&self, address: &Address) -> bool {
1323 match &self.arbitrary_storage {
1324 Some(storage) => storage.copies.contains_key(address),
1325 None => false,
1326 }
1327 }
1328
1329 pub fn struct_defs(&self) -> Option<&foundry_common::fmt::StructDefinitions> {
1331 self.analysis.as_ref().and_then(|analysis| analysis.struct_defs().ok())
1332 }
1333}
1334
1335impl<FEN: FoundryEvmNetwork> Inspector<FoundryContextFor<'_, FEN>> for Cheatcodes<FEN> {
1336 fn initialize_interp(
1337 &mut self,
1338 interpreter: &mut Interpreter,
1339 ecx: &mut FoundryContextFor<'_, FEN>,
1340 ) {
1341 if let Some(block) = self.block.take() {
1344 ecx.set_block(block);
1345 }
1346 if let Some(gas_price) = self.gas_price.take() {
1347 ecx.tx_mut().set_gas_price(gas_price);
1348 }
1349
1350 if self.gas_metering.paused {
1352 self.gas_metering.paused_frames.push(interpreter.gas);
1353 }
1354
1355 if let Some(expected) = &mut self.expected_revert {
1357 expected.max_depth = max(ecx.journal().depth(), expected.max_depth);
1358 }
1359 }
1360
1361 fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut FoundryContextFor<'_, FEN>) {
1362 self.pc = interpreter.bytecode.pc();
1363
1364 if self.broadcast.is_some() {
1365 self.set_gas_limit_type(interpreter);
1366 }
1367
1368 if self.gas_metering.paused {
1370 self.meter_gas(interpreter);
1371 }
1372
1373 if self.gas_metering.reset {
1375 self.meter_gas_reset(interpreter);
1376 }
1377
1378 if self.recording_accesses {
1380 self.record_accesses(interpreter);
1381 }
1382
1383 if self.recorded_account_diffs_stack.is_some() {
1385 self.record_state_diffs(interpreter, ecx);
1386 }
1387
1388 if !self.allowed_mem_writes.is_empty() {
1390 self.check_mem_opcodes(
1391 interpreter,
1392 ecx.journal().depth().try_into().expect("journaled state depth exceeds u64"),
1393 );
1394 }
1395
1396 if let Some(mapping_slots) = &mut self.mapping_slots {
1398 mapping_step(mapping_slots, interpreter);
1399 }
1400
1401 if self.gas_metering.recording {
1403 self.meter_gas_record(interpreter, ecx);
1404 }
1405
1406 if !self.env_overrides.is_empty() {
1411 let fork_id = ecx.db().active_fork_id();
1412 if let Some(env_overrides) =
1413 self.env_overrides.get_mut(&fork_id).filter(|o| o.is_any_set())
1414 {
1415 env_overrides.pending_opcode = None;
1419 env_overrides.pending_blobhash_index = None;
1420
1421 let opcode = interpreter.bytecode.opcode();
1422 match opcode {
1423 op::BASEFEE | op::GASPRICE => {
1424 env_overrides.pending_opcode = Some(opcode);
1425 }
1426 op::BLOBHASH => {
1427 env_overrides.pending_opcode = Some(opcode);
1428 env_overrides.pending_blobhash_index =
1429 interpreter.stack.peek(0).ok().and_then(|index| index.try_into().ok());
1430 }
1431 _ => {}
1432 }
1433 }
1434 }
1435 }
1436
1437 fn step_end(&mut self, interpreter: &mut Interpreter, ecx: &mut FoundryContextFor<'_, FEN>) {
1438 if self.gas_metering.paused {
1439 self.meter_gas_end(interpreter);
1440 }
1441
1442 if self.gas_metering.touched {
1443 self.meter_gas_check(interpreter);
1444 }
1445
1446 if self.arbitrary_storage.is_some() {
1448 self.arbitrary_storage_end(interpreter, ecx);
1449 }
1450
1451 if !self.env_overrides.is_empty() {
1461 let fork_id = ecx.db().active_fork_id();
1462 if self.env_overrides.get(&fork_id).is_some_and(|o| o.is_any_set()) {
1463 let opcode_failed = interpreter
1469 .bytecode
1470 .action
1471 .as_ref()
1472 .and_then(|a| a.instruction_result())
1473 .is_some();
1474 if opcode_failed {
1475 if let Some(env_overrides) = self.env_overrides.get_mut(&fork_id) {
1476 env_overrides.pending_opcode = None;
1477 env_overrides.pending_blobhash_index = None;
1478 }
1479 } else {
1480 self.apply_env_overrides(interpreter, fork_id);
1481 }
1482 }
1483 }
1484 }
1485
1486 fn log(&mut self, _ecx: &mut FoundryContextFor<'_, FEN>, log: Log) {
1487 if !self.expected_emits.is_empty()
1488 && let Some(err) = expect::handle_expect_emit(self, &log, None)
1489 {
1490 let _ = sh_err!("{err:?}");
1494 }
1495
1496 record_logs(&mut self.recorded_logs, &log);
1498 }
1499
1500 fn log_full(
1501 &mut self,
1502 interpreter: &mut Interpreter,
1503 _ecx: &mut FoundryContextFor<'_, FEN>,
1504 log: Log,
1505 ) {
1506 if !self.expected_emits.is_empty() {
1507 expect::handle_expect_emit(self, &log, Some(interpreter));
1508 }
1509
1510 record_logs(&mut self.recorded_logs, &log);
1512 }
1513
1514 fn call(
1515 &mut self,
1516 ecx: &mut FoundryContextFor<'_, FEN>,
1517 inputs: &mut CallInputs,
1518 ) -> Option<CallOutcome> {
1519 Self::call_with_executor(self, ecx, inputs, &mut TransparentCheatcodesExecutor)
1520 }
1521
1522 fn call_end(
1523 &mut self,
1524 ecx: &mut FoundryContextFor<'_, FEN>,
1525 call: &CallInputs,
1526 outcome: &mut CallOutcome,
1527 ) {
1528 let cheatcode_call = call.target_address == CHEATCODE_ADDRESS
1529 || call.target_address == HARDHAT_CONSOLE_ADDRESS;
1530
1531 if !cheatcode_call {
1535 let curr_depth = ecx.journal().depth();
1537 if let Some(prank) = &self.get_prank(curr_depth)
1538 && curr_depth == prank.depth
1539 {
1540 ecx.tx_mut().set_caller(prank.prank_origin);
1541
1542 if prank.single_call {
1544 self.pranks.remove(&curr_depth);
1545 }
1546 }
1547
1548 if let Some(broadcast) = &self.broadcast
1550 && curr_depth == broadcast.depth
1551 {
1552 ecx.tx_mut().set_caller(broadcast.original_origin);
1553
1554 if broadcast.single_call {
1556 let _ = self.broadcast.take();
1557 }
1558 }
1559 }
1560
1561 if let Some(assume_no_revert) = &mut self.assume_no_revert {
1563 if outcome.result.is_revert() && assume_no_revert.reverted_by.is_none() {
1566 assume_no_revert.reverted_by = Some(call.target_address);
1567 }
1568
1569 let curr_depth = ecx.journal().depth();
1571 if curr_depth <= assume_no_revert.depth && !cheatcode_call {
1572 if outcome.result.is_revert() {
1575 let assume_no_revert = std::mem::take(&mut self.assume_no_revert).unwrap();
1576 return match revert_handlers::handle_assume_no_revert(
1577 &assume_no_revert,
1578 outcome.result.result,
1579 &outcome.result.output,
1580 &self.config.available_artifacts,
1581 ) {
1582 Ok(_) => {
1585 outcome.result.output = Error::from(MAGIC_ASSUME).abi_encode().into();
1586 }
1587 Err(error) => {
1590 trace!(expected=?assume_no_revert, ?error, status=?outcome.result.result, "Expected revert mismatch");
1591 outcome.result.result = InstructionResult::Revert;
1592 outcome.result.output = error.abi_encode().into();
1593 }
1594 };
1595 }
1596 self.assume_no_revert = None;
1598 }
1599 }
1600
1601 if let Some(expected_revert) = &mut self.expected_revert {
1603 let call_failed = !matches!(outcome.result.result, return_ok!());
1606 if call_failed {
1607 if expected_revert.reverter.is_some()
1611 && (expected_revert.reverted_by.is_none() || expected_revert.count > 1)
1612 {
1613 expected_revert.reverted_by = Some(call.target_address);
1614 }
1615 }
1616
1617 let curr_depth = ecx.journal().depth();
1618 if curr_depth <= expected_revert.depth {
1619 let internal = self.config.internal_expect_revert;
1624 let went_deeper = expected_revert.max_depth > expected_revert.depth;
1625 let needs_processing = match expected_revert.kind {
1626 ExpectedRevertKind::Default => (|| {
1627 if cheatcode_call {
1629 return false;
1630 }
1631 if call_failed {
1633 return true;
1634 }
1635 if !internal && went_deeper {
1637 return true;
1638 }
1639 if curr_depth == 0 {
1641 return true;
1642 }
1643 !internal
1646 })(),
1647 ExpectedRevertKind::Cheatcode { pending_processing } => {
1650 cheatcode_call && !pending_processing
1651 }
1652 };
1653
1654 if needs_processing {
1655 let mut expected_revert = std::mem::take(&mut self.expected_revert).unwrap();
1656 return match revert_handlers::handle_expect_revert(
1657 cheatcode_call,
1658 false,
1659 self.config.internal_expect_revert,
1660 &expected_revert,
1661 outcome.result.result,
1662 outcome.result.output.clone(),
1663 &self.config.available_artifacts,
1664 ) {
1665 Err(error) => {
1666 trace!(expected=?expected_revert, ?error, status=?outcome.result.result, "Expected revert mismatch");
1667 outcome.result.result = InstructionResult::Revert;
1668 outcome.result.output = error.abi_encode().into();
1669 }
1670 Ok((_, retdata)) => {
1671 expected_revert.actual_count += 1;
1672 if expected_revert.actual_count < expected_revert.count {
1673 self.expected_revert = Some(expected_revert);
1674 }
1675 outcome.result.result = InstructionResult::Return;
1676 outcome.result.output = retdata;
1677 }
1678 };
1679 }
1680
1681 if let ExpectedRevertKind::Cheatcode { pending_processing } =
1684 &mut self.expected_revert.as_mut().unwrap().kind
1685 {
1686 *pending_processing = false;
1687 }
1688 }
1689 }
1690
1691 if cheatcode_call {
1694 return;
1695 }
1696
1697 let gas = outcome.result.gas;
1700 self.gas_metering.last_call_gas = Some(crate::Vm::Gas {
1701 gasLimit: gas.limit(),
1702 gasTotalUsed: gas.total_gas_spent(),
1703 gasMemoryUsed: 0,
1704 gasRefunded: gas.refunded(),
1705 gasRemaining: gas.remaining(),
1706 });
1707
1708 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1711 if ecx.journal().depth() > 0
1713 && let Some(mut last_recorded_depth) = recorded_account_diffs_stack.pop()
1714 {
1715 if outcome.result.is_revert() {
1718 for element in &mut *last_recorded_depth {
1719 element.reverted = true;
1720 for storage_access in &mut element.storageAccesses {
1721 storage_access.reverted = true;
1722 }
1723 }
1724 }
1725
1726 if let Some(call_access) = last_recorded_depth.first_mut() {
1727 let curr_depth = ecx.journal().depth();
1732 if call_access.depth == curr_depth as u64
1733 && let Ok(acc) = ecx.journal_mut().load_account(call.target_address)
1734 {
1735 debug_assert!(access_is_call(call_access.kind));
1736 call_access.newBalance = acc.data.info.balance;
1737 call_access.newNonce = acc.data.info.nonce;
1738 }
1739 if let Some(last) = recorded_account_diffs_stack.last_mut() {
1744 last.extend(last_recorded_depth);
1745 } else {
1746 recorded_account_diffs_stack.push(last_recorded_depth);
1747 }
1748 }
1749 }
1750 }
1751
1752 let diag = self.fork_revert_diagnostic.take();
1755
1756 if outcome.result.is_revert() {
1759 if let Some(err) = diag {
1762 outcome.result.output = Error::encode(err.to_error_msg(&self.labels));
1763 }
1764 return;
1765 }
1766
1767 let should_check_emits = self
1779 .expected_emits
1780 .iter()
1781 .any(|(expected, _)| {
1782 let curr_depth = ecx.journal().depth();
1783 expected.depth == curr_depth
1784 }) &&
1785 !call.is_static;
1787 if should_check_emits {
1788 let expected_counts = self
1789 .expected_emits
1790 .iter()
1791 .filter_map(|(expected, count_map)| {
1792 let count = match expected.address {
1793 Some(emitter) => match count_map.get(&emitter) {
1794 Some(log_count) => expected
1795 .log
1796 .as_ref()
1797 .map(|l| log_count.count(l))
1798 .unwrap_or_else(|| log_count.count_unchecked()),
1799 None => 0,
1800 },
1801 None => match &expected.log {
1802 Some(log) => count_map.values().map(|logs| logs.count(log)).sum(),
1803 None => count_map.values().map(|logs| logs.count_unchecked()).sum(),
1804 },
1805 };
1806
1807 (count != expected.count).then_some((expected, count))
1808 })
1809 .collect::<Vec<_>>();
1810
1811 if let Some((expected, _)) = self
1813 .expected_emits
1814 .iter()
1815 .find(|(expected, _)| !expected.found && expected.count > 0)
1816 {
1817 outcome.result.result = InstructionResult::Revert;
1818 let error_msg = expected.mismatch_error.as_deref().unwrap_or("log != expected log");
1819 outcome.result.output = error_msg.abi_encode().into();
1820 return;
1821 }
1822
1823 if !expected_counts.is_empty() {
1824 let msg = if outcome.result.is_ok() {
1825 let (expected, count) = expected_counts.first().unwrap();
1826 format!("log emitted {count} times, expected {}", expected.count)
1827 } else {
1828 "expected an emit, but the call reverted instead. \
1829 ensure you're testing the happy path when using `expectEmit`"
1830 .to_string()
1831 };
1832
1833 outcome.result.result = InstructionResult::Revert;
1834 outcome.result.output = Error::encode(msg);
1835 return;
1836 }
1837
1838 self.expected_emits.clear()
1842 }
1843
1844 if let TxKind::Call(test_contract) = ecx.tx().kind() {
1847 if ecx.db().is_forked_mode()
1850 && outcome.result.result == InstructionResult::Stop
1851 && call.target_address != test_contract
1852 {
1853 self.fork_revert_diagnostic =
1854 ecx.db().diagnose_revert(call.target_address, ecx.journal().evm_state());
1855 }
1856 }
1857
1858 if ecx.journal().depth() == 0 {
1860 if outcome.result.is_revert() {
1864 return;
1865 }
1866
1867 for (address, calldatas) in &self.expected_calls {
1872 for (calldata, (expected, actual_count)) in calldatas {
1874 let ExpectedCallData { gas, min_gas, value, count, call_type } = expected;
1876
1877 let failed = match call_type {
1878 ExpectedCallType::Count => *count != *actual_count,
1882 ExpectedCallType::NonCount => *count > *actual_count,
1887 };
1888 if failed {
1889 let expected_values = [
1890 Some(format!("data {}", hex::encode_prefixed(calldata))),
1891 value.as_ref().map(|v| format!("value {v}")),
1892 gas.map(|g| format!("gas {g}")),
1893 min_gas.map(|g| format!("minimum gas {g}")),
1894 ]
1895 .into_iter()
1896 .flatten()
1897 .join(", ");
1898 let but = if outcome.result.is_ok() {
1899 let s = if *actual_count == 1 { "" } else { "s" };
1900 format!("was called {actual_count} time{s}")
1901 } else {
1902 "the call reverted instead; \
1903 ensure you're testing the happy path when using `expectCall`"
1904 .to_string()
1905 };
1906 let s = if *count == 1 { "" } else { "s" };
1907 let msg = format!(
1908 "expected call to {address} with {expected_values} \
1909 to be called {count} time{s}, but {but}"
1910 );
1911 outcome.result.result = InstructionResult::Revert;
1912 outcome.result.output = Error::encode(msg);
1913
1914 return;
1915 }
1916 }
1917 }
1918
1919 for (expected, _) in &mut self.expected_emits {
1923 if expected.count == 0 && !expected.found {
1924 expected.found = true;
1925 }
1926 }
1927 self.expected_emits.retain(|(expected, _)| !expected.found);
1928 if !self.expected_emits.is_empty() {
1930 let msg = if outcome.result.is_ok() {
1931 "expected an emit, but no logs were emitted afterwards. \
1932 you might have mismatched events or not enough events were emitted"
1933 } else {
1934 "expected an emit, but the call reverted instead. \
1935 ensure you're testing the happy path when using `expectEmit`"
1936 };
1937 outcome.result.result = InstructionResult::Revert;
1938 outcome.result.output = Error::encode(msg);
1939 return;
1940 }
1941
1942 if let Some(expected_create) = self.expected_creates.first() {
1944 let msg = format!(
1945 "expected {} call by address {} for bytecode {} but not found",
1946 expected_create.create_scheme,
1947 hex::encode_prefixed(expected_create.deployer),
1948 hex::encode_prefixed(&expected_create.bytecode),
1949 );
1950 outcome.result.result = InstructionResult::Revert;
1951 outcome.result.output = Error::encode(msg);
1952 }
1953 }
1954 }
1955
1956 fn create(
1957 &mut self,
1958 ecx: &mut FoundryContextFor<'_, FEN>,
1959 mut input: &mut CreateInputs,
1960 ) -> Option<CreateOutcome> {
1961 if let Some(spec_id) = self.execution_evm_version {
1963 ecx.cfg_mut().set_spec_and_mainnet_gas_params(spec_id);
1964 }
1965
1966 let gas = Gas::new(input.gas_limit());
1967 if self.intercept_next_create_call {
1969 self.intercept_next_create_call = false;
1971
1972 let output = input.init_code();
1974
1975 return Some(CreateOutcome {
1977 result: InterpreterResult { result: InstructionResult::Revert, output, gas },
1978 address: None,
1979 });
1980 }
1981
1982 let curr_depth = ecx.journal().depth();
1983
1984 if let Some(prank) = &self.get_prank(curr_depth)
1986 && curr_depth >= prank.depth
1987 && input.caller() == prank.prank_caller
1988 {
1989 let prank_applied = if curr_depth == prank.depth {
1991 let _ = journaled_account(ecx, prank.new_caller);
1993 input.set_caller(prank.new_caller);
1994 true
1995 } else {
1996 false
1997 };
1998
1999 let prank_applied = if let Some(new_origin) = prank.new_origin {
2001 ecx.tx_mut().set_caller(new_origin);
2002 true
2003 } else {
2004 prank_applied
2005 };
2006
2007 if prank_applied && let Some(applied_prank) = prank.first_time_applied() {
2009 self.pranks.insert(curr_depth, applied_prank);
2010 }
2011 }
2012
2013 self.apply_accesslist(ecx);
2015
2016 if let Some(broadcast) = &mut self.broadcast
2018 && curr_depth >= broadcast.depth
2019 && input.caller() == broadcast.original_caller
2020 {
2021 if let Err(err) = ecx.journal_mut().load_account(broadcast.new_origin) {
2022 return Some(CreateOutcome {
2023 result: InterpreterResult {
2024 result: InstructionResult::Revert,
2025 output: Error::encode(err),
2026 gas,
2027 },
2028 address: None,
2029 });
2030 }
2031
2032 ecx.tx_mut().set_caller(broadcast.new_origin);
2033
2034 if curr_depth == broadcast.depth || broadcast.deploy_from_code {
2035 broadcast.deploy_from_code = false;
2037
2038 input.set_caller(broadcast.new_origin);
2039
2040 let rpc = ecx.db().active_fork_url();
2041 let account = &ecx.journal().evm_state()[&broadcast.new_origin];
2042 let mut tx_req = TransactionRequestFor::<FEN>::default()
2043 .with_from(broadcast.new_origin)
2044 .with_kind(TxKind::Create)
2045 .with_value(input.value())
2046 .with_input(input.init_code())
2047 .with_nonce(account.info.nonce);
2048 if let Some(fee_token) = self.config.fee_token {
2049 tx_req.set_fee_token(fee_token);
2050 }
2051 self.broadcastable_transactions.push_back(BroadcastableTransaction {
2052 rpc,
2053 transaction: TransactionMaybeSigned::new(tx_req),
2054 });
2055
2056 input.log_debug(self, &input.scheme().unwrap_or(CreateScheme::Create));
2057 }
2058 }
2059
2060 let address = input.allow_cheatcodes(self, ecx);
2062
2063 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
2065 recorded_account_diffs_stack.push(vec![AccountAccess {
2066 chainInfo: crate::Vm::ChainInfo {
2067 forkId: ecx.db().active_fork_id().unwrap_or_default(),
2068 chainId: U256::from(ecx.cfg().chain_id()),
2069 },
2070 accessor: input.caller(),
2071 account: address,
2072 kind: crate::Vm::AccountAccessKind::Create,
2073 initialized: true,
2074 oldBalance: U256::ZERO, newBalance: U256::ZERO, oldNonce: 0, newNonce: 1, value: input.value(),
2079 data: input.init_code(),
2080 reverted: false,
2081 deployedCode: Bytes::new(), storageAccesses: vec![], depth: curr_depth as u64,
2084 }]);
2085 }
2086
2087 None
2088 }
2089
2090 fn create_end(
2091 &mut self,
2092 ecx: &mut FoundryContextFor<'_, FEN>,
2093 call: &CreateInputs,
2094 outcome: &mut CreateOutcome,
2095 ) {
2096 let call = Some(call);
2097 let curr_depth = ecx.journal().depth();
2098
2099 if let Some(prank) = &self.get_prank(curr_depth)
2101 && curr_depth == prank.depth
2102 {
2103 ecx.tx_mut().set_caller(prank.prank_origin);
2104
2105 if prank.single_call {
2107 std::mem::take(&mut self.pranks);
2108 }
2109 }
2110
2111 if let Some(broadcast) = &self.broadcast
2113 && curr_depth == broadcast.depth
2114 {
2115 ecx.tx_mut().set_caller(broadcast.original_origin);
2116
2117 if broadcast.single_call {
2119 std::mem::take(&mut self.broadcast);
2120 }
2121 }
2122
2123 if let Some(expected_revert) = &mut self.expected_revert {
2125 if outcome.result.is_revert()
2137 && expected_revert.reverter.is_some()
2138 && expected_revert.reverted_by.is_none()
2139 && let Some(addr) = outcome.address
2140 {
2141 expected_revert.reverted_by = Some(addr);
2142 }
2143
2144 if curr_depth <= expected_revert.depth
2145 && matches!(expected_revert.kind, ExpectedRevertKind::Default)
2146 {
2147 let mut expected_revert = std::mem::take(&mut self.expected_revert).unwrap();
2148 return match revert_handlers::handle_expect_revert(
2149 false,
2150 true,
2151 self.config.internal_expect_revert,
2152 &expected_revert,
2153 outcome.result.result,
2154 outcome.result.output.clone(),
2155 &self.config.available_artifacts,
2156 ) {
2157 Ok((address, retdata)) => {
2158 expected_revert.actual_count += 1;
2159 if expected_revert.actual_count < expected_revert.count {
2160 expected_revert.reverted_by = None;
2162 self.expected_revert = Some(expected_revert.clone());
2163 }
2164
2165 outcome.result.result = InstructionResult::Return;
2166 outcome.result.output = retdata;
2167 outcome.address = address;
2168 }
2169 Err(err) => {
2170 outcome.result.result = InstructionResult::Revert;
2171 outcome.result.output = err.abi_encode().into();
2172 }
2173 };
2174 }
2175 }
2176
2177 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
2180 if curr_depth > 0
2182 && let Some(last_depth) = &mut recorded_account_diffs_stack.pop()
2183 {
2184 if outcome.result.is_revert() {
2187 for element in &mut *last_depth {
2188 element.reverted = true;
2189 for storage_access in &mut element.storageAccesses {
2190 storage_access.reverted = true;
2191 }
2192 }
2193 }
2194
2195 if let Some(create_access) = last_depth.first_mut() {
2196 let depth = ecx.journal().depth();
2201 if create_access.depth == depth as u64 {
2202 debug_assert_eq!(
2203 create_access.kind as u8,
2204 crate::Vm::AccountAccessKind::Create as u8
2205 );
2206 if let Some(address) = outcome.address
2207 && let Ok(created_acc) = ecx.journal_mut().load_account(address)
2208 {
2209 create_access.newBalance = created_acc.data.info.balance;
2210 create_access.newNonce = created_acc.data.info.nonce;
2211 create_access.deployedCode = created_acc
2212 .data
2213 .info
2214 .code
2215 .clone()
2216 .unwrap_or_default()
2217 .original_bytes();
2218 }
2219 }
2220 if let Some(last) = recorded_account_diffs_stack.last_mut() {
2225 last.append(last_depth);
2226 } else {
2227 recorded_account_diffs_stack.push(last_depth.clone());
2228 }
2229 }
2230 }
2231 }
2232
2233 if !self.expected_creates.is_empty()
2235 && let (Some(address), Some(call)) = (outcome.address, call)
2236 && let Ok(created_acc) = ecx.journal_mut().load_account(address)
2237 {
2238 let bytecode = created_acc.data.info.code.clone().unwrap_or_default().original_bytes();
2239 if let Some((index, _)) =
2240 self.expected_creates.iter().find_position(|expected_create| {
2241 expected_create.deployer == call.caller()
2242 && expected_create.create_scheme.eq(call.scheme().into())
2243 && expected_create.bytecode == bytecode
2244 })
2245 {
2246 self.expected_creates.swap_remove(index);
2247 }
2248 }
2249 }
2250}
2251
2252impl<FEN: FoundryEvmNetwork> InspectorExt for Cheatcodes<FEN> {
2253 fn should_use_create2_factory(&mut self, depth: usize, inputs: &CreateInputs) -> bool {
2254 let target_depth = if let Some(prank) = &self.get_prank(depth) {
2255 prank.depth
2256 } else if let Some(broadcast) = &self.broadcast {
2257 broadcast.depth
2258 } else {
2259 1
2260 };
2261
2262 if depth != target_depth {
2263 return false;
2264 }
2265
2266 match inputs.scheme() {
2267 CreateScheme::Create2 { .. } => {
2268 self.broadcast.is_some() || self.config.always_use_create_2_factory
2269 }
2270 CreateScheme::Create => self.config.batch_rewrite_creates && self.broadcast.is_some(),
2271 _ => false,
2272 }
2273 }
2274
2275 fn create2_deployer(&self) -> Address {
2276 self.config.evm_opts.create2_deployer
2277 }
2278}
2279
2280impl<FEN: FoundryEvmNetwork> Cheatcodes<FEN> {
2281 #[cold]
2282 fn meter_gas(&mut self, interpreter: &mut Interpreter) {
2283 if let Some(paused_gas) = self.gas_metering.paused_frames.last() {
2284 let memory = *interpreter.gas.memory();
2287 interpreter.gas = *paused_gas;
2288 interpreter.gas.memory_mut().words_num = memory.words_num;
2289 interpreter.gas.memory_mut().expansion_cost = memory.expansion_cost;
2290 } else {
2291 self.gas_metering.paused_frames.push(interpreter.gas);
2293 }
2294 }
2295
2296 #[cold]
2297 fn meter_gas_record(
2298 &mut self,
2299 interpreter: &mut Interpreter,
2300 ecx: &mut FoundryContextFor<'_, FEN>,
2301 ) {
2302 if interpreter.bytecode.action.as_ref().and_then(|i| i.instruction_result()).is_none() {
2303 self.gas_metering.gas_records.iter_mut().for_each(|record| {
2304 let curr_depth = ecx.journal().depth();
2305 if curr_depth == record.depth {
2306 if self.gas_metering.last_gas_used != 0 {
2309 let gas_diff = interpreter
2310 .gas
2311 .total_gas_spent()
2312 .saturating_sub(self.gas_metering.last_gas_used);
2313 record.gas_used = record.gas_used.saturating_add(gas_diff);
2314 }
2315
2316 self.gas_metering.last_gas_used = interpreter.gas.total_gas_spent();
2319 }
2320 });
2321 }
2322 }
2323
2324 #[cold]
2325 fn meter_gas_end(&mut self, interpreter: &mut Interpreter) {
2326 if let Some(interpreter_action) = interpreter.bytecode.action.as_ref()
2328 && will_exit(interpreter_action)
2329 {
2330 self.gas_metering.paused_frames.pop();
2331 }
2332 }
2333
2334 #[cold]
2335 const fn meter_gas_reset(&mut self, interpreter: &mut Interpreter) {
2336 let mut gas = Gas::new(interpreter.gas.limit());
2337 gas.memory_mut().words_num = interpreter.gas.memory().words_num;
2338 gas.memory_mut().expansion_cost = interpreter.gas.memory().expansion_cost;
2339 interpreter.gas = gas;
2340 self.gas_metering.reset = false;
2341 }
2342
2343 #[cold]
2344 fn meter_gas_check(&mut self, interpreter: &mut Interpreter) {
2345 if let Some(interpreter_action) = interpreter.bytecode.action.as_ref()
2346 && will_exit(interpreter_action)
2347 {
2348 if interpreter.gas.total_gas_spent()
2352 < u64::try_from(interpreter.gas.refunded()).unwrap_or_default()
2353 {
2354 interpreter.gas = Gas::new(interpreter.gas.limit());
2355 }
2356 }
2357 }
2358
2359 #[cold]
2373 fn apply_env_overrides(&mut self, interpreter: &mut Interpreter, fork_id: Option<U256>) {
2374 let Some(env_overrides) = self.env_overrides.get_mut(&fork_id) else { return };
2375 let Some(opcode) = env_overrides.pending_opcode.take() else { return };
2376 match opcode {
2377 op::BASEFEE => {
2378 if let Some(basefee) = env_overrides.basefee {
2379 Self::replace_top_of_stack(interpreter, U256::from(basefee));
2381 }
2382 }
2383 op::GASPRICE => {
2384 if let Some(gas_price) = env_overrides.gas_price {
2385 Self::replace_top_of_stack(interpreter, U256::from(gas_price));
2387 }
2388 }
2389 op::BLOBHASH => {
2390 let blob_hashes = env_overrides.blob_hashes.clone();
2391 let blobhash_index = env_overrides.pending_blobhash_index.take();
2392 if let Some(ref blob_hashes) = blob_hashes
2393 && let Some(index) = blobhash_index
2394 {
2395 let hash = blob_hashes.get(index as usize).copied().unwrap_or_default();
2398 Self::replace_top_of_stack(interpreter, hash.into());
2399 }
2400 }
2401 _ => {}
2402 }
2403 }
2404
2405 fn replace_top_of_stack(interpreter: &mut Interpreter, value: U256) {
2413 if interpreter.stack.pop().is_err() {
2414 debug_assert!(false, "env override expected opcode result on stack");
2415 return;
2416 }
2417 let _ = interpreter.stack.push(value);
2418 }
2419
2420 #[cold]
2428 fn arbitrary_storage_end(
2429 &mut self,
2430 interpreter: &mut Interpreter,
2431 ecx: &mut FoundryContextFor<'_, FEN>,
2432 ) {
2433 let (key, target_address) = if interpreter.bytecode.opcode() == op::SLOAD {
2434 (try_or_return!(interpreter.stack.peek(0)), interpreter.input.target_address)
2435 } else {
2436 return;
2437 };
2438
2439 let Some(value) = ecx.sload(target_address, key) else {
2440 return;
2441 };
2442
2443 if (value.is_cold && value.data.is_zero())
2444 || self.should_overwrite_arbitrary_storage(&target_address, key)
2445 {
2446 if self.has_arbitrary_storage(&target_address) {
2447 let arbitrary_value = self.rng().random();
2448 self.arbitrary_storage.as_mut().unwrap().save(
2449 ecx,
2450 target_address,
2451 key,
2452 arbitrary_value,
2453 );
2454 } else if self.is_arbitrary_storage_copy(&target_address) {
2455 let arbitrary_value = self.rng().random();
2456 self.arbitrary_storage.as_mut().unwrap().copy(
2457 ecx,
2458 target_address,
2459 key,
2460 arbitrary_value,
2461 );
2462 }
2463 }
2464 }
2465
2466 #[cold]
2468 fn record_accesses(&mut self, interpreter: &mut Interpreter) {
2469 let access = &mut self.accesses;
2470 match interpreter.bytecode.opcode() {
2471 op::SLOAD => {
2472 let key = try_or_return!(interpreter.stack.peek(0));
2473 access.record_read(interpreter.input.target_address, key);
2474 }
2475 op::SSTORE => {
2476 let key = try_or_return!(interpreter.stack.peek(0));
2477 access.record_write(interpreter.input.target_address, key);
2478 }
2479 _ => {}
2480 }
2481 }
2482
2483 #[cold]
2484 fn record_state_diffs(
2485 &mut self,
2486 interpreter: &mut Interpreter,
2487 ecx: &mut FoundryContextFor<'_, FEN>,
2488 ) {
2489 let Some(account_accesses) = &mut self.recorded_account_diffs_stack else { return };
2490 match interpreter.bytecode.opcode() {
2491 op::SELFDESTRUCT => {
2492 let Some(last) = account_accesses.last_mut() else { return };
2494
2495 let target = try_or_return!(interpreter.stack.peek(0));
2497 let target = Address::from_word(B256::from(target));
2498 let (initialized, old_balance, old_nonce) = ecx
2499 .journal_mut()
2500 .load_account(target)
2501 .map(|account| {
2502 (
2503 account.data.info.exists(),
2504 account.data.info.balance,
2505 account.data.info.nonce,
2506 )
2507 })
2508 .unwrap_or_default();
2509
2510 let value = ecx
2512 .balance(interpreter.input.target_address)
2513 .map(|b| b.data)
2514 .unwrap_or(U256::ZERO);
2515
2516 last.push(crate::Vm::AccountAccess {
2518 chainInfo: crate::Vm::ChainInfo {
2519 forkId: ecx.db().active_fork_id().unwrap_or_default(),
2520 chainId: U256::from(ecx.cfg().chain_id()),
2521 },
2522 accessor: interpreter.input.target_address,
2523 account: target,
2524 kind: crate::Vm::AccountAccessKind::SelfDestruct,
2525 initialized,
2526 oldBalance: old_balance,
2527 newBalance: old_balance + value,
2528 oldNonce: old_nonce,
2529 newNonce: old_nonce, value,
2531 data: Bytes::new(),
2532 reverted: false,
2533 deployedCode: Bytes::new(),
2534 storageAccesses: vec![],
2535 depth: ecx
2536 .journal()
2537 .depth()
2538 .try_into()
2539 .expect("journaled state depth exceeds u64"),
2540 });
2541 }
2542
2543 op::SLOAD => {
2544 let Some(last) = account_accesses.last_mut() else { return };
2545
2546 let key = try_or_return!(interpreter.stack.peek(0));
2547 let address = interpreter.input.target_address;
2548
2549 let present_value = if ecx.journal_mut().load_account(address).is_ok()
2553 && let Some(previous) = ecx.sload(address, key)
2554 {
2555 previous.data
2556 } else {
2557 U256::ZERO
2558 };
2559 let access = crate::Vm::StorageAccess {
2560 account: interpreter.input.target_address,
2561 slot: key.into(),
2562 isWrite: false,
2563 previousValue: present_value.into(),
2564 newValue: present_value.into(),
2565 reverted: false,
2566 };
2567 let curr_depth =
2568 ecx.journal().depth().try_into().expect("journaled state depth exceeds u64");
2569 append_storage_access(last, access, curr_depth);
2570 }
2571 op::SSTORE => {
2572 let Some(last) = account_accesses.last_mut() else { return };
2573
2574 let key = try_or_return!(interpreter.stack.peek(0));
2575 let value = try_or_return!(interpreter.stack.peek(1));
2576 let address = interpreter.input.target_address;
2577 let previous_value = if ecx.journal_mut().load_account(address).is_ok()
2580 && let Some(previous) = ecx.sload(address, key)
2581 {
2582 previous.data
2583 } else {
2584 U256::ZERO
2585 };
2586
2587 let access = crate::Vm::StorageAccess {
2588 account: address,
2589 slot: key.into(),
2590 isWrite: true,
2591 previousValue: previous_value.into(),
2592 newValue: value.into(),
2593 reverted: false,
2594 };
2595 let curr_depth =
2596 ecx.journal().depth().try_into().expect("journaled state depth exceeds u64");
2597 append_storage_access(last, access, curr_depth);
2598 }
2599
2600 op::EXTCODECOPY | op::EXTCODESIZE | op::EXTCODEHASH | op::BALANCE => {
2602 let kind = match interpreter.bytecode.opcode() {
2603 op::EXTCODECOPY => crate::Vm::AccountAccessKind::Extcodecopy,
2604 op::EXTCODESIZE => crate::Vm::AccountAccessKind::Extcodesize,
2605 op::EXTCODEHASH => crate::Vm::AccountAccessKind::Extcodehash,
2606 op::BALANCE => crate::Vm::AccountAccessKind::Balance,
2607 _ => unreachable!(),
2608 };
2609 let address =
2610 Address::from_word(B256::from(try_or_return!(interpreter.stack.peek(0))));
2611 let (initialized, balance, nonce) =
2612 if let Ok(acc) = ecx.journal_mut().load_account(address) {
2613 (acc.data.info.exists(), acc.data.info.balance, acc.data.info.nonce)
2614 } else {
2615 (false, U256::ZERO, 0)
2616 };
2617 let curr_depth =
2618 ecx.journal().depth().try_into().expect("journaled state depth exceeds u64");
2619 let account_access = crate::Vm::AccountAccess {
2620 chainInfo: crate::Vm::ChainInfo {
2621 forkId: ecx.db().active_fork_id().unwrap_or_default(),
2622 chainId: U256::from(ecx.cfg().chain_id()),
2623 },
2624 accessor: interpreter.input.target_address,
2625 account: address,
2626 kind,
2627 initialized,
2628 oldBalance: balance,
2629 newBalance: balance,
2630 oldNonce: nonce,
2631 newNonce: nonce, value: U256::ZERO,
2633 data: Bytes::new(),
2634 reverted: false,
2635 deployedCode: Bytes::new(),
2636 storageAccesses: vec![],
2637 depth: curr_depth,
2638 };
2639 if let Some(last) = account_accesses.last_mut() {
2642 last.push(account_access);
2643 } else {
2644 account_accesses.push(vec![account_access]);
2645 }
2646 }
2647 _ => {}
2648 }
2649 }
2650
2651 #[cold]
2656 fn check_mem_opcodes(&self, interpreter: &mut Interpreter, depth: u64) {
2657 let Some(ranges) = self.allowed_mem_writes.get(&depth) else {
2658 return;
2659 };
2660
2661 macro_rules! mem_opcode_match {
2670 ($(($opcode:ident, $offset_depth:expr, $size_depth:expr, $writes:expr)),* $(,)?) => {
2671 match interpreter.bytecode.opcode() {
2672 op::MSTORE => {
2677 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2679
2680 if !ranges.iter().any(|range| {
2683 range.contains(&offset) && range.contains(&(offset + 31))
2684 }) {
2685 let value = try_or_return!(interpreter.stack.peek(1)).to_be_bytes::<32>();
2690 if value[..SELECTOR_LEN] == stopExpectSafeMemoryCall::SELECTOR {
2691 return
2692 }
2693
2694 disallowed_mem_write(offset, 32, interpreter, ranges);
2695 return
2696 }
2697 }
2698 op::MSTORE8 => {
2699 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2701
2702 if !ranges.iter().any(|range| range.contains(&offset)) {
2705 disallowed_mem_write(offset, 1, interpreter, ranges);
2706 return
2707 }
2708 }
2709
2710 op::MLOAD => {
2715 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2717
2718 if offset >= interpreter.memory.size() as u64 && !ranges.iter().any(|range| {
2722 range.contains(&offset) && range.contains(&(offset + 31))
2723 }) {
2724 disallowed_mem_write(offset, 32, interpreter, ranges);
2725 return
2726 }
2727 }
2728
2729 op::CALL => {
2734 let dest_offset = try_or_return!(interpreter.stack.peek(5)).saturating_to::<u64>();
2736
2737 let size = try_or_return!(interpreter.stack.peek(6)).saturating_to::<u64>();
2739
2740 let fail_cond = !ranges.iter().any(|range| {
2744 range.contains(&dest_offset) &&
2745 range.contains(&(dest_offset + size.saturating_sub(1)))
2746 });
2747
2748 if fail_cond {
2751 let to = Address::from_word(try_or_return!(interpreter.stack.peek(1)).to_be_bytes::<32>().into());
2755 if to == CHEATCODE_ADDRESS {
2756 let args_offset = try_or_return!(interpreter.stack.peek(3)).saturating_to::<usize>();
2757 let args_size = try_or_return!(interpreter.stack.peek(4)).saturating_to::<usize>();
2758 let memory_word = interpreter.memory.slice_len(args_offset, args_size);
2759 if memory_word[..SELECTOR_LEN] == stopExpectSafeMemoryCall::SELECTOR {
2760 return
2761 }
2762 }
2763
2764 disallowed_mem_write(dest_offset, size, interpreter, ranges);
2765 return
2766 }
2767 }
2768
2769 $(op::$opcode => {
2770 let dest_offset = try_or_return!(interpreter.stack.peek($offset_depth)).saturating_to::<u64>();
2772
2773 let size = try_or_return!(interpreter.stack.peek($size_depth)).saturating_to::<u64>();
2775
2776 let fail_cond = !ranges.iter().any(|range| {
2780 range.contains(&dest_offset) &&
2781 range.contains(&(dest_offset + size.saturating_sub(1)))
2782 }) && ($writes ||
2783 [dest_offset, (dest_offset + size).saturating_sub(1)].into_iter().any(|offset| {
2784 offset >= interpreter.memory.size() as u64
2785 })
2786 );
2787
2788 if fail_cond {
2791 disallowed_mem_write(dest_offset, size, interpreter, ranges);
2792 return
2793 }
2794 })*
2795
2796 _ => {}
2797 }
2798 }
2799 }
2800
2801 mem_opcode_match!(
2804 (CALLDATACOPY, 0, 2, true),
2805 (CODECOPY, 0, 2, true),
2806 (RETURNDATACOPY, 0, 2, true),
2807 (EXTCODECOPY, 1, 3, true),
2808 (CALLCODE, 5, 6, true),
2809 (STATICCALL, 4, 5, true),
2810 (DELEGATECALL, 4, 5, true),
2811 (KECCAK256, 0, 1, false),
2812 (LOG0, 0, 1, false),
2813 (LOG1, 0, 1, false),
2814 (LOG2, 0, 1, false),
2815 (LOG3, 0, 1, false),
2816 (LOG4, 0, 1, false),
2817 (CREATE, 1, 2, false),
2818 (CREATE2, 1, 2, false),
2819 (RETURN, 0, 1, false),
2820 (REVERT, 0, 1, false),
2821 );
2822 }
2823
2824 #[cold]
2825 fn set_gas_limit_type(&mut self, interpreter: &mut Interpreter) {
2826 match interpreter.bytecode.opcode() {
2827 op::CREATE2 => self.dynamic_gas_limit = true,
2828 op::CALL => {
2829 self.dynamic_gas_limit =
2832 try_or_return!(interpreter.stack.peek(0)) >= interpreter.gas.remaining() - 100
2833 }
2834 _ => self.dynamic_gas_limit = false,
2835 }
2836 }
2837}
2838
2839fn disallowed_mem_write(
2845 dest_offset: u64,
2846 size: u64,
2847 interpreter: &mut Interpreter,
2848 ranges: &[Range<u64>],
2849) {
2850 let revert_string = format!(
2851 "memory write at offset 0x{:02X} of size 0x{:02X} not allowed; safe range: {}",
2852 dest_offset,
2853 size,
2854 ranges.iter().map(|r| format!("[0x{:02X}, 0x{:02X})", r.start, r.end)).join(" U ")
2855 );
2856
2857 interpreter.bytecode.set_action(InterpreterAction::new_return(
2858 InstructionResult::Revert,
2859 Bytes::from(revert_string.into_bytes()),
2860 interpreter.gas,
2861 ));
2862}
2863
2864const fn access_is_call(kind: crate::Vm::AccountAccessKind) -> bool {
2866 matches!(
2867 kind,
2868 crate::Vm::AccountAccessKind::Call
2869 | crate::Vm::AccountAccessKind::StaticCall
2870 | crate::Vm::AccountAccessKind::CallCode
2871 | crate::Vm::AccountAccessKind::DelegateCall
2872 )
2873}
2874
2875fn record_logs(recorded_logs: &mut Option<Vec<Vm::Log>>, log: &Log) {
2877 if let Some(storage_recorded_logs) = recorded_logs {
2878 storage_recorded_logs.push(Vm::Log {
2879 topics: log.data.topics().to_vec(),
2880 data: log.data.data.clone(),
2881 emitter: log.address,
2882 });
2883 }
2884}
2885
2886fn append_storage_access(
2888 last: &mut Vec<AccountAccess>,
2889 storage_access: crate::Vm::StorageAccess,
2890 storage_depth: u64,
2891) {
2892 if !last.is_empty() && last.first().unwrap().depth < storage_depth {
2894 if last.len() == 1 {
2900 last.first_mut().unwrap().storageAccesses.push(storage_access);
2901 } else {
2902 let last_record = last.last_mut().unwrap();
2903 if last_record.kind as u8 == crate::Vm::AccountAccessKind::Resume as u8 {
2904 last_record.storageAccesses.push(storage_access);
2905 } else {
2906 let entry = last.first().unwrap();
2907 let resume_record = crate::Vm::AccountAccess {
2908 chainInfo: crate::Vm::ChainInfo {
2909 forkId: entry.chainInfo.forkId,
2910 chainId: entry.chainInfo.chainId,
2911 },
2912 accessor: entry.accessor,
2913 account: entry.account,
2914 kind: crate::Vm::AccountAccessKind::Resume,
2915 initialized: entry.initialized,
2916 storageAccesses: vec![storage_access],
2917 reverted: entry.reverted,
2918 oldBalance: U256::ZERO,
2920 newBalance: U256::ZERO,
2921 oldNonce: 0,
2922 newNonce: 0,
2923 value: U256::ZERO,
2924 data: Bytes::new(),
2925 deployedCode: Bytes::new(),
2926 depth: entry.depth,
2927 };
2928 last.push(resume_record);
2929 }
2930 }
2931 }
2932}
2933
2934const fn cheatcode_of<T: spec::CheatcodeDef>(_: &T) -> &'static spec::Cheatcode<'static> {
2936 T::CHEATCODE
2937}
2938
2939fn cheatcode_name(cheat: &spec::Cheatcode<'static>) -> &'static str {
2940 cheat.func.signature.split('(').next().unwrap()
2941}
2942
2943const fn cheatcode_id(cheat: &spec::Cheatcode<'static>) -> &'static str {
2944 cheat.func.id
2945}
2946
2947const fn cheatcode_signature(cheat: &spec::Cheatcode<'static>) -> &'static str {
2948 cheat.func.signature
2949}
2950
2951fn apply_dispatch<FEN: FoundryEvmNetwork>(
2953 calls: &Vm::VmCalls,
2954 ccx: &mut CheatsCtxt<'_, '_, FEN>,
2955 executor: &mut dyn CheatcodesExecutor<FEN>,
2956) -> Result {
2957 macro_rules! get_cheatcode {
2959 ($($variant:ident),*) => {
2960 match calls {
2961 $(Vm::VmCalls::$variant(cheat) => cheatcode_of(cheat),)*
2962 }
2963 };
2964 }
2965 let cheat = vm_calls!(get_cheatcode);
2966
2967 let _guard = debug_span!(target: "cheatcodes", "apply", id = %cheatcode_id(cheat)).entered();
2968 trace!(target: "cheatcodes", cheat = %cheatcode_signature(cheat), "applying");
2969
2970 if let spec::Status::Deprecated(replacement) = cheat.status {
2971 ccx.state.deprecated.insert(cheatcode_signature(cheat), replacement);
2972 }
2973
2974 macro_rules! dispatch {
2976 ($($variant:ident),*) => {
2977 match calls {
2978 $(Vm::VmCalls::$variant(cheat) => Cheatcode::apply_full(cheat, ccx, executor),)*
2979 }
2980 };
2981 }
2982 let mut result = vm_calls!(dispatch);
2983
2984 if let Err(e) = &mut result
2986 && e.is_str()
2987 {
2988 let name = cheatcode_name(cheat);
2989 if !name.contains("assert") && name != "rpcUrl" {
2993 *e = fmt_err!("vm.{name}: {e}");
2994 }
2995 }
2996
2997 trace!(
2998 target: "cheatcodes",
2999 return = %match &result {
3000 Ok(b) => hex::encode(b),
3001 Err(e) => e.to_string(),
3002 }
3003 );
3004
3005 result
3006}
3007
3008const fn will_exit(action: &InterpreterAction) -> bool {
3010 match action {
3011 InterpreterAction::Return(result) => {
3012 result.result.is_ok_or_revert() || result.result.is_halt()
3013 }
3014 _ => false,
3015 }
3016}
3017
3018#[cfg(test)]
3019mod tests {
3020 use super::*;
3021
3022 fn cheats(flag: bool, broadcast: Option<Broadcast>) -> Cheatcodes {
3023 let config = CheatsConfig { batch_rewrite_creates: flag, ..Default::default() };
3024 let mut cheats = Cheatcodes::new(Arc::new(config));
3025 cheats.broadcast = broadcast;
3026 cheats
3027 }
3028
3029 fn create_inputs() -> CreateInputs {
3030 CreateInputs::new(Address::ZERO, CreateScheme::Create, U256::ZERO, Bytes::new(), 100_000, 0)
3031 }
3032
3033 fn broadcast_at(depth: usize) -> Broadcast {
3034 Broadcast { depth, ..Default::default() }
3035 }
3036
3037 #[test]
3038 fn flag_off_with_broadcast_returns_false() {
3039 let mut cheats = cheats(false, Some(broadcast_at(1)));
3040 assert!(!cheats.should_use_create2_factory(1, &create_inputs()));
3041 }
3042
3043 #[test]
3044 fn flag_on_without_broadcast_returns_false() {
3045 let mut cheats = cheats(true, None);
3046 assert!(!cheats.should_use_create2_factory(1, &create_inputs()));
3047 }
3048
3049 #[test]
3050 fn flag_on_with_broadcast_depth_mismatch_returns_false() {
3051 let mut cheats = cheats(true, Some(broadcast_at(2)));
3052 assert!(!cheats.should_use_create2_factory(1, &create_inputs()));
3053 }
3054
3055 #[test]
3056 fn flag_on_with_broadcast_depth_match_returns_true() {
3057 let mut cheats = cheats(true, Some(broadcast_at(1)));
3058 assert!(cheats.should_use_create2_factory(1, &create_inputs()));
3059 }
3060}