1#[cfg(feature = "monad")]
4use crate::monad::{apply_monad_cheatcode as apply_monad_cheatcode_call, is_monad_cheatcode_call};
5use crate::{
6 Cheatcode, CheatsConfig, CheatsCtxt, Error, Result,
7 Vm::{self, AccountAccess},
8 evm::{
9 DealRecord, GasRecord, RecordAccess, journaled_account,
10 mock::{MockCallDataContext, MockCallReturnData},
11 prank::Prank,
12 },
13 inspector::utils::CommonCreateInput,
14 script::{Broadcast, Wallets},
15 test::{
16 assume::AssumeNoRevert,
17 expect::{
18 self, ExpectedCallData, ExpectedCallTracker, ExpectedCallType, ExpectedCreate,
19 ExpectedEmitTracker, ExpectedRevert, ExpectedRevertKind,
20 },
21 revert_handlers,
22 },
23 utils::IgnoredTraces,
24};
25use alloy_consensus::BlobTransactionSidecarVariant;
26use alloy_network::{Ethereum, Network, TransactionBuilder};
27use alloy_primitives::{
28 Address, B256, Bytes, Log, TxKind, U256, hex,
29 map::{AddressHashMap, HashMap, HashSet},
30};
31use alloy_rpc_types::AccessList;
32use alloy_signer_local::PrivateKeySigner;
33use alloy_sol_types::{SolCall, SolInterface, SolValue};
34use foundry_common::{
35 FoundryTransactionBuilder, SELECTOR_LEN, TransactionMaybeSigned,
36 mapping_slots::{
37 MappingSlots, PendingMappingHash, capture_hash as capture_mapping_hash,
38 record_hash as record_mapping_hash, step as mapping_step,
39 },
40};
41use foundry_evm_core::{
42 Breakpoints, EvmEnv, FoundryTransaction, InspectorExt,
43 abi::Vm::stopExpectSafeMemoryCall,
44 backend::{ContextUpdate, DatabaseError, DatabaseExt, LocalForkId, RevertDiagnostic},
45 constants::{CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS, MAGIC_ASSUME},
46 env::FoundryContextExt,
47 evm::{
48 BlockEnvFor, ChainContextFor, EthEvmNetwork, FoundryContextFor, FoundryEvmFactory,
49 FoundryEvmNetwork, NestedEvmClosureFor, SpecFor, TransactionRequestFor,
50 TransactionStateFor, TxEnvFor, with_cloned_context,
51 },
52};
53use foundry_evm_traces::{
54 TracingInspector, TracingInspectorConfig, identifier::SignaturesIdentifier,
55};
56use foundry_wallets::wallet_multi::MultiWallet;
57use itertools::Itertools;
58use proptest::test_runner::{RngAlgorithm, TestRng, TestRunner};
59use rand::Rng;
60use revm::{
61 Inspector, JournalEntry,
62 bytecode::opcode as op,
63 context::{Cfg, ContextTr, Host, JournalTr, Transaction, TransactionType, result::EVMError},
64 context_interface::{CreateScheme, transaction::SignedAuthorization},
65 handler::FrameResult,
66 interpreter::{
67 CallInput, CallInputs, CallOutcome, CallScheme, CallValue, CreateInputs, CreateOutcome,
68 FrameInput, Gas, InstructionResult, Interpreter, InterpreterAction, InterpreterResult,
69 interpreter_types::{Jumps, LoopControl, MemoryTr, ReturnData},
70 return_ok,
71 },
72};
73use serde_json::Value;
74use std::{
75 cmp::max,
76 collections::{BTreeMap, VecDeque},
77 fmt::Debug,
78 fs::File,
79 io::BufReader,
80 ops::Range,
81 path::PathBuf,
82 sync::{Arc, OnceLock},
83};
84
85mod utils;
86
87pub mod analysis;
88pub use analysis::CheatcodeAnalysis;
89
90pub trait CheatcodesExecutor<FEN: FoundryEvmNetwork> {
92 fn with_nested_evm(
95 &mut self,
96 cheats: &mut Cheatcodes<FEN>,
97 ecx: &mut FoundryContextFor<'_, FEN>,
98 f: NestedEvmClosureFor<'_, FEN>,
99 ) -> Result<(), EVMError<DatabaseError>>;
100
101 fn transact_on_db(
103 &mut self,
104 cheats: &mut Cheatcodes<FEN>,
105 ecx: &mut FoundryContextFor<'_, FEN>,
106 fork_id: Option<U256>,
107 transaction: B256,
108 ) -> eyre::Result<ContextUpdate<ChainContextFor<FEN>>>;
109
110 fn transact_from_tx_on_db(
112 &mut self,
113 cheats: &mut Cheatcodes<FEN>,
114 ecx: &mut FoundryContextFor<'_, FEN>,
115 tx: TxEnvFor<FEN>,
116 ) -> eyre::Result<()>;
117
118 #[allow(clippy::type_complexity)]
123 fn with_fresh_nested_evm(
124 &mut self,
125 cheats: &mut Cheatcodes<FEN>,
126 db: &mut <FoundryContextFor<'_, FEN> as ContextTr>::Db,
127 evm_env: EvmEnv<SpecFor<FEN>, BlockEnvFor<FEN>>,
128 chain_context: ChainContextFor<FEN>,
129 f: NestedEvmClosureFor<'_, FEN>,
130 ) -> Result<EvmEnv<SpecFor<FEN>, BlockEnvFor<FEN>>, EVMError<DatabaseError>>;
131
132 fn console_log(&mut self, msg: &str);
134
135 fn tracing_inspector(&mut self) -> Option<&mut TracingInspector> {
137 None
138 }
139
140 fn set_in_inner_context(&mut self, _enabled: bool, _original_origin: Option<Address>) {}
144}
145
146pub(crate) fn exec_create<FEN: FoundryEvmNetwork>(
148 executor: &mut dyn CheatcodesExecutor<FEN>,
149 inputs: CreateInputs,
150 ccx: &mut CheatsCtxt<'_, '_, FEN>,
151) -> std::result::Result<CreateOutcome, EVMError<DatabaseError>> {
152 let mut inputs = Some(inputs);
153 let mut outcome = None;
154 executor.with_nested_evm(ccx.state, ccx.ecx, &mut |evm| {
155 let inputs = inputs.take().unwrap();
156 evm.journal_inner_mut().depth += 1;
157
158 let frame = FrameInput::Create(Box::new(inputs));
159
160 let result = match evm.run_execution(frame)? {
161 FrameResult::Call(_) => unreachable!(),
162 FrameResult::Create(create) => create,
163 };
164
165 evm.journal_inner_mut().depth -= 1;
166
167 outcome = Some(result);
168 Ok(())
169 })?;
170 Ok(outcome.unwrap())
171}
172
173#[derive(Debug, Default, Clone, Copy)]
176struct TransparentCheatcodesExecutor;
177
178impl<FEN: FoundryEvmNetwork> CheatcodesExecutor<FEN> for TransparentCheatcodesExecutor {
179 fn with_nested_evm(
180 &mut self,
181 cheats: &mut Cheatcodes<FEN>,
182 ecx: &mut FoundryContextFor<'_, FEN>,
183 f: NestedEvmClosureFor<'_, FEN>,
184 ) -> Result<(), EVMError<DatabaseError>> {
185 let factory = FEN::EvmFactory::default();
186 let chain_context = factory.capture_chain_context(ecx);
187 let state = factory.capture_transaction_state(ecx);
188 let mut nested_chain_context = None;
189 let mut transaction_state = None;
190 with_cloned_context(ecx, |db, evm_env, journaled_state| {
191 let mut evm = factory.create_foundry_nested_evm(db, evm_env, chain_context, cheats);
192 *evm.journal_inner_mut() = journaled_state;
193 evm.restore_transaction_state(state);
194 f(&mut *evm)?;
195 nested_chain_context = Some(evm.capture_chain_context());
196 transaction_state = Some(evm.capture_transaction_state());
197 let sub_inner = evm.journal_inner_mut().clone();
198 let sub_evm_env = evm.to_evm_env();
199 Ok((sub_evm_env, sub_inner))
200 })?;
201 factory.apply_context_transition(
202 ecx,
203 Some(&nested_chain_context.expect("nested EVM chain context was captured")),
204 );
205 factory.restore_transaction_state(
206 ecx,
207 transaction_state.expect("nested EVM state was captured"),
208 );
209 Ok(())
210 }
211
212 fn with_fresh_nested_evm(
213 &mut self,
214 cheats: &mut Cheatcodes<FEN>,
215 db: &mut <FoundryContextFor<'_, FEN> as ContextTr>::Db,
216 evm_env: EvmEnv<SpecFor<FEN>, BlockEnvFor<FEN>>,
217 chain_context: ChainContextFor<FEN>,
218 f: NestedEvmClosureFor<'_, FEN>,
219 ) -> Result<EvmEnv<SpecFor<FEN>, BlockEnvFor<FEN>>, EVMError<DatabaseError>> {
220 let mut evm = FEN::EvmFactory::default().create_foundry_nested_evm(
221 db,
222 evm_env,
223 chain_context,
224 cheats,
225 );
226 f(&mut *evm)?;
227 Ok(evm.to_evm_env())
228 }
229
230 fn transact_on_db(
231 &mut self,
232 cheats: &mut Cheatcodes<FEN>,
233 ecx: &mut FoundryContextFor<'_, FEN>,
234 fork_id: Option<U256>,
235 transaction: B256,
236 ) -> eyre::Result<ContextUpdate<ChainContextFor<FEN>>> {
237 let evm_env = ecx.evm_clone();
238 let outer_tx_env = ecx.tx_clone();
239 let (db, inner) = ecx.db_journal_inner_mut();
240 db.transact(fork_id, transaction, evm_env, &outer_tx_env, inner, cheats)
241 }
242
243 fn transact_from_tx_on_db(
244 &mut self,
245 cheats: &mut Cheatcodes<FEN>,
246 ecx: &mut FoundryContextFor<'_, FEN>,
247 tx: TxEnvFor<FEN>,
248 ) -> eyre::Result<()> {
249 let evm_env = ecx.evm_clone();
250 let (db, inner) = ecx.db_journal_inner_mut();
251 db.transact_from_tx(tx, evm_env, inner, cheats)
252 }
253
254 fn console_log(&mut self, _msg: &str) {}
255}
256
257macro_rules! try_or_return {
258 ($e:expr) => {
259 match $e {
260 Ok(v) => v,
261 Err(_) => return,
262 }
263 };
264}
265
266#[derive(Debug, Default)]
268pub struct TestContext {
269 pub opened_read_files: HashMap<PathBuf, BufReader<File>>,
271}
272
273impl Clone for TestContext {
275 fn clone(&self) -> Self {
276 Default::default()
277 }
278}
279
280impl TestContext {
281 pub fn clear(&mut self) {
283 self.opened_read_files.clear();
284 }
285}
286
287#[derive(Clone, Debug)]
289pub struct BroadcastableTransaction<N: Network = Ethereum> {
290 pub rpc: Option<String>,
292 pub transaction: TransactionMaybeSigned<N>,
294}
295
296#[derive(Clone, Debug, Copy)]
297pub struct RecordDebugStepInfo {
298 pub start_node_idx: usize,
300 pub original_tracer_config: TracingInspectorConfig,
302}
303
304#[derive(Clone, Debug, Default)]
333pub struct EnvOverrides {
334 pub basefee: Option<u64>,
336 pub gas_price: Option<u128>,
338 pub blob_hashes: Option<Vec<B256>>,
340 pub pre_override_gas_price: Option<u128>,
344 pub pre_override_tx_type: Option<u8>,
348 pub pre_override_blob_hashes: Option<Vec<B256>>,
351 pending_opcode: Option<u8>,
356 pending_blobhash_index: Option<u64>,
360}
361
362impl EnvOverrides {
363 #[inline]
365 pub const fn is_any_set(&self) -> bool {
366 self.basefee.is_some() || self.gas_price.is_some() || self.blob_hashes.is_some()
367 }
368}
369
370#[derive(Clone, Copy, Debug, PartialEq, Eq)]
372pub struct StorageHook {
373 pub callback_target: Address,
375 pub callback_selector: [u8; 4],
377}
378
379#[derive(Clone, Debug)]
380enum PendingStorageHook {
381 Load {
382 account: Address,
383 slot: U256,
384 hook: StorageHook,
385 },
386 Store {
387 account: Address,
388 slot: U256,
389 old_value: U256,
390 mapping: Option<(B256, Vec<B256>)>,
391 hook: StorageHook,
392 },
393}
394
395#[derive(Clone, Debug)]
396struct ActiveStorageHook {
397 parent_depth: usize,
398 callback_target: Address,
399 callback_input: Bytes,
400 saved_gas: Gas,
401 saved_return_data: Bytes,
402 saved_stack_item: Option<U256>,
403 journal_start: usize,
404 inspector_state: StorageHookInspectorState,
405 outcome: Option<(InstructionResult, Bytes)>,
406}
407
408#[derive(Clone, Debug)]
409struct StorageHookInspectorState {
410 accesses: RecordAccess,
411 recording_accesses: bool,
412 mapping_slots: Option<AddressHashMap<MappingSlots>>,
413 recorded_logs: Option<Vec<Vm::Log>>,
414 mocked_calls: HashMap<Address, BTreeMap<MockCallDataContext, VecDeque<MockCallReturnData>>>,
415 mocked_functions: HashMap<Address, HashMap<Bytes, Address>>,
416 expected_revert: Option<ExpectedRevert>,
417 assume_no_revert: Option<AssumeNoRevert>,
418 expected_calls: ExpectedCallTracker,
419 expected_emits: ExpectedEmitTracker,
420 expected_creates: Vec<ExpectedCreate>,
421}
422
423#[derive(Clone, Debug, Default)]
425pub struct GasMetering {
426 pub paused: bool,
428 pub touched: bool,
431 pub reset: bool,
433 pub paused_frames: Vec<Gas>,
435
436 pub active_gas_snapshot: Option<(String, String)>,
438
439 pub last_call_gas: Option<crate::Vm::Gas>,
442
443 pub last_frame_gas: Option<crate::Vm::Gas>,
446
447 pub recording: bool,
449 pub last_gas_used: u64,
451 pub gas_records: Vec<GasRecord>,
453}
454
455impl GasMetering {
456 pub const fn start(&mut self) {
458 self.recording = true;
459 }
460
461 pub const fn stop(&mut self) {
463 self.recording = false;
464 }
465
466 pub fn resume(&mut self) {
468 if self.paused {
469 self.paused = false;
470 self.touched = true;
471 }
472 self.paused_frames.clear();
473 }
474
475 pub fn reset(&mut self) {
477 self.paused = false;
478 self.touched = true;
479 self.reset = true;
480 self.paused_frames.clear();
481 }
482}
483
484#[derive(Clone, Debug, Default)]
486pub struct ArbitraryStorage {
487 values: HashMap<Address, HashMap<U256, U256>>,
491 copies: HashMap<Address, Address>,
493 overwrites: HashSet<Address>,
495 explicit_slots: HashMap<Address, HashSet<U256>>,
497}
498
499impl ArbitraryStorage {
500 pub fn mark_arbitrary(&mut self, address: &Address, overwrite: bool) {
502 self.values.insert(*address, HashMap::default());
503 self.explicit_slots.remove(address);
504 if overwrite {
505 self.overwrites.insert(*address);
506 } else {
507 self.overwrites.remove(address);
508 }
509 }
510
511 pub fn mark_copy(&mut self, from: &Address, to: &Address) {
513 if self.values.contains_key(from) {
514 self.copies.insert(*to, *from);
515 if let Some(slots) = self.explicit_slots.get(from).cloned() {
516 self.explicit_slots.insert(*to, slots);
517 } else {
518 self.explicit_slots.remove(to);
519 }
520 }
521 }
522
523 fn mark_explicit(&mut self, address: Address, slot: U256) {
525 if self.values.contains_key(&address) || self.copies.contains_key(&address) {
526 self.explicit_slots.entry(address).or_default().insert(slot);
527 }
528 }
529
530 fn is_explicit(&self, address: Address, slot: U256) -> bool {
532 self.explicit_slots.get(&address).is_some_and(|slots| slots.contains(&slot))
533 }
534
535 fn targets(&self) -> impl Iterator<Item = Address> + '_ {
537 self.values.keys().copied()
538 }
539
540 fn target_overwrite_modes(&self) -> impl Iterator<Item = (Address, bool)> + '_ {
543 self.values.keys().map(|address| (*address, self.overwrites.contains(address)))
544 }
545
546 fn copied_targets(&self) -> impl Iterator<Item = Address> + '_ {
548 self.copies.keys().copied()
549 }
550
551 fn copied_target_sources(&self) -> impl Iterator<Item = (Address, Address)> + '_ {
553 self.copies.iter().map(|(target, source)| (*target, *source))
554 }
555
556 fn cache_value(&mut self, address: Address, slot: U256, data: U256) {
558 if let Some(values) = self.values.get_mut(&address) {
559 values.insert(slot, data);
560 return;
561 }
562
563 let Some(source) = self.copies.get(&address).copied() else {
564 return;
565 };
566 if let Some(values) = self.values.get_mut(&source) {
567 values.insert(slot, data);
568 }
569 }
570
571 fn cached_value(&self, address: Address, slot: U256) -> Option<U256> {
573 self.values.get(&address).and_then(|values| values.get(&slot)).copied()
574 }
575
576 pub fn save<CTX: ContextTr>(
580 &mut self,
581 ecx: &mut CTX,
582 address: Address,
583 slot: U256,
584 data: U256,
585 ) {
586 self.values.get_mut(&address).expect("missing arbitrary address entry").insert(slot, data);
587 if ecx.journal_mut().load_account(address).is_ok() {
588 ecx.journal_mut()
589 .sstore(address, slot, data)
590 .expect("could not set arbitrary storage value");
591 }
592 }
593
594 pub fn copy<CTX: ContextTr>(
600 &mut self,
601 ecx: &mut CTX,
602 target: Address,
603 slot: U256,
604 new_value: U256,
605 ) -> U256 {
606 let source = self.copies.get(&target).expect("missing arbitrary copy target entry");
607 let storage_cache = self.values.get_mut(source).expect("missing arbitrary source storage");
608 let value = match storage_cache.get(&slot) {
609 Some(value) => *value,
610 None => {
611 storage_cache.insert(slot, new_value);
612 if ecx.journal_mut().load_account(*source).is_ok() {
614 ecx.journal_mut()
615 .sstore(*source, slot, new_value)
616 .expect("could not copy arbitrary storage value");
617 }
618 new_value
619 }
620 };
621 if ecx.journal_mut().load_account(target).is_ok() {
623 ecx.journal_mut().sstore(target, slot, value).expect("could not set storage");
624 }
625 value
626 }
627}
628
629pub type BroadcastableTransactions<N> = VecDeque<BroadcastableTransaction<N>>;
631
632#[derive(Clone, Copy, Debug, PartialEq, Eq)]
633enum CreatedAccountsFrameKind {
634 Call,
635 Create,
636}
637
638#[derive(Clone, Copy, Debug)]
639struct CreatedAccountsFrame {
640 kind: CreatedAccountsFrameKind,
641 depth: usize,
642 checkpoint: usize,
643}
644
645#[derive(Clone, Copy, Debug)]
646struct CreatedAccountChange {
647 fork_id: Option<LocalForkId>,
648 address: Address,
649 creation: usize,
650 previous: Option<usize>,
651 committed: bool,
652}
653
654#[derive(Clone, Debug)]
655struct CreatedAccountsSnapshot {
656 fork_id: Option<LocalForkId>,
657 bindings: AddressHashMap<usize>,
658}
659
660#[derive(Clone, Debug)]
678pub struct Cheatcodes<FEN: FoundryEvmNetwork = EthEvmNetwork> {
679 pub analysis: Option<CheatcodeAnalysis>,
681
682 pub block: Option<BlockEnvFor<FEN>>,
687
688 pub fork_block_number_override: Option<u64>,
692
693 pub active_delegations: Vec<SignedAuthorization>,
697
698 pub active_blob_sidecar: Option<BlobTransactionSidecarVariant>,
700
701 pub gas_price: Option<u128>,
706
707 pub labels: AddressHashMap<String>,
709
710 pub pranks: BTreeMap<usize, Prank>,
712
713 pub expected_revert: Option<ExpectedRevert>,
715
716 pub assume_no_revert: Option<AssumeNoRevert>,
718
719 pub fork_revert_diagnostic: Option<RevertDiagnostic>,
721
722 pub accesses: RecordAccess,
724
725 pub recording_accesses: bool,
727
728 pub recorded_account_diffs_stack: Option<Vec<Vec<AccountAccess>>>,
734
735 pending_account_diffs: Option<Arc<[AccountAccess]>>,
737
738 recorded_account_diffs_prefix: Option<Arc<[AccountAccess]>>,
740
741 created_accounts: Vec<Address>,
743
744 created_account_bindings: HashMap<(Option<LocalForkId>, Address), usize>,
746
747 created_account_changes: Vec<CreatedAccountChange>,
749
750 created_accounts_frames: Vec<CreatedAccountsFrame>,
752
753 created_accounts_snapshots: HashMap<U256, CreatedAccountsSnapshot>,
755
756 pub record_debug_steps_info: Option<RecordDebugStepInfo>,
758
759 pub recorded_logs: Option<Vec<crate::Vm::Log>>,
761
762 pub mocked_calls: HashMap<Address, BTreeMap<MockCallDataContext, VecDeque<MockCallReturnData>>>,
765
766 pub mocked_functions: HashMap<Address, HashMap<Bytes, Address>>,
768
769 pub expected_calls: ExpectedCallTracker,
771 pub expected_emits: ExpectedEmitTracker,
773 pub expected_creates: Vec<ExpectedCreate>,
775
776 pub allowed_mem_writes: HashMap<u64, Vec<Range<u64>>>,
778
779 pub broadcast: Option<Broadcast>,
781
782 pub broadcastable_transactions: BroadcastableTransactions<FEN::Network>,
784
785 pub access_list: Option<AccessList>,
787
788 pub config: Arc<CheatsConfig>,
790
791 pub test_context: TestContext,
793
794 pub fs_commit: bool,
797
798 pub serialized_jsons: BTreeMap<String, BTreeMap<String, Value>>,
801
802 pub eth_deals: Vec<DealRecord>,
804
805 pub gas_metering: GasMetering,
807
808 pub gas_snapshots: BTreeMap<String, BTreeMap<String, String>>,
811
812 pub mapping_slots: Option<AddressHashMap<MappingSlots>>,
814
815 pub pc: usize,
817 pub breakpoints: Breakpoints,
820
821 pub intercept_next_create_call: bool,
823
824 test_runner: Option<TestRunner>,
827
828 pub ignored_traces: IgnoredTraces,
830
831 pub arbitrary_storage: Option<ArbitraryStorage>,
833
834 storage_load_hooks: AddressHashMap<StorageHook>,
836 storage_store_hooks: AddressHashMap<StorageHook>,
838 mapping_storage_store_hooks: AddressHashMap<HashMap<B256, StorageHook>>,
840 storage_hook_mapping_slots: AddressHashMap<MappingSlots>,
842 pending_mapping_hash: Option<PendingMappingHash>,
844 storage_hooks_registered: bool,
846 pending_storage_hook: Option<PendingStorageHook>,
848 active_storage_hook: Option<ActiveStorageHook>,
850
851 pub deprecated: HashMap<&'static str, Option<&'static str>>,
853 pub wallets: Option<Wallets>,
855 pub private_key_signers: HashMap<U256, PrivateKeySigner>,
857 signatures_identifier: OnceLock<Option<SignaturesIdentifier>>,
859 pub dynamic_gas_limit: bool,
861 pub execution_evm_version: Option<SpecFor<FEN>>,
863
864 pub env_overrides: HashMap<Option<LocalForkId>, EnvOverrides>,
874
875 pub env_overrides_snapshots: HashMap<U256, HashMap<Option<LocalForkId>, EnvOverrides>>,
885
886 pub fork_block_number_override_snapshots: HashMap<U256, Option<u64>>,
888
889 pub context_snapshots: HashMap<U256, (ChainContextFor<FEN>, TransactionStateFor<FEN>)>,
892
893 pub in_isolation_context: bool,
903}
904
905impl Default for Cheatcodes {
909 fn default() -> Self {
910 Self::new(Arc::default())
911 }
912}
913
914impl<FEN: FoundryEvmNetwork> Cheatcodes<FEN> {
915 pub fn new(config: Arc<CheatsConfig>) -> Self {
917 Self {
918 analysis: None,
919 fs_commit: true,
920 labels: config.labels.clone(),
921 config,
922 block: Default::default(),
923 fork_block_number_override: Default::default(),
924 active_delegations: Default::default(),
925 active_blob_sidecar: Default::default(),
926 gas_price: Default::default(),
927 pranks: Default::default(),
928 expected_revert: Default::default(),
929 assume_no_revert: Default::default(),
930 fork_revert_diagnostic: Default::default(),
931 accesses: Default::default(),
932 recording_accesses: Default::default(),
933 recorded_account_diffs_stack: Default::default(),
934 pending_account_diffs: Default::default(),
935 recorded_account_diffs_prefix: Default::default(),
936 created_accounts: Default::default(),
937 created_account_bindings: Default::default(),
938 created_account_changes: Default::default(),
939 created_accounts_frames: Default::default(),
940 created_accounts_snapshots: Default::default(),
941 recorded_logs: Default::default(),
942 record_debug_steps_info: Default::default(),
943 mocked_calls: Default::default(),
944 mocked_functions: Default::default(),
945 expected_calls: Default::default(),
946 expected_emits: Default::default(),
947 expected_creates: Default::default(),
948 allowed_mem_writes: Default::default(),
949 broadcast: Default::default(),
950 broadcastable_transactions: Default::default(),
951 access_list: Default::default(),
952 test_context: Default::default(),
953 serialized_jsons: Default::default(),
954 eth_deals: Default::default(),
955 gas_metering: Default::default(),
956 gas_snapshots: Default::default(),
957 mapping_slots: Default::default(),
958 pc: Default::default(),
959 breakpoints: Default::default(),
960 intercept_next_create_call: Default::default(),
961 test_runner: Default::default(),
962 ignored_traces: Default::default(),
963 arbitrary_storage: Default::default(),
964 storage_load_hooks: Default::default(),
965 storage_store_hooks: Default::default(),
966 mapping_storage_store_hooks: Default::default(),
967 storage_hook_mapping_slots: Default::default(),
968 pending_mapping_hash: Default::default(),
969 storage_hooks_registered: Default::default(),
970 pending_storage_hook: Default::default(),
971 active_storage_hook: Default::default(),
972 deprecated: Default::default(),
973 wallets: Default::default(),
974 private_key_signers: Default::default(),
975 signatures_identifier: Default::default(),
976 dynamic_gas_limit: Default::default(),
977 execution_evm_version: None,
978 env_overrides: Default::default(),
979 env_overrides_snapshots: Default::default(),
980 fork_block_number_override_snapshots: Default::default(),
981 context_snapshots: Default::default(),
982 in_isolation_context: false,
983 }
984 }
985
986 pub fn set_analysis(&mut self, analysis: CheatcodeAnalysis) {
988 self.analysis = Some(analysis);
989 }
990
991 pub fn start_internal_state_diff_recording(&mut self) -> bool {
993 if self.recorded_account_diffs_stack.is_some()
994 || self.recorded_account_diffs_prefix.is_some()
995 {
996 return false;
997 }
998 self.recorded_account_diffs_stack = Some(Default::default());
999 true
1000 }
1001
1002 pub fn stop_internal_state_diff_recording(&mut self) -> Vec<AccountAccess> {
1004 self.recorded_account_diffs_stack.take().unwrap_or_default().into_iter().flatten().collect()
1005 }
1006
1007 pub fn set_pending_account_diffs(&mut self, accesses: Vec<AccountAccess>) {
1009 self.pending_account_diffs = (!accesses.is_empty()).then(|| Arc::from(accesses));
1010 }
1011
1012 pub fn start_state_diff_recording(&mut self) {
1014 self.recorded_account_diffs_prefix = self.pending_account_diffs.take();
1015 self.recorded_account_diffs_stack = Some(Default::default());
1016 }
1017
1018 pub fn recorded_account_diffs(&self) -> impl Iterator<Item = &AccountAccess> {
1020 self.recorded_account_diffs_prefix
1021 .iter()
1022 .flat_map(|prefix| prefix.iter())
1023 .chain(self.recorded_account_diffs_stack.iter().flatten().flatten())
1024 }
1025
1026 pub fn take_recorded_account_diffs_prefix(&mut self) -> Vec<AccountAccess> {
1028 self.recorded_account_diffs_prefix
1029 .take()
1030 .map(|prefix| prefix.as_ref().to_vec())
1031 .unwrap_or_default()
1032 }
1033
1034 pub(crate) fn created_account_bindings(
1036 &self,
1037 fork_id: Option<LocalForkId>,
1038 ) -> AddressHashMap<usize> {
1039 self.created_account_bindings
1040 .iter()
1041 .filter_map(|(&(event_fork_id, address), &creation)| {
1042 (event_fork_id == fork_id).then_some((address, creation))
1043 })
1044 .collect()
1045 }
1046
1047 pub(crate) fn created_accounts(&self, fork_id: Option<LocalForkId>) -> Vec<Address> {
1049 let bindings = self.created_account_bindings(fork_id);
1050 self.created_accounts
1051 .iter()
1052 .enumerate()
1053 .filter_map(|(index, &address)| {
1054 (bindings.get(&address) == Some(&index)).then_some(address)
1055 })
1056 .collect()
1057 }
1058
1059 pub(crate) fn record_created_account(
1061 &mut self,
1062 fork_id: Option<LocalForkId>,
1063 address: Address,
1064 ) {
1065 let creation = self.created_accounts.len();
1066 self.created_accounts.push(address);
1067 let previous = self.created_account_bindings.insert((fork_id, address), creation);
1068 self.created_account_changes.push(CreatedAccountChange {
1069 fork_id,
1070 address,
1071 creation,
1072 previous,
1073 committed: false,
1074 });
1075 }
1076
1077 pub(crate) fn commit_created_account_changes(&mut self, fork_id: Option<LocalForkId>) {
1079 for change in &mut self.created_account_changes {
1080 if change.fork_id == fork_id {
1081 change.committed = true;
1082 }
1083 }
1084 }
1085
1086 pub(crate) fn record_initial_created_accounts(
1088 &mut self,
1089 fork_id: Option<LocalForkId>,
1090 accounts: impl IntoIterator<Item = (Address, usize)>,
1091 ) {
1092 for (address, creation) in accounts {
1093 self.created_account_bindings.entry((fork_id, address)).or_insert(creation);
1094 }
1095 }
1096
1097 pub(crate) fn record_propagated_accounts(
1099 &mut self,
1100 fork_id: Option<LocalForkId>,
1101 accounts: impl IntoIterator<Item = (Address, usize)>,
1102 ) {
1103 self.created_account_bindings
1104 .extend(accounts.into_iter().map(|(address, creation)| ((fork_id, address), creation)));
1105 }
1106
1107 pub(crate) fn snapshot_created_accounts(
1109 &mut self,
1110 snapshot_id: U256,
1111 fork_id: Option<LocalForkId>,
1112 ) {
1113 let bindings = self.created_account_bindings(fork_id);
1114 self.created_accounts_snapshots
1115 .insert(snapshot_id, CreatedAccountsSnapshot { fork_id, bindings });
1116 }
1117
1118 pub(crate) fn revert_created_accounts(&mut self, snapshot_id: U256, remove: bool) {
1120 let snapshot = if remove {
1121 self.created_accounts_snapshots.remove(&snapshot_id)
1122 } else {
1123 self.created_accounts_snapshots.get(&snapshot_id).cloned()
1124 };
1125 if let Some(snapshot) = snapshot {
1126 self.created_account_bindings.retain(|(fork_id, _), _| *fork_id != snapshot.fork_id);
1127 self.created_account_bindings.extend(
1128 snapshot
1129 .bindings
1130 .into_iter()
1131 .map(|(address, creation)| ((snapshot.fork_id, address), creation)),
1132 );
1133 }
1134 }
1135
1136 pub(crate) fn delete_created_accounts_snapshot(&mut self, snapshot_id: U256) {
1138 self.created_accounts_snapshots.remove(&snapshot_id);
1139 }
1140
1141 pub(crate) fn clear_created_accounts_snapshots(&mut self) {
1143 self.created_accounts_snapshots.clear();
1144 }
1145
1146 fn start_created_accounts_frame(
1147 &mut self,
1148 reset: bool,
1149 kind: CreatedAccountsFrameKind,
1150 depth: usize,
1151 ) {
1152 if reset {
1153 self.created_accounts.clear();
1154 self.created_account_bindings.clear();
1155 self.created_account_changes.clear();
1156 self.created_accounts_frames.clear();
1157 for snapshot in self.created_accounts_snapshots.values_mut() {
1159 snapshot.bindings.clear();
1160 }
1161 }
1162 self.created_accounts_frames.push(CreatedAccountsFrame {
1163 kind,
1164 depth,
1165 checkpoint: self.created_account_changes.len(),
1166 });
1167 }
1168
1169 fn finish_created_accounts_frame(
1170 &mut self,
1171 success: bool,
1172 kind: CreatedAccountsFrameKind,
1173 depth: usize,
1174 ) {
1175 let Some(frame) = self
1176 .created_accounts_frames
1177 .last()
1178 .copied()
1179 .filter(|frame| frame.kind == kind && frame.depth == depth)
1180 else {
1181 return;
1182 };
1183 let checkpoint = frame.checkpoint;
1184 self.created_accounts_frames.pop();
1185 if !success {
1186 while self.created_account_changes.len() > checkpoint {
1187 let change = self.created_account_changes.pop().expect("length checked");
1188 if change.committed {
1189 continue;
1190 }
1191 let key = (change.fork_id, change.address);
1192 if self.created_account_bindings.get(&key) != Some(&change.creation) {
1193 continue;
1194 }
1195 if let Some(previous) = change.previous {
1196 self.created_account_bindings.insert(key, previous);
1197 } else {
1198 self.created_account_bindings.remove(&key);
1199 }
1200 }
1201 }
1202 }
1203
1204 pub fn env_overrides_for(&self, fork_id: Option<U256>) -> Option<&EnvOverrides> {
1206 self.env_overrides.get(&fork_id).filter(|o| o.is_any_set())
1207 }
1208
1209 pub fn env_overrides_for_mut(&mut self, fork_id: Option<U256>) -> &mut EnvOverrides {
1212 self.env_overrides.entry(fork_id).or_default()
1213 }
1214
1215 pub fn get_prank(&self, depth: usize) -> Option<&Prank> {
1219 self.pranks.range(..=depth).last().map(|(_, prank)| prank)
1220 }
1221
1222 pub fn wallets(&mut self) -> &Wallets {
1224 self.wallets.get_or_insert_with(|| Wallets::new(MultiWallet::default(), None))
1225 }
1226
1227 pub fn set_wallets(&mut self, wallets: Wallets) {
1229 self.wallets = Some(wallets);
1230 }
1231
1232 pub fn add_delegation(&mut self, authorization: SignedAuthorization) {
1234 self.active_delegations.push(authorization);
1235 }
1236
1237 pub fn signatures_identifier(&self) -> Option<&SignaturesIdentifier> {
1239 self.signatures_identifier
1240 .get_or_init(|| {
1241 if let Some(artifacts) = &self.config.available_artifacts {
1242 return SignaturesIdentifier::new_offline_with_abis(
1243 artifacts.values().map(|contract| &contract.abi),
1244 )
1245 .ok();
1246 }
1247 SignaturesIdentifier::new(true).ok()
1248 })
1249 .as_ref()
1250 }
1251
1252 fn apply_cheatcode(
1254 &mut self,
1255 ecx: &mut FoundryContextFor<'_, FEN>,
1256 call: &CallInputs,
1257 executor: &mut dyn CheatcodesExecutor<FEN>,
1258 ) -> Result {
1259 let decoded = Vm::VmCalls::abi_decode(&call.input.bytes(ecx)).map_err(|e| {
1261 if let alloy_sol_types::Error::UnknownSelector { name: _, selector } = e {
1262 let msg = format!(
1263 "unknown cheatcode with selector {selector}; \
1264 you may have a mismatch between the `Vm` interface (likely in `forge-std`) \
1265 and the `forge` version"
1266 );
1267 return alloy_sol_types::Error::Other(std::borrow::Cow::Owned(msg));
1268 }
1269 e
1270 })?;
1271
1272 let caller = call.caller;
1273
1274 ecx.db_mut().ensure_cheatcode_access_forking_mode(&caller)?;
1277
1278 apply_dispatch(
1279 &decoded,
1280 &mut CheatsCtxt { state: self, ecx, gas_limit: call.gas_limit, caller },
1281 executor,
1282 )
1283 }
1284
1285 #[cfg(feature = "monad")]
1287 fn apply_monad_cheatcode(
1288 &mut self,
1289 ecx: &mut FoundryContextFor<'_, FEN>,
1290 call: &CallInputs,
1291 ) -> Result {
1292 let input = call.input.bytes(ecx);
1293 let caller = call.caller;
1294
1295 ecx.db_mut().ensure_cheatcode_access_forking_mode(&caller)?;
1298
1299 apply_monad_cheatcode_call(
1300 &mut CheatsCtxt { state: self, ecx, gas_limit: call.gas_limit, caller },
1301 &input,
1302 )
1303 }
1304
1305 fn allow_cheatcodes_on_create(
1311 &self,
1312 ecx: &mut FoundryContextFor<FEN>,
1313 caller: Address,
1314 created_address: Address,
1315 ) {
1316 if ecx.journal().depth() <= 1 || ecx.db().has_cheatcode_access(&caller) {
1317 ecx.db_mut().allow_cheatcode_access(created_address);
1318 }
1319 }
1320
1321 fn apply_accesslist(&mut self, ecx: &mut FoundryContextFor<FEN>) {
1327 if let Some(access_list) = &self.access_list {
1328 ecx.tx_mut().set_access_list(access_list.clone());
1329
1330 if ecx.tx().tx_type() == TransactionType::Legacy as u8 {
1331 ecx.tx_mut().set_tx_type(TransactionType::Eip2930 as u8);
1332 }
1333 }
1334 }
1335
1336 pub fn on_revert(&mut self, ecx: &mut FoundryContextFor<FEN>) {
1341 trace!(deals=?self.eth_deals.len(), "rolling back deals");
1342
1343 if self.expected_revert.is_some() {
1345 return;
1346 }
1347
1348 if ecx.journal().depth() > 0 {
1350 return;
1351 }
1352
1353 while let Some(record) = self.eth_deals.pop() {
1357 if let Some(acc) = ecx.journal_mut().evm_state_mut().get_mut(&record.address) {
1358 acc.info.balance = record.old_balance;
1359 }
1360 }
1361 }
1362
1363 pub fn call_with_executor(
1364 &mut self,
1365 ecx: &mut FoundryContextFor<'_, FEN>,
1366 call: &mut CallInputs,
1367 executor: &mut dyn CheatcodesExecutor<FEN>,
1368 ) -> Option<CallOutcome> {
1369 if let Some(spec_id) = self.execution_evm_version {
1371 ecx.set_spec_and_gas_params(spec_id);
1372 }
1373
1374 let gas = Gas::new(call.gas_limit);
1375 let curr_depth = ecx.journal().depth();
1376 self.start_created_accounts_frame(
1377 curr_depth == 0,
1378 CreatedAccountsFrameKind::Call,
1379 curr_depth,
1380 );
1381
1382 if curr_depth == 0 {
1386 let sender = ecx.tx().caller();
1387 let account = match super::evm::journaled_account(ecx, sender) {
1388 Ok(account) => account,
1389 Err(err) => {
1390 return Some(CallOutcome {
1391 result: InterpreterResult {
1392 result: InstructionResult::Revert,
1393 output: err.abi_encode().into(),
1394 gas,
1395 },
1396 memory_offset: call.return_memory_offset.clone(),
1397 was_precompile_called: false,
1398 precompile_call_logs: vec![],
1399 charged_new_account_state_gas: call.charged_new_account_state_gas,
1400 });
1401 }
1402 };
1403 let prev = account.info.nonce;
1404 account.info.nonce = prev.saturating_sub(1);
1405
1406 trace!(target: "cheatcodes", %sender, nonce=account.info.nonce, prev, "corrected nonce");
1407 }
1408
1409 if call.target_address == CHEATCODE_ADDRESS {
1410 return match self.apply_cheatcode(ecx, call, executor) {
1411 Ok(retdata) => Some(CallOutcome {
1412 result: InterpreterResult {
1413 result: InstructionResult::Return,
1414 output: retdata.into(),
1415 gas,
1416 },
1417 memory_offset: call.return_memory_offset.clone(),
1418 was_precompile_called: true,
1419 precompile_call_logs: vec![],
1420 charged_new_account_state_gas: call.charged_new_account_state_gas,
1421 }),
1422 Err(err) => Some(CallOutcome {
1423 result: InterpreterResult {
1424 result: InstructionResult::Revert,
1425 output: err.abi_encode().into(),
1426 gas,
1427 },
1428 memory_offset: call.return_memory_offset.clone(),
1429 was_precompile_called: false,
1430 precompile_call_logs: vec![],
1431 charged_new_account_state_gas: call.charged_new_account_state_gas,
1432 }),
1433 };
1434 }
1435
1436 #[cfg(feature = "monad")]
1437 if is_monad_cheatcode_call::<FEN>(call.target_address) {
1438 let checkpoint = ecx.journal_mut().checkpoint();
1439 return match self.apply_monad_cheatcode(ecx, call) {
1440 Ok(retdata) => {
1441 ecx.journal_mut().checkpoint_commit();
1442 Some(CallOutcome {
1443 result: InterpreterResult {
1444 result: InstructionResult::Return,
1445 output: retdata.into(),
1446 gas,
1447 },
1448 memory_offset: call.return_memory_offset.clone(),
1449 was_precompile_called: true,
1450 precompile_call_logs: vec![],
1451 charged_new_account_state_gas: call.charged_new_account_state_gas,
1452 })
1453 }
1454 Err(err) => {
1455 ecx.journal_mut().checkpoint_revert(checkpoint);
1456 Some(CallOutcome {
1457 result: InterpreterResult {
1458 result: InstructionResult::Revert,
1459 output: err.abi_encode().into(),
1460 gas,
1461 },
1462 memory_offset: call.return_memory_offset.clone(),
1463 was_precompile_called: false,
1464 precompile_call_logs: vec![],
1465 charged_new_account_state_gas: call.charged_new_account_state_gas,
1466 })
1467 }
1468 };
1469 }
1470
1471 if call.target_address == HARDHAT_CONSOLE_ADDRESS {
1472 return None;
1473 }
1474
1475 if let Some(expected) = &mut self.expected_revert {
1479 expected.max_depth = max(curr_depth + 1, expected.max_depth);
1480 }
1481
1482 if let Some(expected_calls_for_target) = self.expected_calls.get_mut(&call.bytecode_address)
1486 {
1487 let input = call.input.as_bytes(ecx);
1488 let value = call.transfer_value();
1489
1490 for ((calldata, expected_scheme), (expected, actual_count)) in expected_calls_for_target
1492 {
1493 if calldata.len() <= input.len() &&
1496 input.get(..calldata.len()) == Some(calldata.as_ref()) &&
1498 expected.value.is_none_or(|expected_value| Some(expected_value) == value) &&
1500 expected.gas.is_none_or(|gas| gas == call.gas_limit) &&
1502 expected.min_gas.is_none_or(|min_gas| min_gas <= call.gas_limit) &&
1504 expected_scheme.is_none_or(|scheme| scheme == call.scheme)
1506 {
1507 *actual_count += 1;
1508 }
1509 }
1510 }
1511
1512 if let Some(prank) = &self.get_prank(curr_depth) {
1514 if prank.delegate_call
1516 && curr_depth == prank.depth
1517 && call.scheme == CallScheme::DelegateCall
1518 {
1519 call.target_address = prank.new_caller;
1520 call.caller = prank.new_caller;
1521 if let Some(new_origin) = prank.new_origin {
1522 ecx.tx_mut().set_caller(new_origin);
1523 }
1524 }
1525
1526 if curr_depth >= prank.depth && call.caller == prank.prank_caller {
1527 let prank_applied = if curr_depth == prank.depth {
1529 let _ = journaled_account(ecx, prank.new_caller);
1531 call.caller = prank.new_caller;
1532 true
1533 } else {
1534 false
1535 };
1536
1537 let prank_applied = if let Some(new_origin) = prank.new_origin {
1539 ecx.tx_mut().set_caller(new_origin);
1540 true
1541 } else {
1542 prank_applied
1543 };
1544
1545 if prank_applied && let Some(applied_prank) = prank.first_time_applied() {
1547 self.pranks.insert(curr_depth, applied_prank);
1548 }
1549 }
1550 }
1551
1552 if let Some(mocks) = self.mocked_calls.get_mut(&call.bytecode_address) {
1554 let input = call.input.bytes(ecx);
1555 let value = call.transfer_value();
1556 let ctx = MockCallDataContext { calldata: input.clone(), value };
1557
1558 if let Some(return_data_queue) = match mocks.get_mut(&ctx) {
1559 Some(queue) => Some(queue),
1560 None => mocks
1561 .iter_mut()
1562 .find(|(mock, _)| {
1563 input.get(..mock.calldata.len()) == Some(&mock.calldata[..])
1564 && mock.value.is_none_or(|mock_value| Some(mock_value) == value)
1565 })
1566 .map(|(_, v)| v),
1567 } && let Some(return_data) = return_data_queue.front().map(|x| x.to_owned())
1568 {
1569 if let Some(value) = call.transfer_value() {
1570 let checkpoint = ecx.journal_mut().checkpoint();
1571 match ecx.journal_mut().transfer_loaded(
1572 call.transfer_from(),
1573 call.transfer_to(),
1574 value,
1575 ) {
1576 None => {
1577 if return_data.ret_type.is_ok() {
1578 ecx.journal_mut().checkpoint_commit();
1579 } else {
1580 ecx.journal_mut().checkpoint_revert(checkpoint);
1581 }
1582 }
1583 Some(err) => {
1584 ecx.journal_mut().checkpoint_revert(checkpoint);
1585 return Some(CallOutcome {
1586 result: InterpreterResult {
1587 result: err.into(),
1588 output: Bytes::new(),
1589 gas,
1590 },
1591 memory_offset: call.return_memory_offset.clone(),
1592 was_precompile_called: false,
1593 precompile_call_logs: vec![],
1594 charged_new_account_state_gas: call.charged_new_account_state_gas,
1595 });
1596 }
1597 }
1598 }
1599
1600 if return_data_queue.len() > 1 {
1602 return_data_queue.pop_front();
1603 }
1604
1605 return Some(CallOutcome {
1606 result: InterpreterResult {
1607 result: return_data.ret_type,
1608 output: return_data.data,
1609 gas,
1610 },
1611 memory_offset: call.return_memory_offset.clone(),
1612 was_precompile_called: true,
1613 precompile_call_logs: vec![],
1614 charged_new_account_state_gas: call.charged_new_account_state_gas,
1615 });
1616 }
1617 }
1618
1619 self.apply_accesslist(ecx);
1621
1622 if let Some(broadcast) = &self.broadcast {
1624 let is_fixed_gas_limit = call.gas_limit >= 21_000 && !self.dynamic_gas_limit;
1627 self.dynamic_gas_limit = false;
1628
1629 if curr_depth == broadcast.depth && call.caller == broadcast.original_caller {
1634 ecx.tx_mut().set_caller(broadcast.new_origin);
1638
1639 call.caller = broadcast.new_origin;
1640 if !call.is_static {
1645 if let Err(err) = ecx.journal_mut().load_account(broadcast.new_origin) {
1646 return Some(CallOutcome {
1647 result: InterpreterResult {
1648 result: InstructionResult::Revert,
1649 output: Error::encode(err),
1650 gas,
1651 },
1652 memory_offset: call.return_memory_offset.clone(),
1653 was_precompile_called: false,
1654 precompile_call_logs: vec![],
1655 charged_new_account_state_gas: call.charged_new_account_state_gas,
1656 });
1657 }
1658
1659 let input = call.input.bytes(ecx);
1660 let chain_id = ecx.cfg().chain_id();
1661 let rpc = ecx.db().active_fork_url();
1662 let account =
1663 ecx.journal_mut().evm_state_mut().get_mut(&broadcast.new_origin).unwrap();
1664
1665 let mut tx_req = TransactionRequestFor::<FEN>::default()
1666 .with_from(broadcast.new_origin)
1667 .with_to(call.target_address)
1668 .with_value(call.transfer_value().unwrap_or_default())
1669 .with_input(input)
1670 .with_nonce(account.info.nonce)
1671 .with_chain_id(chain_id);
1672 if is_fixed_gas_limit {
1673 tx_req.set_gas_limit(call.gas_limit)
1674 }
1675
1676 let active_delegations = std::mem::take(&mut self.active_delegations);
1677 if let Some(blob_sidecar) = self.active_blob_sidecar.take() {
1679 if !active_delegations.is_empty() {
1681 let msg = "both delegation and blob are active; `attachBlob` and `attachDelegation` are not compatible";
1682 return Some(CallOutcome {
1683 result: InterpreterResult {
1684 result: InstructionResult::Revert,
1685 output: Error::encode(msg),
1686 gas,
1687 },
1688 memory_offset: call.return_memory_offset.clone(),
1689 was_precompile_called: false,
1690 precompile_call_logs: vec![],
1691 charged_new_account_state_gas: call.charged_new_account_state_gas,
1692 });
1693 }
1694 tx_req.set_blob_sidecar(blob_sidecar);
1695 }
1696
1697 if !active_delegations.is_empty() {
1699 for auth in &active_delegations {
1700 let Ok(authority) = auth.recover_authority() else {
1701 continue;
1702 };
1703 if authority == broadcast.new_origin {
1704 account.info.nonce += 1;
1707 }
1708 }
1709 tx_req.set_authorization_list(active_delegations);
1710 }
1711 if let Some(fee_token) = self.config.fee_token {
1712 tx_req.set_fee_token(fee_token);
1713 }
1714 self.broadcastable_transactions.push_back(BroadcastableTransaction {
1715 rpc,
1716 transaction: TransactionMaybeSigned::new(tx_req),
1717 });
1718 debug!(target: "cheatcodes", tx=?self.broadcastable_transactions.back().unwrap(), "broadcastable call");
1719
1720 if !self.config.evm_opts.isolate {
1722 let prev = account.info.nonce;
1723 account.info.nonce += 1;
1724 debug!(target: "cheatcodes", address=%broadcast.new_origin, nonce=prev+1, prev, "incremented nonce");
1725 }
1726 } else if broadcast.single_call {
1727 let msg = "`staticcall`s are not allowed after `broadcast`; use `startBroadcast` instead";
1728 return Some(CallOutcome {
1729 result: InterpreterResult {
1730 result: InstructionResult::Revert,
1731 output: Error::encode(msg),
1732 gas,
1733 },
1734 memory_offset: call.return_memory_offset.clone(),
1735 was_precompile_called: false,
1736 precompile_call_logs: vec![],
1737 charged_new_account_state_gas: call.charged_new_account_state_gas,
1738 });
1739 }
1740 }
1741 }
1742
1743 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1745 let (initialized, old_balance, old_nonce) =
1748 if let Ok(acc) = ecx.journal_mut().load_account(call.target_address) {
1749 (acc.data.info.exists(), acc.data.info.balance, acc.data.info.nonce)
1750 } else {
1751 (false, U256::ZERO, 0)
1752 };
1753
1754 let kind = match call.scheme {
1755 CallScheme::Call => crate::Vm::AccountAccessKind::Call,
1756 CallScheme::CallCode => crate::Vm::AccountAccessKind::CallCode,
1757 CallScheme::DelegateCall => crate::Vm::AccountAccessKind::DelegateCall,
1758 CallScheme::StaticCall => crate::Vm::AccountAccessKind::StaticCall,
1759 };
1760
1761 recorded_account_diffs_stack.push(vec![AccountAccess {
1767 chainInfo: crate::Vm::ChainInfo {
1768 forkId: ecx.db().active_fork_id().unwrap_or_default(),
1769 chainId: U256::from(ecx.cfg().chain_id()),
1770 },
1771 accessor: call.caller,
1772 account: call.bytecode_address,
1773 kind,
1774 initialized,
1775 oldBalance: old_balance,
1776 newBalance: U256::ZERO, oldNonce: old_nonce,
1778 newNonce: 0, value: call.call_value(),
1780 data: call.input.bytes(ecx),
1781 reverted: false,
1782 deployedCode: Bytes::new(),
1783 storageAccesses: vec![], depth: ecx.journal().depth().try_into().expect("journaled state depth exceeds u64"),
1785 }]);
1786 }
1787
1788 None
1789 }
1790
1791 pub fn rng(&mut self) -> &mut impl Rng {
1792 self.test_runner().rng()
1793 }
1794
1795 pub fn test_runner(&mut self) -> &mut TestRunner {
1796 self.test_runner.get_or_insert_with(|| match self.config.seed {
1797 Some(seed) => TestRunner::new_with_rng(
1798 proptest::test_runner::Config::default(),
1799 TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>()),
1800 ),
1801 None => TestRunner::new(proptest::test_runner::Config::default()),
1802 })
1803 }
1804
1805 pub fn set_seed(&mut self, seed: U256) {
1806 self.test_runner = Some(TestRunner::new_with_rng(
1807 proptest::test_runner::Config::default(),
1808 TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>()),
1809 ));
1810 }
1811
1812 pub fn arbitrary_storage(&mut self) -> &mut ArbitraryStorage {
1815 self.arbitrary_storage.get_or_insert_with(ArbitraryStorage::default)
1816 }
1817
1818 pub fn arbitrary_storage_targets(&self) -> impl Iterator<Item = Address> + '_ {
1820 self.arbitrary_storage.as_ref().into_iter().flat_map(ArbitraryStorage::targets)
1821 }
1822
1823 pub fn arbitrary_storage_target_overwrite_modes(
1826 &self,
1827 ) -> impl Iterator<Item = (Address, bool)> + '_ {
1828 self.arbitrary_storage
1829 .as_ref()
1830 .into_iter()
1831 .flat_map(ArbitraryStorage::target_overwrite_modes)
1832 }
1833
1834 pub fn arbitrary_storage_copied_targets(&self) -> impl Iterator<Item = Address> + '_ {
1836 self.arbitrary_storage.as_ref().into_iter().flat_map(ArbitraryStorage::copied_targets)
1837 }
1838
1839 pub fn arbitrary_storage_copied_target_sources(
1841 &self,
1842 ) -> impl Iterator<Item = (Address, Address)> + '_ {
1843 self.arbitrary_storage
1844 .as_ref()
1845 .into_iter()
1846 .flat_map(ArbitraryStorage::copied_target_sources)
1847 }
1848
1849 pub fn cache_arbitrary_storage_value(&mut self, address: Address, slot: U256, value: U256) {
1851 if let Some(storage) = &mut self.arbitrary_storage {
1852 storage.cache_value(address, slot, value);
1853 }
1854 }
1855
1856 pub fn mark_arbitrary_storage_slot_explicit(&mut self, address: Address, slot: U256) {
1858 if let Some(storage) = &mut self.arbitrary_storage {
1859 storage.mark_explicit(address, slot);
1860 }
1861 }
1862
1863 pub fn is_arbitrary_storage_slot_explicit(&self, address: Address, slot: U256) -> bool {
1865 self.arbitrary_storage.as_ref().is_some_and(|storage| storage.is_explicit(address, slot))
1866 }
1867
1868 pub fn cached_arbitrary_storage_value(&self, address: Address, slot: U256) -> Option<U256> {
1870 self.arbitrary_storage.as_ref().and_then(|storage| storage.cached_value(address, slot))
1871 }
1872
1873 pub fn has_arbitrary_storage(&self, address: &Address) -> bool {
1875 match &self.arbitrary_storage {
1876 Some(storage) => storage.values.contains_key(address),
1877 None => false,
1878 }
1879 }
1880
1881 pub fn should_overwrite_arbitrary_storage(
1885 &self,
1886 address: &Address,
1887 storage_slot: U256,
1888 ) -> bool {
1889 match &self.arbitrary_storage {
1890 Some(storage) => {
1891 storage.overwrites.contains(address)
1892 && storage
1893 .values
1894 .get(address)
1895 .and_then(|arbitrary_values| arbitrary_values.get(&storage_slot))
1896 .is_none()
1897 }
1898 None => false,
1899 }
1900 }
1901
1902 pub fn is_arbitrary_storage_copy(&self, address: &Address) -> bool {
1904 match &self.arbitrary_storage {
1905 Some(storage) => storage.copies.contains_key(address),
1906 None => false,
1907 }
1908 }
1909
1910 pub fn register_storage_load_hook(
1912 &mut self,
1913 target: Address,
1914 callback_target: Address,
1915 callback_selector: [u8; 4],
1916 ) {
1917 self.storage_load_hooks.insert(target, StorageHook { callback_target, callback_selector });
1918 self.storage_hooks_registered = true;
1919 }
1920
1921 pub fn register_storage_store_hook(
1923 &mut self,
1924 target: Address,
1925 callback_target: Address,
1926 callback_selector: [u8; 4],
1927 ) {
1928 self.storage_store_hooks.insert(target, StorageHook { callback_target, callback_selector });
1929 self.storage_hooks_registered = true;
1930 }
1931
1932 pub fn register_mapping_storage_store_hook(
1934 &mut self,
1935 target: Address,
1936 root_slot: B256,
1937 callback_target: Address,
1938 callback_selector: [u8; 4],
1939 ) -> bool {
1940 if self.storage_store_hooks.contains_key(&target) {
1941 return false;
1942 }
1943 self.storage_hook_mapping_slots.remove(&target);
1944 self.mapping_storage_store_hooks
1945 .entry(target)
1946 .or_default()
1947 .insert(root_slot, StorageHook { callback_target, callback_selector });
1948 self.storage_hooks_registered = true;
1949 true
1950 }
1951
1952 pub fn mapping_storage_store_hooks(
1954 &self,
1955 ) -> impl Iterator<Item = (Address, B256, StorageHook)> + '_ {
1956 self.mapping_storage_store_hooks
1957 .iter()
1958 .flat_map(|(target, hooks)| hooks.iter().map(|(root, hook)| (*target, *root, *hook)))
1959 }
1960
1961 pub fn has_mapping_storage_store_hooks(&self, target: Address) -> bool {
1963 self.mapping_storage_store_hooks.get(&target).is_some_and(|hooks| !hooks.is_empty())
1964 }
1965
1966 pub fn storage_load_hooks(&self) -> impl Iterator<Item = (Address, StorageHook)> + '_ {
1968 self.storage_load_hooks.iter().map(|(target, hook)| (*target, *hook))
1969 }
1970
1971 pub fn storage_store_hooks(&self) -> impl Iterator<Item = (Address, StorageHook)> + '_ {
1973 self.storage_store_hooks.iter().map(|(target, hook)| (*target, *hook))
1974 }
1975
1976 #[inline]
1978 pub const fn has_storage_hooks(&self) -> bool {
1979 self.storage_hooks_registered
1980 }
1981
1982 pub fn clear_storage_hook_mapping_slots(&mut self) {
1984 self.storage_hook_mapping_slots.clear();
1985 }
1986
1987 #[inline]
1989 pub const fn is_storage_hook_active(&self) -> bool {
1990 self.active_storage_hook.is_some()
1991 }
1992
1993 pub fn is_storage_hook_callback(
1995 &self,
1996 ecx: &FoundryContextFor<'_, FEN>,
1997 call: &CallInputs,
1998 ) -> bool {
1999 self.active_storage_hook.as_ref().is_some_and(|active| {
2000 active.outcome.is_none()
2001 && ecx.journal().depth() == active.parent_depth
2002 && call.caller == CHEATCODE_ADDRESS
2003 && call.target_address == active.callback_target
2004 && call.input.bytes(ecx) == active.callback_input
2005 })
2006 }
2007
2008 fn finish_storage_hook_call(
2009 &mut self,
2010 ecx: &FoundryContextFor<'_, FEN>,
2011 call: &CallInputs,
2012 outcome: &CallOutcome,
2013 ) -> bool {
2014 let Some(active) = self.active_storage_hook.as_mut() else { return false };
2015 if active.outcome.is_some()
2016 || ecx.journal().depth() != active.parent_depth
2017 || call.caller != CHEATCODE_ADDRESS
2018 || call.target_address != active.callback_target
2019 || call.input.bytes(ecx) != active.callback_input
2020 {
2021 return false;
2022 }
2023 active.outcome = Some((outcome.result.result, outcome.result.output.clone()));
2024 true
2025 }
2026
2027 #[inline(always)]
2028 pub fn has_step_hooks(&self) -> bool {
2029 self.broadcast.is_some()
2030 || self.gas_metering.paused
2031 || self.gas_metering.reset
2032 || self.recording_accesses
2033 || self.recorded_account_diffs_stack.is_some()
2034 || !self.allowed_mem_writes.is_empty()
2035 || self.mapping_slots.is_some()
2036 || self.gas_metering.recording
2037 || self.has_active_env_overrides()
2038 || self.has_storage_hooks()
2039 }
2040
2041 #[inline(always)]
2042 pub fn has_step_end_hooks(&self) -> bool {
2043 self.gas_metering.paused
2044 || self.gas_metering.touched
2045 || self.arbitrary_storage.is_some()
2046 || self.mapping_slots.is_some()
2047 || self.has_active_env_overrides()
2048 || self.has_storage_hooks()
2049 }
2050
2051 #[inline(always)]
2052 pub fn has_log_hooks(&self) -> bool {
2053 !self.expected_emits.is_empty() || self.recorded_logs.is_some()
2054 }
2055
2056 #[inline(always)]
2057 pub fn has_recording_accesses_only_step_hook(&self) -> bool {
2058 self.recording_accesses
2059 && self.broadcast.is_none()
2060 && !self.gas_metering.paused
2061 && !self.gas_metering.reset
2062 && self.recorded_account_diffs_stack.is_none()
2063 && self.allowed_mem_writes.is_empty()
2064 && self.mapping_slots.is_none()
2065 && !self.has_storage_hooks()
2066 && !self.gas_metering.recording
2067 && !self.has_active_env_overrides()
2068 }
2069
2070 #[inline(always)]
2071 fn has_active_env_overrides(&self) -> bool {
2072 self.env_overrides.values().any(EnvOverrides::is_any_set)
2073 }
2074
2075 pub fn struct_defs(&self) -> Option<&foundry_common::fmt::StructDefinitions> {
2077 self.analysis.as_ref().and_then(|analysis| analysis.struct_defs().ok())
2078 }
2079}
2080
2081impl<FEN: FoundryEvmNetwork> Inspector<FoundryContextFor<'_, FEN>> for Cheatcodes<FEN> {
2082 fn initialize_interp(
2083 &mut self,
2084 interpreter: &mut Interpreter,
2085 ecx: &mut FoundryContextFor<'_, FEN>,
2086 ) {
2087 if let Some(block) = self.block.take() {
2090 ecx.set_block(block);
2091 }
2092 if let Some(gas_price) = self.gas_price.take() {
2093 ecx.tx_mut().set_gas_price(gas_price);
2094 }
2095
2096 if self.gas_metering.paused {
2098 self.gas_metering.paused_frames.push(interpreter.gas);
2099 }
2100
2101 if let Some(expected) = &mut self.expected_revert {
2103 expected.max_depth = max(ecx.journal().depth(), expected.max_depth);
2104 }
2105 }
2106
2107 fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut FoundryContextFor<'_, FEN>) {
2108 self.pc = interpreter.bytecode.pc();
2109
2110 if !self.has_step_hooks() {
2111 return;
2112 }
2113
2114 if self.finish_storage_hook_callback(interpreter, ecx) {
2115 return;
2116 }
2117
2118 if self.broadcast.is_some() {
2119 self.set_gas_limit_type(interpreter);
2120 }
2121
2122 if self.gas_metering.paused {
2124 self.meter_gas(interpreter);
2125 }
2126
2127 if self.gas_metering.reset {
2129 self.meter_gas_reset(interpreter);
2130 }
2131
2132 if self.recording_accesses {
2134 self.record_accesses(interpreter);
2135 }
2136
2137 if self.recorded_account_diffs_stack.is_some() {
2139 self.record_state_diffs(interpreter, ecx);
2140 }
2141
2142 if !self.allowed_mem_writes.is_empty() {
2144 self.check_mem_opcodes(
2145 interpreter,
2146 ecx.journal().depth().try_into().expect("journaled state depth exceeds u64"),
2147 );
2148 }
2149
2150 if self.mapping_slots.is_some() || !self.mapping_storage_store_hooks.is_empty() {
2151 if let Some(mapping_slots) = &mut self.mapping_slots {
2153 mapping_step(mapping_slots, interpreter);
2154 }
2155
2156 let account = interpreter.input.target_address;
2157 let mapping_hook_active = self.active_storage_hook.is_none()
2158 && self
2159 .mapping_storage_store_hooks
2160 .get(&account)
2161 .is_some_and(|hooks| !hooks.is_empty());
2162 if mapping_hook_active {
2163 mapping_step(&mut self.storage_hook_mapping_slots, interpreter);
2164 }
2165 self.pending_mapping_hash = if self.mapping_slots.is_some() || mapping_hook_active {
2166 capture_mapping_hash(interpreter)
2167 } else {
2168 None
2169 };
2170 }
2171
2172 if self.gas_metering.recording {
2174 self.meter_gas_record(interpreter, ecx);
2175 }
2176
2177 if !self.env_overrides.is_empty() {
2182 let fork_id = ecx.db().active_fork_id();
2183 if let Some(env_overrides) =
2184 self.env_overrides.get_mut(&fork_id).filter(|o| o.is_any_set())
2185 {
2186 env_overrides.pending_opcode = None;
2190 env_overrides.pending_blobhash_index = None;
2191
2192 let opcode = interpreter.bytecode.opcode();
2193 match opcode {
2194 op::BASEFEE | op::GASPRICE => {
2195 env_overrides.pending_opcode = Some(opcode);
2196 }
2197 op::BLOBHASH => {
2198 env_overrides.pending_opcode = Some(opcode);
2199 env_overrides.pending_blobhash_index =
2200 interpreter.stack.peek(0).ok().and_then(|index| index.try_into().ok());
2201 }
2202 _ => {}
2203 }
2204 }
2205 }
2206
2207 if self.active_storage_hook.is_none() {
2208 self.capture_storage_hook(interpreter, ecx);
2209 }
2210 }
2211
2212 fn step_end(&mut self, interpreter: &mut Interpreter, ecx: &mut FoundryContextFor<'_, FEN>) {
2213 if !self.has_step_end_hooks() {
2214 return;
2215 }
2216
2217 if self.gas_metering.paused {
2218 self.meter_gas_end(interpreter);
2219 }
2220
2221 if self.gas_metering.touched {
2222 self.meter_gas_check(interpreter);
2223 }
2224
2225 if self.arbitrary_storage.is_some() {
2227 self.arbitrary_storage_end(interpreter, ecx);
2228 }
2229
2230 if let Some(pending) = self.pending_mapping_hash.take()
2231 && interpreter
2232 .bytecode
2233 .action
2234 .as_ref()
2235 .and_then(InterpreterAction::instruction_result)
2236 .is_none()
2237 {
2238 if let Some(mapping_slots) = &mut self.mapping_slots {
2239 record_mapping_hash(mapping_slots, interpreter, pending);
2240 }
2241 if self
2242 .mapping_storage_store_hooks
2243 .get(&pending.address)
2244 .is_some_and(|hooks| !hooks.is_empty())
2245 && self.active_storage_hook.is_none()
2246 {
2247 record_mapping_hash(&mut self.storage_hook_mapping_slots, interpreter, pending);
2248 }
2249 }
2250
2251 if self.active_storage_hook.is_none() {
2252 self.invoke_pending_storage_hook(interpreter, ecx);
2253 }
2254
2255 if !self.env_overrides.is_empty() {
2265 let fork_id = ecx.db().active_fork_id();
2266 if self.env_overrides.get(&fork_id).is_some_and(|o| o.is_any_set()) {
2267 let opcode_failed = interpreter
2273 .bytecode
2274 .action
2275 .as_ref()
2276 .and_then(|a| a.instruction_result())
2277 .is_some();
2278 if opcode_failed {
2279 if let Some(env_overrides) = self.env_overrides.get_mut(&fork_id) {
2280 env_overrides.pending_opcode = None;
2281 env_overrides.pending_blobhash_index = None;
2282 }
2283 } else {
2284 self.apply_env_overrides(interpreter, fork_id);
2285 }
2286 }
2287 }
2288 }
2289
2290 fn log(&mut self, _ecx: &mut FoundryContextFor<'_, FEN>, log: Log) {
2291 if !self.expected_emits.is_empty()
2292 && let Some(err) = expect::handle_expect_emit(self, &log, None)
2293 {
2294 let _ = sh_err!("{err:?}");
2298 }
2299
2300 record_logs(&mut self.recorded_logs, &log);
2302 }
2303
2304 fn log_full(
2305 &mut self,
2306 interpreter: &mut Interpreter,
2307 _ecx: &mut FoundryContextFor<'_, FEN>,
2308 log: Log,
2309 ) {
2310 if !self.expected_emits.is_empty() {
2311 expect::handle_expect_emit(self, &log, Some(interpreter));
2312 }
2313
2314 record_logs(&mut self.recorded_logs, &log);
2316 }
2317
2318 fn call(
2319 &mut self,
2320 ecx: &mut FoundryContextFor<'_, FEN>,
2321 inputs: &mut CallInputs,
2322 ) -> Option<CallOutcome> {
2323 if self.is_storage_hook_callback(ecx, inputs) {
2324 return None;
2325 }
2326 Self::call_with_executor(self, ecx, inputs, &mut TransparentCheatcodesExecutor)
2327 }
2328
2329 fn call_end(
2330 &mut self,
2331 ecx: &mut FoundryContextFor<'_, FEN>,
2332 call: &CallInputs,
2333 outcome: &mut CallOutcome,
2334 ) {
2335 if self.finish_storage_hook_call(ecx, call, outcome) {
2336 return;
2337 }
2338
2339 let cheatcode_call = call.target_address == CHEATCODE_ADDRESS
2340 || call.target_address == HARDHAT_CONSOLE_ADDRESS;
2341 #[cfg(feature = "monad")]
2342 let cheatcode_call = cheatcode_call || is_monad_cheatcode_call::<FEN>(call.target_address);
2343 let curr_depth = ecx.journal().depth();
2344
2345 self.finish_created_accounts_frame(
2346 outcome.result.is_ok(),
2347 CreatedAccountsFrameKind::Call,
2348 curr_depth,
2349 );
2350
2351 if !cheatcode_call {
2355 if let Some(prank) = &self.get_prank(curr_depth)
2357 && curr_depth == prank.depth
2358 {
2359 ecx.tx_mut().set_caller(prank.prank_origin);
2360
2361 if prank.single_call {
2363 self.pranks.remove(&curr_depth);
2364 }
2365 }
2366
2367 if let Some(broadcast) = &self.broadcast
2369 && curr_depth == broadcast.depth
2370 {
2371 ecx.tx_mut().set_caller(broadcast.original_origin);
2372
2373 if broadcast.single_call {
2375 let _ = self.broadcast.take();
2376 }
2377 }
2378 }
2379
2380 if let Some(assume_no_revert) = &mut self.assume_no_revert {
2382 if outcome.result.is_revert() && assume_no_revert.reverted_by.is_none() {
2385 assume_no_revert.reverted_by = Some(call.target_address);
2386 }
2387
2388 let curr_depth = ecx.journal().depth();
2390 if curr_depth <= assume_no_revert.depth && !cheatcode_call {
2391 if outcome.result.is_revert() {
2394 let assume_no_revert = std::mem::take(&mut self.assume_no_revert).unwrap();
2395 return match revert_handlers::handle_assume_no_revert(
2396 &assume_no_revert,
2397 outcome.result.result,
2398 &outcome.result.output,
2399 &self.config.available_artifacts,
2400 ) {
2401 Ok(_) => {
2404 outcome.result.output = Error::from(MAGIC_ASSUME).abi_encode().into();
2405 }
2406 Err(error) => {
2409 trace!(expected=?assume_no_revert, ?error, status=?outcome.result.result, "Expected revert mismatch");
2410 outcome.result.result = InstructionResult::Revert;
2411 outcome.result.output = error.abi_encode().into();
2412 }
2413 };
2414 }
2415 self.assume_no_revert = None;
2417 }
2418 }
2419
2420 if let Some(expected_revert) = &mut self.expected_revert {
2422 let call_failed = !matches!(outcome.result.result, return_ok!());
2425 if call_failed {
2426 if expected_revert.reverter.is_some()
2430 && (expected_revert.reverted_by.is_none() || expected_revert.count > 1)
2431 {
2432 expected_revert.reverted_by = Some(call.target_address);
2433 }
2434 }
2435
2436 let curr_depth = ecx.journal().depth();
2437 if curr_depth <= expected_revert.depth {
2438 let internal = self.config.internal_expect_revert;
2443 let went_deeper = expected_revert.max_depth > expected_revert.depth;
2444 let needs_processing = match expected_revert.kind {
2445 ExpectedRevertKind::Default => (|| {
2446 if cheatcode_call {
2448 return false;
2449 }
2450 if call_failed {
2452 return true;
2453 }
2454 if !internal && went_deeper {
2456 return true;
2457 }
2458 if curr_depth == 0 {
2460 return true;
2461 }
2462 !internal
2465 })(),
2466 ExpectedRevertKind::Cheatcode { pending_processing } => {
2469 cheatcode_call && !pending_processing
2470 }
2471 };
2472
2473 if needs_processing {
2474 let mut expected_revert = std::mem::take(&mut self.expected_revert).unwrap();
2475 let clear_last_frame_gas =
2476 matches!(expected_revert.kind, ExpectedRevertKind::Default);
2477 return match revert_handlers::handle_expect_revert(
2478 cheatcode_call,
2479 false,
2480 self.config.internal_expect_revert,
2481 &expected_revert,
2482 outcome.result.result,
2483 outcome.result.output.clone(),
2484 &self.config.available_artifacts,
2485 ) {
2486 Err(error) => {
2487 trace!(expected=?expected_revert, ?error, status=?outcome.result.result, "Expected revert mismatch");
2488 outcome.result.result = InstructionResult::Revert;
2489 outcome.result.output = error.abi_encode().into();
2490 }
2491 Ok((_, retdata)) => {
2492 expected_revert.actual_count += 1;
2493 if expected_revert.actual_count < expected_revert.count {
2494 self.expected_revert = Some(expected_revert);
2495 }
2496 if clear_last_frame_gas {
2497 self.gas_metering.last_frame_gas = None;
2498 }
2499 outcome.result.result = InstructionResult::Return;
2500 outcome.result.output = retdata;
2501 }
2502 };
2503 }
2504
2505 if let ExpectedRevertKind::Cheatcode { pending_processing } =
2508 &mut self.expected_revert.as_mut().unwrap().kind
2509 {
2510 *pending_processing = false;
2511 }
2512 }
2513 }
2514
2515 if cheatcode_call {
2518 return;
2519 }
2520
2521 let gas = outcome.result.gas;
2524 let frame_gas = crate::Vm::Gas {
2525 gasLimit: gas.limit(),
2526 gasTotalUsed: gas.total_gas_spent(),
2527 gasMemoryUsed: 0,
2528 gasRefunded: gas.refunded(),
2529 gasRemaining: gas.remaining(),
2530 };
2531 self.gas_metering.last_call_gas = Some(frame_gas.clone());
2532 self.gas_metering.last_frame_gas = Some(frame_gas);
2533
2534 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
2537 if ecx.journal().depth() > 0
2539 && let Some(mut last_recorded_depth) = recorded_account_diffs_stack.pop()
2540 {
2541 if outcome.result.is_revert() {
2544 for element in &mut *last_recorded_depth {
2545 element.reverted = true;
2546 for storage_access in &mut element.storageAccesses {
2547 storage_access.reverted = true;
2548 }
2549 }
2550 }
2551
2552 if let Some(call_access) = last_recorded_depth.first_mut() {
2553 let curr_depth = ecx.journal().depth();
2558 if call_access.depth == curr_depth as u64
2559 && let Ok(acc) = ecx.journal_mut().load_account(call.target_address)
2560 {
2561 debug_assert!(access_is_call(call_access.kind));
2562 call_access.newBalance = acc.data.info.balance;
2563 call_access.newNonce = acc.data.info.nonce;
2564 }
2565 if let Some(last) = recorded_account_diffs_stack.last_mut() {
2570 last.extend(last_recorded_depth);
2571 } else {
2572 recorded_account_diffs_stack.push(last_recorded_depth);
2573 }
2574 }
2575 }
2576 }
2577
2578 let diag = self.fork_revert_diagnostic.take();
2581
2582 if outcome.result.is_revert() {
2585 if let Some(err) = diag {
2588 outcome.result.output = Error::encode(err.to_error_msg(&self.labels));
2589 }
2590 return;
2591 }
2592
2593 let should_check_emits = self
2605 .expected_emits
2606 .iter()
2607 .any(|(expected, _)| {
2608 let curr_depth = ecx.journal().depth();
2609 expected.depth == curr_depth
2610 }) &&
2611 !call.is_static;
2613 if should_check_emits {
2614 let expected_counts = self
2615 .expected_emits
2616 .iter()
2617 .filter_map(|(expected, count_map)| {
2618 let count = match expected.address {
2619 Some(emitter) => match count_map.get(&emitter) {
2620 Some(log_count) => expected
2621 .log
2622 .as_ref()
2623 .map(|l| log_count.count(l))
2624 .unwrap_or_else(|| log_count.count_unchecked()),
2625 None => 0,
2626 },
2627 None => match &expected.log {
2628 Some(log) => count_map.values().map(|logs| logs.count(log)).sum(),
2629 None => count_map.values().map(|logs| logs.count_unchecked()).sum(),
2630 },
2631 };
2632
2633 (count != expected.count).then_some((expected, count))
2634 })
2635 .collect::<Vec<_>>();
2636
2637 if let Some((expected, _)) = self
2639 .expected_emits
2640 .iter()
2641 .find(|(expected, _)| !expected.found && expected.count > 0)
2642 {
2643 outcome.result.result = InstructionResult::Revert;
2644 let mismatch_error = expected.mismatch_error.clone();
2645 let expected_log = expected.log.clone();
2646 let checks = expected.checks;
2647 let anonymous = expected.anonymous;
2648 let error_msg = mismatch_error
2649 .as_ref()
2650 .map(|mismatch| {
2651 mismatch.to_error_msg(self, checks, expected_log.as_ref(), anonymous)
2652 })
2653 .unwrap_or_else(|| "log != expected log".to_string());
2654 outcome.result.output = error_msg.abi_encode().into();
2655 return;
2656 }
2657
2658 if !expected_counts.is_empty() {
2659 let msg = if outcome.result.is_ok() {
2660 let (expected, count) = expected_counts.first().unwrap();
2661 format!("log emitted {count} times, expected {}", expected.count)
2662 } else {
2663 "expected an emit, but the call reverted instead. \
2664 ensure you're testing the happy path when using `expectEmit`"
2665 .to_string()
2666 };
2667
2668 outcome.result.result = InstructionResult::Revert;
2669 outcome.result.output = Error::encode(msg);
2670 return;
2671 }
2672
2673 self.expected_emits.clear()
2677 }
2678
2679 if let TxKind::Call(test_contract) = ecx.tx().kind() {
2682 if ecx.db().is_forked_mode()
2685 && outcome.result.result == InstructionResult::Stop
2686 && call.target_address != test_contract
2687 {
2688 self.fork_revert_diagnostic =
2689 ecx.db().diagnose_revert(call.target_address, ecx.journal().evm_state());
2690 }
2691 }
2692
2693 if ecx.journal().depth() == 0 {
2695 if outcome.result.is_revert() {
2699 return;
2700 }
2701
2702 for (address, calldatas) in &self.expected_calls {
2707 for ((calldata, scheme), (expected, actual_count)) in calldatas {
2709 let ExpectedCallData { gas, min_gas, value, count, call_type } = expected;
2711
2712 let failed = match call_type {
2713 ExpectedCallType::Count => *count != *actual_count,
2717 ExpectedCallType::NonCount => *count > *actual_count,
2722 };
2723 if failed {
2724 let expected_values = [
2725 Some(format!("data {}", hex::encode_prefixed(calldata))),
2726 value.as_ref().map(|v| format!("value {v}")),
2727 gas.map(|g| format!("gas {g}")),
2728 min_gas.map(|g| format!("minimum gas {g}")),
2729 scheme.map(|scheme| format!("call type {scheme:?}")),
2730 ]
2731 .into_iter()
2732 .flatten()
2733 .join(", ");
2734 let but = if outcome.result.is_ok() {
2735 let s = if *actual_count == 1 { "" } else { "s" };
2736 format!("was called {actual_count} time{s}")
2737 } else {
2738 "the call reverted instead; \
2739 ensure you're testing the happy path when using `expectCall`"
2740 .to_string()
2741 };
2742 let s = if *count == 1 { "" } else { "s" };
2743 let msg = format!(
2744 "expected call to {address} with {expected_values} \
2745 to be called {count} time{s}, but {but}"
2746 );
2747 outcome.result.result = InstructionResult::Revert;
2748 outcome.result.output = Error::encode(msg);
2749
2750 return;
2751 }
2752 }
2753 }
2754
2755 for (expected, _) in &mut self.expected_emits {
2759 if expected.count == 0 && !expected.found {
2760 expected.found = true;
2761 }
2762 }
2763 self.expected_emits.retain(|(expected, _)| !expected.found);
2764 if !self.expected_emits.is_empty() {
2766 let msg = if outcome.result.is_ok() {
2767 "expected an emit, but no logs were emitted afterwards. \
2768 you might have mismatched events or not enough events were emitted"
2769 } else {
2770 "expected an emit, but the call reverted instead. \
2771 ensure you're testing the happy path when using `expectEmit`"
2772 };
2773 outcome.result.result = InstructionResult::Revert;
2774 outcome.result.output = Error::encode(msg);
2775 return;
2776 }
2777
2778 if let Some(expected_create) = self.expected_creates.first() {
2780 let msg = format!(
2781 "expected {} call by address {} for bytecode {} but not found",
2782 expected_create.create_scheme,
2783 hex::encode_prefixed(expected_create.deployer),
2784 hex::encode_prefixed(&expected_create.bytecode),
2785 );
2786 outcome.result.result = InstructionResult::Revert;
2787 outcome.result.output = Error::encode(msg);
2788 }
2789 }
2790 }
2791
2792 fn create(
2793 &mut self,
2794 ecx: &mut FoundryContextFor<'_, FEN>,
2795 mut input: &mut CreateInputs,
2796 ) -> Option<CreateOutcome> {
2797 if let Some(spec_id) = self.execution_evm_version {
2799 ecx.set_spec_and_gas_params(spec_id);
2800 }
2801
2802 let gas = Gas::new(input.gas_limit());
2803 let curr_depth = ecx.journal().depth();
2804 self.start_created_accounts_frame(
2805 curr_depth == 0,
2806 CreatedAccountsFrameKind::Create,
2807 curr_depth,
2808 );
2809
2810 if self.intercept_next_create_call {
2812 self.intercept_next_create_call = false;
2814
2815 let output = input.init_code();
2817
2818 return Some(CreateOutcome {
2820 result: InterpreterResult { result: InstructionResult::Revert, output, gas },
2821 address: None,
2822 charged_create_state_gas: input.charged_create_state_gas(),
2823 });
2824 }
2825
2826 if let Some(prank) = &self.get_prank(curr_depth)
2828 && curr_depth >= prank.depth
2829 && input.caller() == prank.prank_caller
2830 {
2831 let prank_applied = if curr_depth == prank.depth {
2833 let _ = journaled_account(ecx, prank.new_caller);
2835 input.set_caller(prank.new_caller);
2836 true
2837 } else {
2838 false
2839 };
2840
2841 let prank_applied = if let Some(new_origin) = prank.new_origin {
2843 ecx.tx_mut().set_caller(new_origin);
2844 true
2845 } else {
2846 prank_applied
2847 };
2848
2849 if prank_applied && let Some(applied_prank) = prank.first_time_applied() {
2851 self.pranks.insert(curr_depth, applied_prank);
2852 }
2853 }
2854
2855 self.apply_accesslist(ecx);
2857
2858 if let Some(broadcast) = &mut self.broadcast
2860 && curr_depth >= broadcast.depth
2861 && input.caller() == broadcast.original_caller
2862 {
2863 if let Err(err) = ecx.journal_mut().load_account(broadcast.new_origin) {
2864 return Some(CreateOutcome {
2865 result: InterpreterResult {
2866 result: InstructionResult::Revert,
2867 output: Error::encode(err),
2868 gas,
2869 },
2870 address: None,
2871 charged_create_state_gas: input.charged_create_state_gas(),
2872 });
2873 }
2874
2875 ecx.tx_mut().set_caller(broadcast.new_origin);
2876
2877 if curr_depth == broadcast.depth || broadcast.deploy_from_code {
2878 broadcast.deploy_from_code = false;
2880
2881 input.set_caller(broadcast.new_origin);
2882
2883 let rpc = ecx.db().active_fork_url();
2884 let account = &ecx.journal().evm_state()[&broadcast.new_origin];
2885 let mut tx_req = TransactionRequestFor::<FEN>::default()
2886 .with_from(broadcast.new_origin)
2887 .with_kind(TxKind::Create)
2888 .with_value(input.value())
2889 .with_input(input.init_code())
2890 .with_nonce(account.info.nonce);
2891 if let Some(fee_token) = self.config.fee_token {
2892 tx_req.set_fee_token(fee_token);
2893 }
2894 self.broadcastable_transactions.push_back(BroadcastableTransaction {
2895 rpc,
2896 transaction: TransactionMaybeSigned::new(tx_req),
2897 });
2898
2899 input.log_debug(self, &input.scheme().unwrap_or(CreateScheme::Create));
2900 }
2901 }
2902
2903 let address = input.allow_cheatcodes(self, ecx);
2905
2906 self.record_created_account(ecx.db().active_fork_id(), address);
2907
2908 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
2910 recorded_account_diffs_stack.push(vec![AccountAccess {
2911 chainInfo: crate::Vm::ChainInfo {
2912 forkId: ecx.db().active_fork_id().unwrap_or_default(),
2913 chainId: U256::from(ecx.cfg().chain_id()),
2914 },
2915 accessor: input.caller(),
2916 account: address,
2917 kind: crate::Vm::AccountAccessKind::Create,
2918 initialized: true,
2919 oldBalance: U256::ZERO, newBalance: U256::ZERO, oldNonce: 0, newNonce: 1, value: input.value(),
2924 data: input.init_code(),
2925 reverted: false,
2926 deployedCode: Bytes::new(), storageAccesses: vec![], depth: curr_depth as u64,
2929 }]);
2930 }
2931
2932 None
2933 }
2934
2935 fn create_end(
2936 &mut self,
2937 ecx: &mut FoundryContextFor<'_, FEN>,
2938 call: &CreateInputs,
2939 outcome: &mut CreateOutcome,
2940 ) {
2941 let call = Some(call);
2942 let curr_depth = ecx.journal().depth();
2943
2944 self.finish_created_accounts_frame(
2945 outcome.result.is_ok(),
2946 CreatedAccountsFrameKind::Create,
2947 curr_depth,
2948 );
2949
2950 if let Some(prank) = &self.get_prank(curr_depth)
2952 && curr_depth == prank.depth
2953 {
2954 ecx.tx_mut().set_caller(prank.prank_origin);
2955
2956 if prank.single_call {
2958 std::mem::take(&mut self.pranks);
2959 }
2960 }
2961
2962 if let Some(broadcast) = &self.broadcast
2964 && curr_depth == broadcast.depth
2965 {
2966 ecx.tx_mut().set_caller(broadcast.original_origin);
2967
2968 if broadcast.single_call {
2970 std::mem::take(&mut self.broadcast);
2971 }
2972 }
2973
2974 if let Some(expected_revert) = &mut self.expected_revert {
2976 if outcome.result.is_revert()
2988 && expected_revert.reverter.is_some()
2989 && expected_revert.reverted_by.is_none()
2990 && let Some(addr) = outcome.address
2991 {
2992 expected_revert.reverted_by = Some(addr);
2993 }
2994
2995 if curr_depth <= expected_revert.depth
2996 && matches!(expected_revert.kind, ExpectedRevertKind::Default)
2997 {
2998 let mut expected_revert = std::mem::take(&mut self.expected_revert).unwrap();
2999 return match revert_handlers::handle_expect_revert(
3000 false,
3001 true,
3002 self.config.internal_expect_revert,
3003 &expected_revert,
3004 outcome.result.result,
3005 outcome.result.output.clone(),
3006 &self.config.available_artifacts,
3007 ) {
3008 Ok((address, retdata)) => {
3009 expected_revert.actual_count += 1;
3010 if expected_revert.actual_count < expected_revert.count {
3011 expected_revert.reverted_by = None;
3013 self.expected_revert = Some(expected_revert.clone());
3014 }
3015
3016 outcome.result.result = InstructionResult::Return;
3017 outcome.result.output = retdata;
3018 outcome.address = address;
3019 self.gas_metering.last_frame_gas = None;
3020 }
3021 Err(err) => {
3022 outcome.result.result = InstructionResult::Revert;
3023 outcome.result.output = err.abi_encode().into();
3024 }
3025 };
3026 }
3027 }
3028
3029 if curr_depth > 0 {
3030 let gas = outcome.result.gas;
3033 self.gas_metering.last_frame_gas = Some(crate::Vm::Gas {
3034 gasLimit: gas.limit(),
3035 gasTotalUsed: gas.total_gas_spent(),
3036 gasMemoryUsed: 0,
3037 gasRefunded: gas.refunded(),
3038 gasRemaining: gas.remaining(),
3039 });
3040 }
3041
3042 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack
3045 && let Some(mut last_depth) = recorded_account_diffs_stack.pop()
3046 {
3047 if outcome.result.is_revert() {
3050 for element in &mut *last_depth {
3051 element.reverted = true;
3052 for storage_access in &mut element.storageAccesses {
3053 storage_access.reverted = true;
3054 }
3055 }
3056 }
3057
3058 if let Some(create_access) = last_depth.first_mut() {
3059 if create_access.depth == curr_depth as u64 {
3061 debug_assert_eq!(
3062 create_access.kind as u8,
3063 crate::Vm::AccountAccessKind::Create as u8
3064 );
3065 if let Some(address) = outcome.address
3066 && let Ok(created_acc) = ecx.journal_mut().load_account(address)
3067 {
3068 create_access.newBalance = created_acc.data.info.balance;
3069 create_access.newNonce = created_acc.data.info.nonce;
3070 create_access.deployedCode =
3071 created_acc.data.info.code.clone().unwrap_or_default().original_bytes();
3072 }
3073 }
3074 }
3075 if let Some(last) = recorded_account_diffs_stack.last_mut() {
3080 last.append(&mut last_depth);
3081 } else {
3082 recorded_account_diffs_stack.push(last_depth);
3083 }
3084 }
3085
3086 if !self.expected_creates.is_empty()
3088 && let (Some(address), Some(call)) = (outcome.address, call)
3089 && let Ok(created_acc) = ecx.journal_mut().load_account(address)
3090 {
3091 let bytecode = created_acc.data.info.code.clone().unwrap_or_default().original_bytes();
3092 if let Some((index, _)) =
3093 self.expected_creates.iter().find_position(|expected_create| {
3094 expected_create.deployer == call.caller()
3095 && expected_create.create_scheme.eq(call.scheme().into())
3096 && expected_create.bytecode == bytecode
3097 })
3098 {
3099 self.expected_creates.swap_remove(index);
3100 }
3101 }
3102 }
3103}
3104
3105impl<FEN: FoundryEvmNetwork> InspectorExt for Cheatcodes<FEN> {
3106 fn should_use_create2_factory(&mut self, depth: usize, inputs: &CreateInputs) -> bool {
3107 let target_depth = if let Some(prank) = &self.get_prank(depth) {
3108 prank.depth
3109 } else if let Some(broadcast) = &self.broadcast {
3110 broadcast.depth
3111 } else {
3112 1
3113 };
3114
3115 if depth != target_depth {
3116 return false;
3117 }
3118
3119 match inputs.scheme() {
3120 CreateScheme::Create2 { .. } => {
3121 self.broadcast.is_some() || self.config.always_use_create_2_factory
3122 }
3123 CreateScheme::Create => self.config.batch_rewrite_creates && self.broadcast.is_some(),
3124 _ => false,
3125 }
3126 }
3127
3128 fn create2_deployer(&self) -> Address {
3129 self.config.evm_opts.create2_deployer
3130 }
3131}
3132
3133impl<FEN: FoundryEvmNetwork> Cheatcodes<FEN> {
3134 #[cold]
3135 fn meter_gas(&mut self, interpreter: &mut Interpreter) {
3136 if let Some(paused_gas) = self.gas_metering.paused_frames.last() {
3137 let memory = *interpreter.gas.memory();
3140 interpreter.gas = *paused_gas;
3141 interpreter.gas.memory_mut().words_num = memory.words_num;
3142 interpreter.gas.memory_mut().expansion_cost = memory.expansion_cost;
3143 } else {
3144 self.gas_metering.paused_frames.push(interpreter.gas);
3146 }
3147 }
3148
3149 #[cold]
3150 fn meter_gas_record(
3151 &mut self,
3152 interpreter: &mut Interpreter,
3153 ecx: &mut FoundryContextFor<'_, FEN>,
3154 ) {
3155 if interpreter.bytecode.action.as_ref().and_then(|i| i.instruction_result()).is_none() {
3156 self.gas_metering.gas_records.iter_mut().for_each(|record| {
3157 let curr_depth = ecx.journal().depth();
3158 if curr_depth == record.depth {
3159 if self.gas_metering.last_gas_used != 0 {
3162 let gas_diff = interpreter
3163 .gas
3164 .total_gas_spent()
3165 .saturating_sub(self.gas_metering.last_gas_used);
3166 record.gas_used = record.gas_used.saturating_add(gas_diff);
3167 }
3168
3169 self.gas_metering.last_gas_used = interpreter.gas.total_gas_spent();
3172 }
3173 });
3174 }
3175 }
3176
3177 #[cold]
3178 fn meter_gas_end(&mut self, interpreter: &mut Interpreter) {
3179 if let Some(interpreter_action) = interpreter.bytecode.action.as_ref()
3181 && will_exit(interpreter_action)
3182 {
3183 self.gas_metering.paused_frames.pop();
3184 }
3185 }
3186
3187 #[cold]
3188 const fn meter_gas_reset(&mut self, interpreter: &mut Interpreter) {
3189 let mut gas = Gas::new(interpreter.gas.limit());
3190 gas.memory_mut().words_num = interpreter.gas.memory().words_num;
3191 gas.memory_mut().expansion_cost = interpreter.gas.memory().expansion_cost;
3192 interpreter.gas = gas;
3193 self.gas_metering.reset = false;
3194 }
3195
3196 #[cold]
3197 fn meter_gas_check(&mut self, interpreter: &mut Interpreter) {
3198 if let Some(interpreter_action) = interpreter.bytecode.action.as_ref()
3199 && will_exit(interpreter_action)
3200 {
3201 if interpreter.gas.total_gas_spent()
3205 < u64::try_from(interpreter.gas.refunded()).unwrap_or_default()
3206 {
3207 interpreter.gas = Gas::new(interpreter.gas.limit());
3208 }
3209 }
3210 }
3211
3212 #[cold]
3226 fn apply_env_overrides(&mut self, interpreter: &mut Interpreter, fork_id: Option<U256>) {
3227 let Some(env_overrides) = self.env_overrides.get_mut(&fork_id) else { return };
3228 let Some(opcode) = env_overrides.pending_opcode.take() else { return };
3229 match opcode {
3230 op::BASEFEE => {
3231 if let Some(basefee) = env_overrides.basefee {
3232 Self::replace_top_of_stack(interpreter, U256::from(basefee));
3234 }
3235 }
3236 op::GASPRICE => {
3237 if let Some(gas_price) = env_overrides.gas_price {
3238 Self::replace_top_of_stack(interpreter, U256::from(gas_price));
3240 }
3241 }
3242 op::BLOBHASH => {
3243 let blob_hashes = env_overrides.blob_hashes.clone();
3244 let blobhash_index = env_overrides.pending_blobhash_index.take();
3245 if let Some(ref blob_hashes) = blob_hashes
3246 && let Some(index) = blobhash_index
3247 {
3248 let hash = blob_hashes.get(index as usize).copied().unwrap_or_default();
3251 Self::replace_top_of_stack(interpreter, hash.into());
3252 }
3253 }
3254 _ => {}
3255 }
3256 }
3257
3258 fn replace_top_of_stack(interpreter: &mut Interpreter, value: U256) {
3266 if interpreter.stack.pop().is_err() {
3267 debug_assert!(false, "env override expected opcode result on stack");
3268 return;
3269 }
3270 let _ = interpreter.stack.push(value);
3271 }
3272
3273 #[cold]
3281 fn arbitrary_storage_end(
3282 &mut self,
3283 interpreter: &mut Interpreter,
3284 ecx: &mut FoundryContextFor<'_, FEN>,
3285 ) {
3286 let (key, target_address) = if interpreter.bytecode.opcode() == op::SLOAD {
3287 (try_or_return!(interpreter.stack.peek(0)), interpreter.input.target_address)
3288 } else {
3289 return;
3290 };
3291
3292 if self.is_arbitrary_storage_slot_explicit(target_address, key) {
3293 return;
3294 }
3295
3296 let Some(value) = ecx.sload(target_address, key) else {
3297 return;
3298 };
3299
3300 if (value.is_cold && value.data.is_zero())
3301 || self.should_overwrite_arbitrary_storage(&target_address, key)
3302 {
3303 if self.has_arbitrary_storage(&target_address) {
3304 let arbitrary_value = self
3305 .cached_arbitrary_storage_value(target_address, key)
3306 .unwrap_or_else(|| self.rng().random());
3307 self.arbitrary_storage.as_mut().unwrap().save(
3308 ecx,
3309 target_address,
3310 key,
3311 arbitrary_value,
3312 );
3313 } else if self.is_arbitrary_storage_copy(&target_address) {
3314 let arbitrary_value = self.rng().random();
3315 self.arbitrary_storage.as_mut().unwrap().copy(
3316 ecx,
3317 target_address,
3318 key,
3319 arbitrary_value,
3320 );
3321 }
3322 }
3323 }
3324
3325 #[inline]
3329 pub fn finish_storage_hook_callback(
3330 &mut self,
3331 interpreter: &mut Interpreter,
3332 ecx: &mut FoundryContextFor<'_, FEN>,
3333 ) -> bool {
3334 let Some(active) = self.active_storage_hook.as_ref() else { return false };
3335 let Some((result, output)) = active.outcome.clone() else { return false };
3336
3337 let active = self.active_storage_hook.take().expect("active storage hook exists");
3338 Self::restore_storage_hook_access(ecx, active.journal_start);
3339 self.restore_storage_hook_inspector_state(active.inspector_state);
3340 let _ = interpreter.stack.pop();
3341 if let Some(item) = active.saved_stack_item {
3342 let result = interpreter.stack.push(item);
3343 debug_assert!(result, "reserved storage-hook stack slot must be available");
3344 }
3345 interpreter.gas = active.saved_gas;
3346 interpreter.return_data.set_buffer(active.saved_return_data);
3347
3348 if result.is_ok() {
3349 false
3350 } else {
3351 interpreter.bytecode.set_action(InterpreterAction::new_return(
3352 InstructionResult::Revert,
3353 output,
3354 interpreter.gas,
3355 ));
3356 true
3357 }
3358 }
3359
3360 fn take_storage_hook_inspector_state(&mut self) -> StorageHookInspectorState {
3361 StorageHookInspectorState {
3362 accesses: std::mem::take(&mut self.accesses),
3363 recording_accesses: std::mem::replace(&mut self.recording_accesses, false),
3364 mapping_slots: self.mapping_slots.take(),
3365 recorded_logs: self.recorded_logs.take(),
3366 mocked_calls: std::mem::take(&mut self.mocked_calls),
3367 mocked_functions: std::mem::take(&mut self.mocked_functions),
3368 expected_revert: self.expected_revert.take(),
3369 assume_no_revert: self.assume_no_revert.take(),
3370 expected_calls: std::mem::take(&mut self.expected_calls),
3371 expected_emits: std::mem::take(&mut self.expected_emits),
3372 expected_creates: std::mem::take(&mut self.expected_creates),
3373 }
3374 }
3375
3376 fn restore_storage_hook_inspector_state(&mut self, state: StorageHookInspectorState) {
3377 self.accesses = state.accesses;
3378 self.recording_accesses = state.recording_accesses;
3379 self.mapping_slots = state.mapping_slots;
3380 self.recorded_logs = state.recorded_logs;
3381 self.mocked_calls = state.mocked_calls;
3382 self.mocked_functions = state.mocked_functions;
3383 self.expected_revert = state.expected_revert;
3384 self.assume_no_revert = state.assume_no_revert;
3385 self.expected_calls = state.expected_calls;
3386 self.expected_emits = state.expected_emits;
3387 self.expected_creates = state.expected_creates;
3388 }
3389
3390 fn restore_storage_hook_access(ecx: &mut FoundryContextFor<'_, FEN>, journal_start: usize) {
3391 let (_, journal) = ecx.db_journal_inner_mut();
3392 let entries =
3393 journal.journal.drain(journal_start.min(journal.journal.len())..).collect_vec();
3394 for entry in entries {
3395 match entry {
3396 JournalEntry::AccountWarmed { address } => {
3397 journal.state.get_mut(&address).expect("warmed account exists").mark_cold();
3398 }
3399 JournalEntry::StorageWarmed { address, key } => {
3400 journal
3404 .state
3405 .get_mut(&address)
3406 .expect("warmed account exists")
3407 .storage
3408 .get_mut(&key)
3409 .expect("warmed storage slot exists")
3410 .mark_cold();
3411 }
3412 entry => journal.journal.push(entry),
3413 }
3414 }
3415 }
3416
3417 fn capture_storage_hook(
3418 &mut self,
3419 interpreter: &Interpreter,
3420 ecx: &mut FoundryContextFor<'_, FEN>,
3421 ) {
3422 self.pending_storage_hook = None;
3423 if self.active_storage_hook.is_some() {
3424 return;
3425 }
3426 let account = interpreter.input.target_address;
3427 match interpreter.bytecode.opcode() {
3428 op::SLOAD => {
3429 let slot = try_or_return!(interpreter.stack.peek(0));
3430 let Some(hook) = self.storage_load_hooks.get(&account).copied() else { return };
3431 self.pending_storage_hook = Some(PendingStorageHook::Load { account, slot, hook });
3432 }
3433 op::SSTORE => {
3434 let slot = try_or_return!(interpreter.stack.peek(0));
3435 let (hook, mapping) = if let Some(hook) = self.storage_store_hooks.get(&account) {
3436 (*hook, None)
3437 } else {
3438 let Some(provenance) = self
3439 .storage_hook_mapping_slots
3440 .get(&account)
3441 .and_then(|slots| slots.resolve(slot.into()))
3442 else {
3443 return;
3444 };
3445 let Some(hook) = self
3446 .mapping_storage_store_hooks
3447 .get(&account)
3448 .and_then(|hooks| hooks.get(&provenance.root_slot))
3449 .copied()
3450 else {
3451 return;
3452 };
3453 (hook, Some((provenance.root_slot, provenance.keys)))
3454 };
3455 let checkpoint = ecx.journal_mut().checkpoint();
3456 let old_value =
3457 ecx.sload(account, slot).map(|value| value.data).unwrap_or_default();
3458 ecx.journal_mut().checkpoint_revert(checkpoint);
3459 self.pending_storage_hook =
3460 Some(PendingStorageHook::Store { account, slot, old_value, mapping, hook });
3461 }
3462 _ => {}
3463 }
3464 }
3465
3466 fn invoke_pending_storage_hook(
3467 &mut self,
3468 interpreter: &mut Interpreter,
3469 ecx: &mut FoundryContextFor<'_, FEN>,
3470 ) {
3471 let Some(pending) = self.pending_storage_hook.take() else { return };
3472 if interpreter
3473 .bytecode
3474 .action
3475 .as_ref()
3476 .and_then(InterpreterAction::instruction_result)
3477 .is_some()
3478 {
3479 return;
3480 }
3481
3482 let (hook, input, saved_stack_item) = match pending {
3483 PendingStorageHook::Load { account, slot, hook } => {
3484 let value = try_or_return!(interpreter.stack.peek(0));
3485 let mut input = Vec::with_capacity(4 + 32 * 3);
3486 input.extend_from_slice(&hook.callback_selector);
3487 input.extend_from_slice(account.into_word().as_slice());
3488 input.extend_from_slice(&slot.to_be_bytes::<32>());
3489 input.extend_from_slice(&value.to_be_bytes::<32>());
3490 (hook, Bytes::from(input), Some(value))
3491 }
3492 PendingStorageHook::Store { account, slot, old_value, mapping, hook } => {
3493 let new_value =
3494 ecx.sload(account, slot).map(|value| value.data).unwrap_or_default();
3495 let mut input = Vec::with_capacity(4 + 32 * 4);
3496 input.extend_from_slice(&hook.callback_selector);
3497 input.extend_from_slice(account.into_word().as_slice());
3498 input.extend_from_slice(&slot.to_be_bytes::<32>());
3499 if let Some((root, keys)) = mapping {
3500 input.extend_from_slice(root.as_slice());
3501 input.extend_from_slice(&U256::from(32 * 6).to_be_bytes::<32>());
3502 input.extend_from_slice(&old_value.to_be_bytes::<32>());
3503 input.extend_from_slice(&new_value.to_be_bytes::<32>());
3504 input.extend_from_slice(&U256::from(keys.len()).to_be_bytes::<32>());
3505 for key in keys {
3506 input.extend_from_slice(key.as_slice());
3507 }
3508 } else {
3509 input.extend_from_slice(&old_value.to_be_bytes::<32>());
3510 input.extend_from_slice(&new_value.to_be_bytes::<32>());
3511 }
3512 (hook, Bytes::from(input), None)
3513 }
3514 };
3515
3516 let journal_start = ecx.db_journal_inner_mut().1.journal.len();
3517 let account = match ecx.journal_mut().load_account_with_code(hook.callback_target) {
3518 Ok(account) => account,
3519 Err(err) => {
3520 interpreter.bytecode.set_action(InterpreterAction::new_return(
3521 InstructionResult::Revert,
3522 Error::encode(err),
3523 interpreter.gas,
3524 ));
3525 return;
3526 }
3527 };
3528 let known_bytecode =
3529 (account.info.code_hash, account.info.code.clone().unwrap_or_default());
3530 let saved_gas = interpreter.gas;
3531 let saved_return_data = Bytes::copy_from_slice(interpreter.return_data.buffer());
3532 let gas_limit = interpreter.gas.remaining();
3533 let parent_depth = ecx.journal().depth();
3534 if saved_stack_item.is_some() {
3535 let result = interpreter.stack.pop();
3536 debug_assert!(result.is_ok(), "captured SLOAD result must be on the stack");
3537 }
3538 let inspector_state = self.take_storage_hook_inspector_state();
3539
3540 self.active_storage_hook = Some(ActiveStorageHook {
3541 parent_depth,
3542 callback_target: hook.callback_target,
3543 callback_input: input.clone(),
3544 saved_gas,
3545 saved_return_data,
3546 saved_stack_item,
3547 journal_start,
3548 inspector_state,
3549 outcome: None,
3550 });
3551 interpreter.bytecode.set_action(InterpreterAction::NewFrame(FrameInput::Call(Box::new(
3552 CallInputs {
3553 input: CallInput::Bytes(input),
3554 return_memory_offset: 0..0,
3555 gas_limit,
3556 reservoir: 0,
3557 bytecode_address: hook.callback_target,
3558 known_bytecode,
3559 target_address: hook.callback_target,
3560 caller: CHEATCODE_ADDRESS,
3561 value: CallValue::Transfer(U256::ZERO),
3562 scheme: CallScheme::Call,
3563 is_static: false,
3564 charged_new_account_state_gas: false,
3565 },
3566 ))));
3567 }
3568
3569 #[cold]
3571 fn record_accesses(&mut self, interpreter: &mut Interpreter) {
3572 let access = &mut self.accesses;
3573 match interpreter.bytecode.opcode() {
3574 op::SLOAD => {
3575 let key = try_or_return!(interpreter.stack.peek(0));
3576 access.record_read(interpreter.input.target_address, key);
3577 }
3578 op::SSTORE => {
3579 let key = try_or_return!(interpreter.stack.peek(0));
3580 access.record_write(interpreter.input.target_address, key);
3581 }
3582 _ => {}
3583 }
3584 }
3585
3586 #[cold]
3587 fn record_state_diffs(
3588 &mut self,
3589 interpreter: &mut Interpreter,
3590 ecx: &mut FoundryContextFor<'_, FEN>,
3591 ) {
3592 let Some(account_accesses) = &mut self.recorded_account_diffs_stack else { return };
3593 match interpreter.bytecode.opcode() {
3594 op::SELFDESTRUCT => {
3595 let Some(last) = account_accesses.last_mut() else { return };
3597
3598 let target = try_or_return!(interpreter.stack.peek(0));
3600 let target = Address::from_word(B256::from(target));
3601 let (initialized, old_balance, old_nonce) = ecx
3602 .journal_mut()
3603 .load_account(target)
3604 .map(|account| {
3605 (
3606 account.data.info.exists(),
3607 account.data.info.balance,
3608 account.data.info.nonce,
3609 )
3610 })
3611 .unwrap_or_default();
3612
3613 let value = ecx
3615 .balance(interpreter.input.target_address)
3616 .map(|b| b.data)
3617 .unwrap_or(U256::ZERO);
3618
3619 last.push(crate::Vm::AccountAccess {
3621 chainInfo: crate::Vm::ChainInfo {
3622 forkId: ecx.db().active_fork_id().unwrap_or_default(),
3623 chainId: U256::from(ecx.cfg().chain_id()),
3624 },
3625 accessor: interpreter.input.target_address,
3626 account: target,
3627 kind: crate::Vm::AccountAccessKind::SelfDestruct,
3628 initialized,
3629 oldBalance: old_balance,
3630 newBalance: old_balance + value,
3631 oldNonce: old_nonce,
3632 newNonce: old_nonce, value,
3634 data: Bytes::new(),
3635 reverted: false,
3636 deployedCode: Bytes::new(),
3637 storageAccesses: vec![],
3638 depth: ecx
3639 .journal()
3640 .depth()
3641 .try_into()
3642 .expect("journaled state depth exceeds u64"),
3643 });
3644 }
3645
3646 op::SLOAD => {
3647 let Some(last) = account_accesses.last_mut() else { return };
3648
3649 let key = try_or_return!(interpreter.stack.peek(0));
3650 let address = interpreter.input.target_address;
3651
3652 let checkpoint = ecx.journal_mut().checkpoint();
3656 let present_value =
3657 ecx.sload(address, key).map(|previous| previous.data).unwrap_or_default();
3658 ecx.journal_mut().checkpoint_revert(checkpoint);
3659 let access = crate::Vm::StorageAccess {
3660 account: interpreter.input.target_address,
3661 slot: key.into(),
3662 isWrite: false,
3663 previousValue: present_value.into(),
3664 newValue: present_value.into(),
3665 reverted: false,
3666 };
3667 let curr_depth =
3668 ecx.journal().depth().try_into().expect("journaled state depth exceeds u64");
3669 append_storage_access(last, access, curr_depth);
3670 }
3671 op::SSTORE => {
3672 let Some(last) = account_accesses.last_mut() else { return };
3673
3674 let key = try_or_return!(interpreter.stack.peek(0));
3675 let value = try_or_return!(interpreter.stack.peek(1));
3676 let address = interpreter.input.target_address;
3677 let checkpoint = ecx.journal_mut().checkpoint();
3681 let previous_value =
3682 ecx.sload(address, key).map(|previous| previous.data).unwrap_or_default();
3683 ecx.journal_mut().checkpoint_revert(checkpoint);
3684
3685 let access = crate::Vm::StorageAccess {
3686 account: address,
3687 slot: key.into(),
3688 isWrite: true,
3689 previousValue: previous_value.into(),
3690 newValue: value.into(),
3691 reverted: false,
3692 };
3693 let curr_depth =
3694 ecx.journal().depth().try_into().expect("journaled state depth exceeds u64");
3695 append_storage_access(last, access, curr_depth);
3696 }
3697
3698 op::EXTCODECOPY | op::EXTCODESIZE | op::EXTCODEHASH | op::BALANCE => {
3700 let kind = match interpreter.bytecode.opcode() {
3701 op::EXTCODECOPY => crate::Vm::AccountAccessKind::Extcodecopy,
3702 op::EXTCODESIZE => crate::Vm::AccountAccessKind::Extcodesize,
3703 op::EXTCODEHASH => crate::Vm::AccountAccessKind::Extcodehash,
3704 op::BALANCE => crate::Vm::AccountAccessKind::Balance,
3705 _ => unreachable!(),
3706 };
3707 let address =
3708 Address::from_word(B256::from(try_or_return!(interpreter.stack.peek(0))));
3709 let checkpoint = ecx.journal_mut().checkpoint();
3710 let (initialized, balance, nonce) = ecx
3711 .journal_mut()
3712 .load_account(address)
3713 .map(|acc| (acc.data.info.exists(), acc.data.info.balance, acc.data.info.nonce))
3714 .unwrap_or_default();
3715 ecx.journal_mut().checkpoint_revert(checkpoint);
3716 let curr_depth =
3717 ecx.journal().depth().try_into().expect("journaled state depth exceeds u64");
3718 let account_access = crate::Vm::AccountAccess {
3719 chainInfo: crate::Vm::ChainInfo {
3720 forkId: ecx.db().active_fork_id().unwrap_or_default(),
3721 chainId: U256::from(ecx.cfg().chain_id()),
3722 },
3723 accessor: interpreter.input.target_address,
3724 account: address,
3725 kind,
3726 initialized,
3727 oldBalance: balance,
3728 newBalance: balance,
3729 oldNonce: nonce,
3730 newNonce: nonce, value: U256::ZERO,
3732 data: Bytes::new(),
3733 reverted: false,
3734 deployedCode: Bytes::new(),
3735 storageAccesses: vec![],
3736 depth: curr_depth,
3737 };
3738 if let Some(last) = account_accesses.last_mut() {
3741 last.push(account_access);
3742 } else {
3743 account_accesses.push(vec![account_access]);
3744 }
3745 }
3746 _ => {}
3747 }
3748 }
3749
3750 #[cold]
3755 fn check_mem_opcodes(&self, interpreter: &mut Interpreter, depth: u64) {
3756 let Some(ranges) = self.allowed_mem_writes.get(&depth) else {
3757 return;
3758 };
3759
3760 macro_rules! mem_opcode_match {
3769 ($(($opcode:ident, $offset_depth:expr, $size_depth:expr, $writes:expr)),* $(,)?) => {
3770 match interpreter.bytecode.opcode() {
3771 op::MSTORE => {
3776 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
3778
3779 if !ranges.iter().any(|range| {
3782 range.contains(&offset) && range.contains(&(offset + 31))
3783 }) {
3784 let value = try_or_return!(interpreter.stack.peek(1)).to_be_bytes::<32>();
3789 if value[..SELECTOR_LEN] == stopExpectSafeMemoryCall::SELECTOR {
3790 return
3791 }
3792
3793 disallowed_mem_write(offset, 32, interpreter, ranges);
3794 return
3795 }
3796 }
3797 op::MSTORE8 => {
3798 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
3800
3801 if !ranges.iter().any(|range| range.contains(&offset)) {
3804 disallowed_mem_write(offset, 1, interpreter, ranges);
3805 return
3806 }
3807 }
3808
3809 op::MLOAD => {
3814 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
3816
3817 if offset >= interpreter.memory.size() as u64 && !ranges.iter().any(|range| {
3821 range.contains(&offset) && range.contains(&(offset + 31))
3822 }) {
3823 disallowed_mem_write(offset, 32, interpreter, ranges);
3824 return
3825 }
3826 }
3827
3828 op::CALL => {
3833 let dest_offset = try_or_return!(interpreter.stack.peek(5)).saturating_to::<u64>();
3835
3836 let size = try_or_return!(interpreter.stack.peek(6)).saturating_to::<u64>();
3838
3839 let fail_cond = !ranges.iter().any(|range| {
3843 range.contains(&dest_offset) &&
3844 range.contains(&(dest_offset + size.saturating_sub(1)))
3845 });
3846
3847 if fail_cond {
3850 let to = Address::from_word(try_or_return!(interpreter.stack.peek(1)).to_be_bytes::<32>().into());
3854 if to == CHEATCODE_ADDRESS {
3855 let args_offset = try_or_return!(interpreter.stack.peek(3)).saturating_to::<usize>();
3856 let args_size = try_or_return!(interpreter.stack.peek(4)).saturating_to::<usize>();
3857 let memory_word = interpreter.memory.slice_len(args_offset, args_size);
3858 if memory_word[..SELECTOR_LEN] == stopExpectSafeMemoryCall::SELECTOR {
3859 return
3860 }
3861 }
3862
3863 disallowed_mem_write(dest_offset, size, interpreter, ranges);
3864 return
3865 }
3866 }
3867
3868 $(op::$opcode => {
3869 let dest_offset = try_or_return!(interpreter.stack.peek($offset_depth)).saturating_to::<u64>();
3871
3872 let size = try_or_return!(interpreter.stack.peek($size_depth)).saturating_to::<u64>();
3874
3875 let fail_cond = !ranges.iter().any(|range| {
3879 range.contains(&dest_offset) &&
3880 range.contains(&(dest_offset + size.saturating_sub(1)))
3881 }) && ($writes ||
3882 [dest_offset, (dest_offset + size).saturating_sub(1)].into_iter().any(|offset| {
3883 offset >= interpreter.memory.size() as u64
3884 })
3885 );
3886
3887 if fail_cond {
3890 disallowed_mem_write(dest_offset, size, interpreter, ranges);
3891 return
3892 }
3893 })*
3894
3895 _ => {}
3896 }
3897 }
3898 }
3899
3900 mem_opcode_match!(
3903 (CALLDATACOPY, 0, 2, true),
3904 (CODECOPY, 0, 2, true),
3905 (RETURNDATACOPY, 0, 2, true),
3906 (EXTCODECOPY, 1, 3, true),
3907 (CALLCODE, 5, 6, true),
3908 (STATICCALL, 4, 5, true),
3909 (DELEGATECALL, 4, 5, true),
3910 (KECCAK256, 0, 1, false),
3911 (LOG0, 0, 1, false),
3912 (LOG1, 0, 1, false),
3913 (LOG2, 0, 1, false),
3914 (LOG3, 0, 1, false),
3915 (LOG4, 0, 1, false),
3916 (CREATE, 1, 2, false),
3917 (CREATE2, 1, 2, false),
3918 (RETURN, 0, 1, false),
3919 (REVERT, 0, 1, false),
3920 );
3921 }
3922
3923 #[cold]
3924 fn set_gas_limit_type(&mut self, interpreter: &mut Interpreter) {
3925 match interpreter.bytecode.opcode() {
3926 op::CREATE2 => self.dynamic_gas_limit = true,
3927 op::CALL => {
3928 self.dynamic_gas_limit =
3931 try_or_return!(interpreter.stack.peek(0)) >= interpreter.gas.remaining() - 100
3932 }
3933 _ => self.dynamic_gas_limit = false,
3934 }
3935 }
3936}
3937
3938fn disallowed_mem_write(
3944 dest_offset: u64,
3945 size: u64,
3946 interpreter: &mut Interpreter,
3947 ranges: &[Range<u64>],
3948) {
3949 let revert_string = format!(
3950 "memory write at offset 0x{:02X} of size 0x{:02X} not allowed; safe range: {}",
3951 dest_offset,
3952 size,
3953 ranges.iter().map(|r| format!("[0x{:02X}, 0x{:02X})", r.start, r.end)).join(" U ")
3954 );
3955
3956 interpreter.bytecode.set_action(InterpreterAction::new_return(
3957 InstructionResult::Revert,
3958 Bytes::from(revert_string.into_bytes()),
3959 interpreter.gas,
3960 ));
3961}
3962
3963const fn access_is_call(kind: crate::Vm::AccountAccessKind) -> bool {
3965 matches!(
3966 kind,
3967 crate::Vm::AccountAccessKind::Call
3968 | crate::Vm::AccountAccessKind::StaticCall
3969 | crate::Vm::AccountAccessKind::CallCode
3970 | crate::Vm::AccountAccessKind::DelegateCall
3971 )
3972}
3973
3974fn record_logs(recorded_logs: &mut Option<Vec<Vm::Log>>, log: &Log) {
3976 if let Some(storage_recorded_logs) = recorded_logs {
3977 storage_recorded_logs.push(Vm::Log {
3978 topics: log.data.topics().to_vec(),
3979 data: log.data.data.clone(),
3980 emitter: log.address,
3981 });
3982 }
3983}
3984
3985fn append_storage_access(
3987 last: &mut Vec<AccountAccess>,
3988 storage_access: crate::Vm::StorageAccess,
3989 storage_depth: u64,
3990) {
3991 if !last.is_empty() && last.first().unwrap().depth < storage_depth {
3993 if last.len() == 1 {
3999 last.first_mut().unwrap().storageAccesses.push(storage_access);
4000 } else {
4001 let last_record = last.last_mut().unwrap();
4002 if last_record.kind as u8 == crate::Vm::AccountAccessKind::Resume as u8 {
4003 last_record.storageAccesses.push(storage_access);
4004 } else {
4005 let entry = last.first().unwrap();
4006 let resume_record = crate::Vm::AccountAccess {
4007 chainInfo: crate::Vm::ChainInfo {
4008 forkId: entry.chainInfo.forkId,
4009 chainId: entry.chainInfo.chainId,
4010 },
4011 accessor: entry.accessor,
4012 account: entry.account,
4013 kind: crate::Vm::AccountAccessKind::Resume,
4014 initialized: entry.initialized,
4015 storageAccesses: vec![storage_access],
4016 reverted: entry.reverted,
4017 oldBalance: U256::ZERO,
4019 newBalance: U256::ZERO,
4020 oldNonce: 0,
4021 newNonce: 0,
4022 value: U256::ZERO,
4023 data: Bytes::new(),
4024 deployedCode: Bytes::new(),
4025 depth: entry.depth,
4026 };
4027 last.push(resume_record);
4028 }
4029 }
4030 }
4031}
4032
4033const fn cheatcode_of<T: spec::CheatcodeDef>(_: &T) -> &'static spec::Cheatcode<'static> {
4035 T::CHEATCODE
4036}
4037
4038fn cheatcode_name(cheat: &spec::Cheatcode<'static>) -> &'static str {
4039 cheat.func.signature.split('(').next().unwrap()
4040}
4041
4042const fn cheatcode_id(cheat: &spec::Cheatcode<'static>) -> &'static str {
4043 cheat.func.id
4044}
4045
4046const fn cheatcode_signature(cheat: &spec::Cheatcode<'static>) -> &'static str {
4047 cheat.func.signature
4048}
4049
4050fn apply_dispatch<FEN: FoundryEvmNetwork>(
4052 calls: &Vm::VmCalls,
4053 ccx: &mut CheatsCtxt<'_, '_, FEN>,
4054 executor: &mut dyn CheatcodesExecutor<FEN>,
4055) -> Result {
4056 macro_rules! get_cheatcode {
4058 ($($variant:ident),*) => {
4059 match calls {
4060 $(Vm::VmCalls::$variant(cheat) => cheatcode_of(cheat),)*
4061 }
4062 };
4063 }
4064 let cheat = vm_calls!(get_cheatcode);
4065
4066 let _guard = debug_span!(target: "cheatcodes", "apply", id = %cheatcode_id(cheat)).entered();
4067 trace!(target: "cheatcodes", cheat = %cheatcode_signature(cheat), "applying");
4068
4069 if let spec::Status::Deprecated(replacement) = cheat.status {
4070 ccx.state.deprecated.insert(cheatcode_signature(cheat), replacement);
4071 }
4072
4073 macro_rules! dispatch {
4075 ($($variant:ident),*) => {
4076 match calls {
4077 $(Vm::VmCalls::$variant(cheat) => Cheatcode::apply_full(cheat, ccx, executor),)*
4078 }
4079 };
4080 }
4081 let mut result = if ccx.state.config.blocked_cheatcodes.contains(&cheat.func.selector_bytes) {
4082 Err(fmt_err!("disabled during restricted execution"))
4083 } else {
4084 vm_calls!(dispatch)
4085 };
4086
4087 if let Err(e) = &mut result
4089 && e.is_str()
4090 {
4091 let name = cheatcode_name(cheat);
4092 if !name.contains("assert") && name != "rpcUrl" {
4096 *e = fmt_err!("vm.{name}: {e}");
4097 }
4098 }
4099
4100 trace!(
4101 target: "cheatcodes",
4102 return = %match &result {
4103 Ok(b) => hex::encode(b),
4104 Err(e) => e.to_string(),
4105 }
4106 );
4107
4108 result
4109}
4110
4111const fn will_exit(action: &InterpreterAction) -> bool {
4113 match action {
4114 InterpreterAction::Return(result) => {
4115 result.result.is_ok_or_revert() || result.result.is_halt()
4116 }
4117 _ => false,
4118 }
4119}
4120
4121#[cfg(test)]
4122mod tests {
4123 use super::*;
4124
4125 fn cheats(flag: bool, broadcast: Option<Broadcast>) -> Cheatcodes {
4126 let config = CheatsConfig { batch_rewrite_creates: flag, ..Default::default() };
4127 let mut cheats = Cheatcodes::new(Arc::new(config));
4128 cheats.broadcast = broadcast;
4129 cheats
4130 }
4131
4132 fn create_inputs() -> CreateInputs {
4133 CreateInputs::new(Address::ZERO, CreateScheme::Create, U256::ZERO, Bytes::new(), 100_000, 0)
4134 }
4135
4136 fn broadcast_at(depth: usize) -> Broadcast {
4137 Broadcast { depth, ..Default::default() }
4138 }
4139
4140 #[test]
4141 fn flag_off_with_broadcast_returns_false() {
4142 let mut cheats = cheats(false, Some(broadcast_at(1)));
4143 assert!(!cheats.should_use_create2_factory(1, &create_inputs()));
4144 }
4145
4146 #[test]
4147 fn flag_on_without_broadcast_returns_false() {
4148 let mut cheats = cheats(true, None);
4149 assert!(!cheats.should_use_create2_factory(1, &create_inputs()));
4150 }
4151
4152 #[test]
4153 fn flag_on_with_broadcast_depth_mismatch_returns_false() {
4154 let mut cheats = cheats(true, Some(broadcast_at(2)));
4155 assert!(!cheats.should_use_create2_factory(1, &create_inputs()));
4156 }
4157
4158 #[test]
4159 fn flag_on_with_broadcast_depth_match_returns_true() {
4160 let mut cheats = cheats(true, Some(broadcast_at(1)));
4161 assert!(cheats.should_use_create2_factory(1, &create_inputs()));
4162 }
4163
4164 #[test]
4165 fn default_cheatcodes_have_no_opcode_hooks() {
4166 let cheats = Cheatcodes::<EthEvmNetwork>::new(Arc::default());
4167 assert!(!cheats.has_step_hooks());
4168 assert!(!cheats.has_step_end_hooks());
4169 assert!(!cheats.has_log_hooks());
4170 }
4171
4172 #[test]
4173 fn active_cheatcode_state_enables_opcode_hooks() {
4174 let mut cheats = Cheatcodes::<EthEvmNetwork>::new(Arc::default());
4175
4176 cheats.recording_accesses = true;
4177 assert!(cheats.has_step_hooks());
4178 assert!(!cheats.has_step_end_hooks());
4179 assert!(cheats.has_recording_accesses_only_step_hook());
4180
4181 cheats.recording_accesses = false;
4182 cheats.gas_metering.touched = true;
4183 assert!(!cheats.has_step_hooks());
4184 assert!(cheats.has_step_end_hooks());
4185 assert!(!cheats.has_recording_accesses_only_step_hook());
4186
4187 cheats.gas_metering.touched = false;
4188 cheats.register_storage_load_hook(Address::ZERO, Address::ZERO, [0; 4]);
4189 assert!(cheats.has_step_hooks());
4190 assert!(cheats.has_step_end_hooks());
4191 assert!(!cheats.has_recording_accesses_only_step_hook());
4192 }
4193
4194 #[test]
4195 fn mixed_step_hooks_disable_record_access_fast_path() {
4196 let mut cheats = Cheatcodes::<EthEvmNetwork>::new(Arc::default());
4197 cheats.recording_accesses = true;
4198
4199 cheats.gas_metering.reset = true;
4200 assert!(!cheats.has_recording_accesses_only_step_hook());
4201
4202 cheats.gas_metering.reset = false;
4203 cheats.env_overrides.insert(None, EnvOverrides { basefee: Some(1), ..Default::default() });
4204 assert!(!cheats.has_recording_accesses_only_step_hook());
4205 }
4206
4207 #[test]
4208 fn inactive_env_override_entries_do_not_enable_opcode_hooks() {
4209 let mut cheats = Cheatcodes::<EthEvmNetwork>::new(Arc::default());
4210 cheats.env_overrides.insert(None, EnvOverrides::default());
4211
4212 assert!(!cheats.has_step_hooks());
4213 assert!(!cheats.has_step_end_hooks());
4214
4215 cheats.env_overrides.get_mut(&None).unwrap().basefee = Some(1);
4216 assert!(cheats.has_step_hooks());
4217 assert!(cheats.has_step_end_hooks());
4218 }
4219
4220 #[test]
4221 fn active_log_state_enables_log_hooks() {
4222 let mut cheats = Cheatcodes::<EthEvmNetwork>::new(Arc::default());
4223
4224 cheats.recorded_logs = Some(Default::default());
4225 assert!(cheats.has_log_hooks());
4226
4227 cheats.recorded_logs = None;
4228 cheats.expected_emits.push_back((
4229 expect::ExpectedEmit {
4230 depth: 0,
4231 log: None,
4232 checks: [false; 5],
4233 address: None,
4234 anonymous: false,
4235 found: false,
4236 count: 1,
4237 mismatch_error: None,
4238 },
4239 Default::default(),
4240 ));
4241 assert!(cheats.has_log_hooks());
4242 }
4243
4244 #[test]
4245 fn arbitrary_storage_cache_value_routes_copied_targets_to_source() {
4246 let mut storage = ArbitraryStorage::default();
4247 let source = Address::repeat_byte(0x11);
4248 let copied = Address::repeat_byte(0x22);
4249 let slot = U256::from(7);
4250
4251 storage.mark_arbitrary(&source, false);
4252 storage.mark_copy(&source, &copied);
4253 storage.cache_value(copied, slot, U256::ZERO);
4254
4255 assert_eq!(storage.cached_value(source, slot), Some(U256::ZERO));
4256 }
4257}