1use crate::{
4 Cheatcode, CheatcodesCore, CheatsConfig, CheatsCtxt, Error, Result,
5 Vm::{self, AccountAccess},
6 evm::{
7 DealRecord, GasRecord, RecordAccess, append_storage_access, journaled_account,
8 mark_account_accesses_reverted, merge_recorded_frame,
9 mock::{self, MockCallDataContext, MockCallReturnData},
10 prank::Prank,
11 },
12 expected_emit::{self, ExpectedEmitTracker},
13 inspector::utils::CommonCreateInput,
14 script::Broadcast,
15 test::{
16 assume::AssumeNoRevert,
17 expect::{self, ExpectedCallTracker, ExpectedCreate, ExpectedRevert, ExpectedRevertKind},
18 revert_handlers,
19 },
20 utils::IgnoredTraces,
21};
22use alloy_consensus::BlobTransactionSidecarVariant;
23use alloy_network::{Ethereum, Network, TransactionBuilder};
24use alloy_primitives::{
25 Address, B256, Bytes, Log, TxKind, U256, hex,
26 map::{AddressHashMap, HashMap, HashSet},
27};
28use alloy_sol_types::{SolCall, SolInterface};
29use foundry_common::{
30 FoundryTransactionBuilder, SELECTOR_LEN, TransactionMaybeSigned,
31 mapping_slots::{
32 MappingSlots, PendingMappingHash, capture_hash as capture_mapping_hash,
33 record_hash as record_mapping_hash, step as mapping_step,
34 },
35};
36use foundry_evm_core::{
37 Breakpoints, EvmEnv, FoundryTransaction, InspectorExt,
38 abi::Vm::stopExpectSafeMemoryCall,
39 backend::{
40 ContextUpdateFor, DatabaseError, DatabaseExt, JournaledState, LocalForkId, RevertDiagnostic,
41 },
42 constants::{CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS, MAGIC_ASSUME},
43 env::FoundryContextExt,
44 evm::{
45 BlockEnvFor, ChainFor, EthEvmNetwork, EvmFactoryFor, FoundryContextFor, FoundryEvmFactory,
46 FoundryEvmNetwork, NestedEvmClosureFor, SpecFor, TransactionRequestFor, TxEnvFor,
47 with_inherited_evm,
48 },
49};
50use foundry_evm_traces::{
51 TracingInspector, TracingInspectorConfig, identifier::SignaturesIdentifier,
52};
53use itertools::Itertools;
54use rand::Rng;
55use revm::{
56 Inspector, JournalEntry,
57 bytecode::opcode as op,
58 context::{Cfg, ContextTr, Host, JournalTr, Transaction, TransactionType, result::EVMError},
59 context_interface::{CreateScheme, transaction::SignedAuthorization},
60 handler::FrameResult,
61 interpreter::{
62 CallInput, CallInputs, CallOutcome, CallScheme, CallValue, CreateInputs, CreateOutcome,
63 FrameInput, Gas, InstructionResult, Interpreter, InterpreterAction, InterpreterResult,
64 interpreter_types::{Jumps, LoopControl, MemoryTr, ReturnData},
65 },
66 primitives::hardfork::SpecId,
67};
68use std::{
69 cmp::max,
70 collections::{BTreeMap, VecDeque},
71 fmt::Debug,
72 ops::Range,
73 sync::{Arc, OnceLock},
74};
75
76mod env_overrides;
77pub use env_overrides::EnvOverrideState;
78
79mod utils;
80
81pub mod analysis;
82pub use analysis::CheatcodeAnalysis;
83
84pub trait CheatcodesExecutor<FEN: FoundryEvmNetwork> {
86 fn with_nested_evm(
89 &mut self,
90 cheats: &mut Cheatcodes<FEN>,
91 ecx: &mut FoundryContextFor<'_, FEN>,
92 f: NestedEvmClosureFor<'_, FEN>,
93 ) -> Result<(), EVMError<DatabaseError>>;
94
95 fn transact_on_db(
97 &mut self,
98 cheats: &mut Cheatcodes<FEN>,
99 ecx: &mut FoundryContextFor<'_, FEN>,
100 fork_id: Option<U256>,
101 transaction: B256,
102 ) -> eyre::Result<ContextUpdateFor<EvmFactoryFor<FEN>>>;
103
104 fn transact_from_tx_on_db(
106 &mut self,
107 cheats: &mut Cheatcodes<FEN>,
108 ecx: &mut FoundryContextFor<'_, FEN>,
109 tx: TxEnvFor<FEN>,
110 ) -> eyre::Result<()>;
111
112 #[allow(clippy::type_complexity)]
117 fn with_fresh_nested_evm(
118 &mut self,
119 cheats: &mut Cheatcodes<FEN>,
120 ecx: &mut FoundryContextFor<'_, FEN>,
121 chain_context: ChainFor<FEN>,
122 f: NestedEvmClosureFor<'_, FEN>,
123 ) -> Result<EvmEnv<SpecFor<FEN>, BlockEnvFor<FEN>>, EVMError<DatabaseError>>;
124
125 fn console_log(&mut self, msg: &str);
127
128 fn tracing_inspector(&mut self) -> Option<&mut TracingInspector> {
130 None
131 }
132}
133
134pub(crate) fn exec_create<FEN: FoundryEvmNetwork>(
136 executor: &mut dyn CheatcodesExecutor<FEN>,
137 inputs: CreateInputs,
138 ccx: &mut CheatsCtxt<'_, '_, FEN>,
139) -> std::result::Result<CreateOutcome, EVMError<DatabaseError>> {
140 let fee_token = ccx.tx_fee_token();
141 let tx_origin = ccx.tx_caller();
142 let mut inputs = Some(inputs);
143 let mut outcome = None;
144 executor.with_nested_evm(ccx.state, ccx.ecx, &mut |evm| {
145 evm.tx_mut().set_fee_token(fee_token);
146 evm.tx_mut().set_caller(tx_origin);
147 let inputs = inputs.take().unwrap();
148 evm.journal_inner_mut().depth += 1;
149
150 let frame = FrameInput::Create(Box::new(inputs));
151
152 let result = match evm.run_execution(frame)? {
153 FrameResult::Call(_) => unreachable!(),
154 FrameResult::Create(create) => create,
155 };
156
157 evm.journal_inner_mut().depth -= 1;
158
159 outcome = Some(result);
160 Ok(())
161 })?;
162 Ok(outcome.unwrap())
163}
164
165#[derive(Debug, Default, Clone, Copy)]
168struct TransparentCheatcodesExecutor;
169
170impl<FEN: FoundryEvmNetwork> CheatcodesExecutor<FEN> for TransparentCheatcodesExecutor {
171 fn with_nested_evm(
172 &mut self,
173 cheats: &mut Cheatcodes<FEN>,
174 ecx: &mut FoundryContextFor<'_, FEN>,
175 f: NestedEvmClosureFor<'_, FEN>,
176 ) -> Result<(), EVMError<DatabaseError>> {
177 with_inherited_evm::<FEN::EvmFactory, _>(ecx, cheats, f)
178 }
179
180 fn with_fresh_nested_evm(
181 &mut self,
182 cheats: &mut Cheatcodes<FEN>,
183 ecx: &mut FoundryContextFor<'_, FEN>,
184 chain_context: ChainFor<FEN>,
185 f: NestedEvmClosureFor<'_, FEN>,
186 ) -> Result<EvmEnv<SpecFor<FEN>, BlockEnvFor<FEN>>, EVMError<DatabaseError>> {
187 let depth = ecx.journal().depth();
188 let evm_env = ecx.evm_clone();
189 let (db, _) = ecx.db_journal_inner_mut();
190 let mut evm =
191 FEN::EvmFactory::default().create_nested_evm_with_inspector(db, evm_env, cheats);
192 evm.journal_inner_mut().depth = depth;
193 *evm.chain_mut() = chain_context;
194 f(&mut *evm)?;
195 Ok(evm.to_evm_env())
196 }
197
198 fn transact_on_db(
199 &mut self,
200 cheats: &mut Cheatcodes<FEN>,
201 ecx: &mut FoundryContextFor<'_, FEN>,
202 fork_id: Option<U256>,
203 transaction: B256,
204 ) -> eyre::Result<ContextUpdateFor<EvmFactoryFor<FEN>>> {
205 let evm_env = ecx.evm_clone();
206 let outer_tx_env = ecx.tx_clone();
207 let (db, inner) = ecx.db_journal_inner_mut();
208 db.transact(fork_id, transaction, evm_env, &outer_tx_env, inner, cheats)
209 }
210
211 fn transact_from_tx_on_db(
212 &mut self,
213 cheats: &mut Cheatcodes<FEN>,
214 ecx: &mut FoundryContextFor<'_, FEN>,
215 tx: TxEnvFor<FEN>,
216 ) -> eyre::Result<()> {
217 let evm_env = ecx.evm_clone();
218 let (db, inner) = ecx.db_journal_inner_mut();
219 db.transact_from_tx(tx, evm_env, inner, cheats)
220 }
221
222 fn console_log(&mut self, _msg: &str) {}
223}
224
225macro_rules! try_or_return {
226 ($e:expr) => {
227 match $e {
228 Ok(v) => v,
229 Err(_) => return,
230 }
231 };
232}
233
234#[derive(Clone, Debug)]
236pub struct BroadcastableTransaction<N: Network = Ethereum> {
237 pub rpc: Option<String>,
239 pub transaction: TransactionMaybeSigned<N>,
241 pub contract_address: Option<Address>,
243}
244
245#[derive(Clone, Debug, Copy)]
246pub struct RecordDebugStepInfo {
247 pub start_node_idx: usize,
249 pub original_tracer_config: TracingInspectorConfig,
251}
252
253#[derive(Clone, Copy, Debug, PartialEq, Eq)]
255pub struct StorageHook {
256 pub callback_target: Address,
258 pub callback_selector: [u8; 4],
260}
261
262#[derive(Clone, Debug)]
263enum PendingStorageHook {
264 Load {
265 account: Address,
266 slot: U256,
267 hook: StorageHook,
268 },
269 Store {
270 account: Address,
271 slot: U256,
272 old_value: U256,
273 mapping: Option<(B256, Vec<B256>)>,
274 hook: StorageHook,
275 },
276}
277
278#[derive(Clone, Debug)]
279struct ActiveStorageHook {
280 parent_depth: usize,
281 callback_target: Address,
282 callback_input: Bytes,
283 saved_gas: Gas,
284 saved_return_data: Bytes,
285 saved_stack_item: Option<U256>,
286 journal_start: usize,
287 inspector_state: StorageHookInspectorState,
288 outcome: Option<(InstructionResult, Bytes)>,
289}
290
291#[derive(Clone, Debug)]
292struct StorageHookInspectorState {
293 accesses: RecordAccess,
294 recording_accesses: bool,
295 mapping_slots: Option<AddressHashMap<MappingSlots>>,
296 recorded_logs: Option<Vec<Vm::Log>>,
297 recorded_logs_generation: u64,
298 mocked_calls: HashMap<Address, BTreeMap<MockCallDataContext, VecDeque<MockCallReturnData>>>,
299 mocked_functions: HashMap<Address, HashMap<Bytes, Address>>,
300 expected_revert: Option<ExpectedRevert>,
301 assume_no_revert: Option<AssumeNoRevert>,
302 expected_calls: ExpectedCallTracker,
303 expected_emits: ExpectedEmitTracker,
304 expected_creates: Vec<ExpectedCreate>,
305}
306
307#[derive(Clone, Debug, Default)]
309pub struct GasMetering {
310 pub paused: bool,
312 pub touched: bool,
315 pub reset: bool,
317 pub paused_frames: Vec<Gas>,
319
320 pub active_gas_snapshot: Option<(String, String)>,
322
323 pub last_call_gas: Option<crate::Vm::Gas>,
326 pub(crate) last_call_snapshot_gas_used: u64,
328
329 pub last_frame_gas: Option<crate::Vm::Gas>,
332 pub(crate) last_frame_snapshot_gas_used: u64,
334
335 isolated_snapshot_gas_used: Option<(u64, u64)>,
338
339 pending_isolated_region_gas: Option<(usize, u64, u64)>,
342
343 pub recording: bool,
345 pub last_gas_used: u64,
347 pub gas_records: Vec<GasRecord>,
349}
350
351impl GasMetering {
352 pub const fn start(&mut self) {
354 self.recording = true;
355 self.last_gas_used = 0;
356 self.pending_isolated_region_gas = None;
357 }
358
359 pub const fn stop(&mut self) {
361 self.recording = false;
362 }
363
364 pub fn resume(&mut self) {
366 if self.paused {
367 self.paused = false;
368 self.touched = true;
369 }
370 self.paused_frames.clear();
371 }
372
373 pub fn reset(&mut self) {
375 self.paused = false;
376 self.touched = true;
377 self.reset = true;
378 self.paused_frames.clear();
379 }
380
381 pub const fn set_isolated_snapshot_gas_used(&mut self, gas_used: u64, precharged_state: u64) {
386 self.isolated_snapshot_gas_used = Some((gas_used, precharged_state));
387 }
388
389 const fn take_isolated_snapshot_gas_used(&mut self, depth: usize, gas: &Gas) -> Option<u64> {
395 let Some((gas_used, precharged_state)) = self.isolated_snapshot_gas_used.take() else {
396 return None;
397 };
398 if self.recording {
399 self.pending_isolated_region_gas =
401 Some((depth, gas.total_gas_spent(), gas_used.saturating_sub(precharged_state)));
402 }
403 Some(gas_used)
404 }
405}
406
407#[derive(Clone, Debug, Default)]
409pub struct ArbitraryStorage {
410 values: HashMap<Address, HashMap<U256, U256>>,
414 copies: HashMap<Address, Address>,
416 overwrites: HashSet<Address>,
418 explicit_slots: HashMap<Address, HashSet<U256>>,
420}
421
422impl ArbitraryStorage {
423 pub fn mark_arbitrary(&mut self, address: &Address, overwrite: bool) {
425 self.values.insert(*address, HashMap::default());
426 self.explicit_slots.remove(address);
427 if overwrite {
428 self.overwrites.insert(*address);
429 } else {
430 self.overwrites.remove(address);
431 }
432 }
433
434 pub fn mark_copy(&mut self, from: &Address, to: &Address) {
436 if self.values.contains_key(from) {
437 self.copies.insert(*to, *from);
438 if let Some(slots) = self.explicit_slots.get(from).cloned() {
439 self.explicit_slots.insert(*to, slots);
440 } else {
441 self.explicit_slots.remove(to);
442 }
443 }
444 }
445
446 fn mark_explicit(&mut self, address: Address, slot: U256) {
448 if self.values.contains_key(&address) || self.copies.contains_key(&address) {
449 self.explicit_slots.entry(address).or_default().insert(slot);
450 }
451 }
452
453 fn is_explicit(&self, address: Address, slot: U256) -> bool {
455 self.explicit_slots.get(&address).is_some_and(|slots| slots.contains(&slot))
456 }
457
458 fn targets(&self) -> impl Iterator<Item = Address> + '_ {
460 self.values.keys().copied()
461 }
462
463 fn target_overwrite_modes(&self) -> impl Iterator<Item = (Address, bool)> + '_ {
466 self.values.keys().map(|address| (*address, self.overwrites.contains(address)))
467 }
468
469 fn copied_targets(&self) -> impl Iterator<Item = Address> + '_ {
471 self.copies.keys().copied()
472 }
473
474 fn copied_target_sources(&self) -> impl Iterator<Item = (Address, Address)> + '_ {
476 self.copies.iter().map(|(target, source)| (*target, *source))
477 }
478
479 fn cache_value(&mut self, address: Address, slot: U256, data: U256) {
481 if let Some(values) = self.values.get_mut(&address) {
482 values.insert(slot, data);
483 return;
484 }
485
486 let Some(source) = self.copies.get(&address).copied() else {
487 return;
488 };
489 if let Some(values) = self.values.get_mut(&source) {
490 values.insert(slot, data);
491 }
492 }
493
494 fn cached_value(&self, address: Address, slot: U256) -> Option<U256> {
496 self.values.get(&address).and_then(|values| values.get(&slot)).copied()
497 }
498
499 pub fn save<CTX: ContextTr>(
503 &mut self,
504 ecx: &mut CTX,
505 address: Address,
506 slot: U256,
507 data: U256,
508 ) {
509 self.values.get_mut(&address).expect("missing arbitrary address entry").insert(slot, data);
510 if ecx.journal_mut().load_account(address).is_ok() {
511 ecx.journal_mut()
512 .sstore(address, slot, data)
513 .expect("could not set arbitrary storage value");
514 }
515 }
516
517 pub fn copy<CTX: ContextTr>(
523 &mut self,
524 ecx: &mut CTX,
525 target: Address,
526 slot: U256,
527 new_value: U256,
528 ) -> U256 {
529 let source = self.copies.get(&target).expect("missing arbitrary copy target entry");
530 let storage_cache = self.values.get_mut(source).expect("missing arbitrary source storage");
531 let value = match storage_cache.get(&slot) {
532 Some(value) => *value,
533 None => {
534 storage_cache.insert(slot, new_value);
535 if ecx.journal_mut().load_account(*source).is_ok() {
537 ecx.journal_mut()
538 .sstore(*source, slot, new_value)
539 .expect("could not copy arbitrary storage value");
540 }
541 new_value
542 }
543 };
544 if ecx.journal_mut().load_account(target).is_ok() {
546 ecx.journal_mut().sstore(target, slot, value).expect("could not set storage");
547 }
548 value
549 }
550}
551
552pub type BroadcastableTransactions<N> = VecDeque<BroadcastableTransaction<N>>;
554
555#[derive(Clone, Copy, Debug, PartialEq, Eq)]
556enum CreatedAccountsFrameKind {
557 Call,
558 Create,
559}
560
561#[derive(Clone, Copy, Debug)]
562struct CreatedAccountsFrame {
563 kind: CreatedAccountsFrameKind,
564 depth: usize,
565 checkpoint: usize,
566 recorded_logs: usize,
568 recorded_logs_generation: u64,
570}
571
572#[derive(Clone, Copy, Debug)]
573struct CreatedAccountChange {
574 fork_id: Option<LocalForkId>,
575 address: Address,
576 creation: usize,
577 previous: Option<usize>,
578 committed: bool,
579}
580
581#[derive(Clone, Debug)]
582struct CreatedAccountsSnapshot {
583 fork_id: Option<LocalForkId>,
584 bindings: AddressHashMap<usize>,
585}
586
587#[derive(Clone, Debug)]
605pub struct Cheatcodes<FEN: FoundryEvmNetwork = EthEvmNetwork> {
606 pub core: CheatcodesCore,
608
609 pub block: Option<BlockEnvFor<FEN>>,
614
615 pub fork_block_number_override: Option<u64>,
619
620 pub active_delegations: Vec<SignedAuthorization>,
624
625 pub active_blob_sidecar: Option<BlobTransactionSidecarVariant>,
627
628 pub gas_price: Option<u128>,
633
634 pub pranks: BTreeMap<usize, Prank>,
636
637 pub expected_revert: Option<ExpectedRevert>,
639
640 pub assume_no_revert: Option<AssumeNoRevert>,
642
643 pub fork_revert_diagnostic: Option<RevertDiagnostic>,
645
646 pub accesses: RecordAccess,
648
649 pub recording_accesses: bool,
651
652 pub recorded_account_diffs_stack: Option<Vec<Vec<AccountAccess>>>,
658
659 pending_account_diffs: Option<Arc<[AccountAccess]>>,
661
662 recorded_account_diffs_prefix: Option<Arc<[AccountAccess]>>,
664
665 created_accounts: Vec<Address>,
667
668 created_account_bindings: HashMap<(Option<LocalForkId>, Address), usize>,
670
671 created_account_changes: Vec<CreatedAccountChange>,
673
674 created_accounts_frames: Vec<CreatedAccountsFrame>,
676
677 created_accounts_snapshots: HashMap<U256, CreatedAccountsSnapshot>,
679
680 pub record_debug_steps_info: Option<RecordDebugStepInfo>,
682
683 pub expected_emits: ExpectedEmitTracker,
685 pub expected_creates: Vec<ExpectedCreate>,
687
688 pub allowed_mem_writes: HashMap<u64, Vec<Range<u64>>>,
690
691 pub broadcast: Option<Broadcast>,
693
694 pub broadcastable_transactions: BroadcastableTransactions<FEN::Network>,
696
697 pub extra_cheatcode_addresses: &'static [Address],
699
700 pub skip_payloads: Vec<Bytes>,
705
706 pub eth_deals: Vec<DealRecord>,
708
709 pub gas_metering: GasMetering,
711
712 pub gas_snapshots: BTreeMap<String, BTreeMap<String, String>>,
715
716 pub pc: usize,
718 pub breakpoints: Breakpoints,
721
722 pub ignored_traces: IgnoredTraces,
724
725 storage_load_hooks: AddressHashMap<StorageHook>,
727 storage_store_hooks: AddressHashMap<StorageHook>,
729 mapping_storage_store_hooks: AddressHashMap<HashMap<B256, StorageHook>>,
731 storage_hook_mapping_slots: AddressHashMap<MappingSlots>,
733 pending_mapping_hash: Option<PendingMappingHash>,
735 storage_hooks_registered: bool,
737 pending_storage_hook: Option<PendingStorageHook>,
739 active_storage_hook: Option<ActiveStorageHook>,
741
742 pub deprecated: HashMap<&'static str, Option<&'static str>>,
744 pub script_address: Option<Address>,
746 signatures_identifier: OnceLock<Option<SignaturesIdentifier>>,
748 pub dynamic_gas_limit: bool,
750 pub execution_evm_version: Option<SpecFor<FEN>>,
752
753 pub env_overrides: EnvOverrideState,
755
756 pub fork_block_number_override_snapshots: HashMap<U256, Option<u64>>,
758
759 #[cfg(feature = "monad")]
762 pub context_snapshots:
763 HashMap<U256, (ChainFor<FEN>, monad_revm::reserve_balance::tracker::ReserveBalanceTracker)>,
764
765 pub in_isolation_context: bool,
775
776 pub pending_isolated_snapshot_journal: Option<Vec<JournalEntry>>,
779
780 pub track_isolated_snapshots: bool,
783
784 pub isolated_snapshot_restores: Vec<JournaledState>,
787
788 pub capture_isolated_snapshot_restore: bool,
791
792 pub deploy_code_depth: Option<usize>,
794}
795
796impl Default for Cheatcodes {
800 fn default() -> Self {
801 Self::new(Arc::default())
802 }
803}
804
805impl<FEN: FoundryEvmNetwork> Cheatcodes<FEN> {
806 pub fn new(config: Arc<CheatsConfig>) -> Self {
808 Self {
809 core: CheatcodesCore::new(config),
810 extra_cheatcode_addresses: &[],
811 block: Default::default(),
812 fork_block_number_override: Default::default(),
813 active_delegations: Default::default(),
814 active_blob_sidecar: Default::default(),
815 gas_price: Default::default(),
816 pranks: Default::default(),
817 expected_revert: Default::default(),
818 assume_no_revert: Default::default(),
819 fork_revert_diagnostic: Default::default(),
820 accesses: Default::default(),
821 recording_accesses: Default::default(),
822 recorded_account_diffs_stack: Default::default(),
823 pending_account_diffs: Default::default(),
824 recorded_account_diffs_prefix: Default::default(),
825 created_accounts: Default::default(),
826 created_account_bindings: Default::default(),
827 created_account_changes: Default::default(),
828 created_accounts_frames: Default::default(),
829 created_accounts_snapshots: Default::default(),
830 record_debug_steps_info: Default::default(),
831 expected_emits: Default::default(),
832 expected_creates: Default::default(),
833 allowed_mem_writes: Default::default(),
834 broadcast: Default::default(),
835 broadcastable_transactions: Default::default(),
836 skip_payloads: Default::default(),
837 eth_deals: Default::default(),
838 gas_metering: Default::default(),
839 gas_snapshots: Default::default(),
840 pc: Default::default(),
841 breakpoints: Default::default(),
842 ignored_traces: Default::default(),
843 storage_load_hooks: Default::default(),
844 storage_store_hooks: Default::default(),
845 mapping_storage_store_hooks: Default::default(),
846 storage_hook_mapping_slots: Default::default(),
847 pending_mapping_hash: Default::default(),
848 storage_hooks_registered: Default::default(),
849 pending_storage_hook: Default::default(),
850 active_storage_hook: Default::default(),
851 deprecated: Default::default(),
852 script_address: Default::default(),
853 signatures_identifier: Default::default(),
854 dynamic_gas_limit: Default::default(),
855 execution_evm_version: None,
856 env_overrides: Default::default(),
857 fork_block_number_override_snapshots: Default::default(),
858 #[cfg(feature = "monad")]
859 context_snapshots: Default::default(),
860 in_isolation_context: false,
861 pending_isolated_snapshot_journal: None,
862 track_isolated_snapshots: false,
863 isolated_snapshot_restores: Vec::new(),
864 capture_isolated_snapshot_restore: false,
865 deploy_code_depth: None,
866 }
867 }
868
869 #[inline]
871 pub const fn set_extra_cheatcode_addresses(&mut self, addresses: &'static [Address]) {
872 self.extra_cheatcode_addresses = addresses;
873 }
874
875 pub fn start_internal_state_diff_recording(&mut self) -> bool {
877 if self.recorded_account_diffs_stack.is_some()
878 || self.recorded_account_diffs_prefix.is_some()
879 {
880 return false;
881 }
882 self.recorded_account_diffs_stack = Some(Default::default());
883 true
884 }
885
886 pub fn stop_internal_state_diff_recording(&mut self) -> Vec<AccountAccess> {
888 self.recorded_account_diffs_stack.take().unwrap_or_default().into_iter().flatten().collect()
889 }
890
891 pub fn set_pending_account_diffs(&mut self, accesses: Vec<AccountAccess>) {
893 self.pending_account_diffs = (!accesses.is_empty()).then(|| Arc::from(accesses));
894 }
895
896 pub fn start_state_diff_recording(&mut self) {
898 self.recorded_account_diffs_prefix = self.pending_account_diffs.take();
899 self.recorded_account_diffs_stack = Some(Default::default());
900 }
901
902 pub fn recorded_account_diffs(&self) -> impl Iterator<Item = &AccountAccess> {
904 self.recorded_account_diffs_prefix
905 .iter()
906 .flat_map(|prefix| prefix.iter())
907 .chain(self.recorded_account_diffs_stack.iter().flatten().flatten())
908 }
909
910 pub fn take_recorded_account_diffs_prefix(&mut self) -> Vec<AccountAccess> {
912 self.recorded_account_diffs_prefix
913 .take()
914 .map(|prefix| prefix.as_ref().to_vec())
915 .unwrap_or_default()
916 }
917
918 pub(crate) fn created_account_bindings(
920 &self,
921 fork_id: Option<LocalForkId>,
922 ) -> AddressHashMap<usize> {
923 self.created_account_bindings
924 .iter()
925 .filter_map(|(&(event_fork_id, address), &creation)| {
926 (event_fork_id == fork_id).then_some((address, creation))
927 })
928 .collect()
929 }
930
931 pub(crate) fn created_accounts(&self, fork_id: Option<LocalForkId>) -> Vec<Address> {
933 let bindings = self.created_account_bindings(fork_id);
934 self.created_accounts
935 .iter()
936 .enumerate()
937 .filter_map(|(index, &address)| {
938 (bindings.get(&address) == Some(&index)).then_some(address)
939 })
940 .collect()
941 }
942
943 pub(crate) fn record_created_account(
945 &mut self,
946 fork_id: Option<LocalForkId>,
947 address: Address,
948 ) {
949 let creation = self.created_accounts.len();
950 self.created_accounts.push(address);
951 let previous = self.created_account_bindings.insert((fork_id, address), creation);
952 self.created_account_changes.push(CreatedAccountChange {
953 fork_id,
954 address,
955 creation,
956 previous,
957 committed: false,
958 });
959 }
960
961 pub(crate) fn commit_created_account_changes(&mut self, fork_id: Option<LocalForkId>) {
963 for change in &mut self.created_account_changes {
964 if change.fork_id == fork_id {
965 change.committed = true;
966 }
967 }
968 }
969
970 pub(crate) fn record_initial_created_accounts(
972 &mut self,
973 fork_id: Option<LocalForkId>,
974 accounts: impl IntoIterator<Item = (Address, usize)>,
975 ) {
976 for (address, creation) in accounts {
977 self.created_account_bindings.entry((fork_id, address)).or_insert(creation);
978 }
979 }
980
981 pub(crate) fn record_propagated_accounts(
983 &mut self,
984 fork_id: Option<LocalForkId>,
985 accounts: impl IntoIterator<Item = (Address, usize)>,
986 ) {
987 self.created_account_bindings
988 .extend(accounts.into_iter().map(|(address, creation)| ((fork_id, address), creation)));
989 }
990
991 pub(crate) fn snapshot_created_accounts(
993 &mut self,
994 snapshot_id: U256,
995 fork_id: Option<LocalForkId>,
996 ) {
997 let bindings = self.created_account_bindings(fork_id);
998 self.created_accounts_snapshots
999 .insert(snapshot_id, CreatedAccountsSnapshot { fork_id, bindings });
1000 }
1001
1002 pub(crate) fn revert_created_accounts(&mut self, snapshot_id: U256, remove: bool) {
1004 let snapshot = if remove {
1005 self.created_accounts_snapshots.remove(&snapshot_id)
1006 } else {
1007 self.created_accounts_snapshots.get(&snapshot_id).cloned()
1008 };
1009 if let Some(snapshot) = snapshot {
1010 self.created_account_bindings.retain(|(fork_id, _), _| *fork_id != snapshot.fork_id);
1011 self.created_account_bindings.extend(
1012 snapshot
1013 .bindings
1014 .into_iter()
1015 .map(|(address, creation)| ((snapshot.fork_id, address), creation)),
1016 );
1017 }
1018 }
1019
1020 pub(crate) fn delete_created_accounts_snapshot(&mut self, snapshot_id: U256) {
1022 self.created_accounts_snapshots.remove(&snapshot_id);
1023 }
1024
1025 pub(crate) fn clear_created_accounts_snapshots(&mut self) {
1027 self.created_accounts_snapshots.clear();
1028 }
1029
1030 fn start_created_accounts_frame(
1031 &mut self,
1032 reset: bool,
1033 kind: CreatedAccountsFrameKind,
1034 depth: usize,
1035 ) {
1036 if reset {
1037 self.created_accounts.clear();
1038 self.created_account_bindings.clear();
1039 self.created_account_changes.clear();
1040 self.created_accounts_frames.clear();
1041 for snapshot in self.created_accounts_snapshots.values_mut() {
1043 snapshot.bindings.clear();
1044 }
1045 }
1046 self.created_accounts_frames.push(CreatedAccountsFrame {
1047 kind,
1048 depth,
1049 checkpoint: self.created_account_changes.len(),
1050 recorded_logs: self.core.recorded_logs.as_ref().map_or(0, Vec::len),
1051 recorded_logs_generation: self.core.recorded_logs_generation,
1052 });
1053 }
1054
1055 fn finish_created_accounts_frame(
1056 &mut self,
1057 success: bool,
1058 kind: CreatedAccountsFrameKind,
1059 depth: usize,
1060 ) {
1061 let Some(frame) = self
1062 .created_accounts_frames
1063 .last()
1064 .copied()
1065 .filter(|frame| frame.kind == kind && frame.depth == depth)
1066 else {
1067 return;
1068 };
1069 let checkpoint = frame.checkpoint;
1070 self.created_accounts_frames.pop();
1071 if !success {
1072 if let Some(logs) = &mut self.core.recorded_logs {
1075 logs.truncate(
1076 if frame.recorded_logs_generation == self.core.recorded_logs_generation {
1077 frame.recorded_logs
1078 } else {
1079 0
1080 },
1081 );
1082 }
1083 while self.created_account_changes.len() > checkpoint {
1084 let change = self.created_account_changes.pop().expect("length checked");
1085 if change.committed {
1086 continue;
1087 }
1088 let key = (change.fork_id, change.address);
1089 if self.created_account_bindings.get(&key) != Some(&change.creation) {
1090 continue;
1091 }
1092 if let Some(previous) = change.previous {
1093 self.created_account_bindings.insert(key, previous);
1094 } else {
1095 self.created_account_bindings.remove(&key);
1096 }
1097 }
1098 }
1099 }
1100
1101 pub fn get_prank(&self, depth: usize) -> Option<&Prank> {
1105 self.pranks.range(..=depth).last().map(|(_, prank)| prank)
1106 }
1107
1108 pub fn add_delegation(&mut self, authorization: SignedAuthorization) {
1110 self.active_delegations.push(authorization);
1111 }
1112
1113 pub fn signatures_identifier(&self) -> Option<&SignaturesIdentifier> {
1115 self.signatures_identifier
1116 .get_or_init(|| {
1117 if let Some(artifacts) = &self.core.config.available_artifacts {
1118 return SignaturesIdentifier::new_offline_with_abis(
1119 artifacts.values().map(|contract| &contract.abi),
1120 )
1121 .ok();
1122 }
1123 SignaturesIdentifier::new(true).ok()
1124 })
1125 .as_ref()
1126 }
1127
1128 fn apply_cheatcode(
1130 &mut self,
1131 ecx: &mut FoundryContextFor<'_, FEN>,
1132 call: &CallInputs,
1133 gas: &mut Gas,
1134 executor: &mut dyn CheatcodesExecutor<FEN>,
1135 ) -> Result {
1136 let decoded = Vm::VmCalls::abi_decode(&call.input.bytes(ecx)).map_err(|e| {
1138 if let alloy_sol_types::Error::UnknownSelector { name: _, selector } = e {
1139 let msg = format!(
1140 "unknown cheatcode with selector {selector}; \
1141 you may have a mismatch between the `Vm` interface (likely in `forge-std`) \
1142 and the `forge` version"
1143 );
1144 return alloy_sol_types::Error::Other(std::borrow::Cow::Owned(msg));
1145 }
1146 e
1147 })?;
1148
1149 let caller = call.transfer_from();
1150
1151 ecx.db_mut().ensure_cheatcode_access_forking_mode(&caller)?;
1154
1155 apply_dispatch(
1156 &decoded,
1157 &mut CheatsCtxt { state: self, ecx, gas, caller, is_static: call.is_static },
1158 executor,
1159 )
1160 }
1161
1162 #[cfg(feature = "monad")]
1164 fn apply_monad_cheatcode(
1165 &mut self,
1166 ecx: &mut FoundryContextFor<'_, FEN>,
1167 call: &CallInputs,
1168 gas: &mut Gas,
1169 ) -> Result {
1170 let input = call.input.bytes(ecx);
1171 let caller = call.transfer_from();
1172
1173 ecx.db_mut().ensure_cheatcode_access_forking_mode(&caller)?;
1176
1177 crate::monad::apply_monad_cheatcode(
1178 &mut CheatsCtxt { state: self, ecx, gas, caller, is_static: call.is_static },
1179 &input,
1180 )
1181 }
1182
1183 fn allow_cheatcodes_on_create(
1189 &self,
1190 ecx: &mut FoundryContextFor<FEN>,
1191 caller: Address,
1192 created_address: Address,
1193 ) {
1194 if ecx.journal().depth() <= 1 || ecx.db().has_cheatcode_access(&caller) {
1195 ecx.db_mut().allow_cheatcode_access(created_address);
1196 }
1197 }
1198
1199 fn apply_accesslist(&mut self, ecx: &mut FoundryContextFor<FEN>) {
1208 if let Some(access_list) = &self.core.access_list
1209 && ecx.cfg().spec().into() >= SpecId::BERLIN
1210 {
1211 ecx.tx_mut().set_access_list(access_list.clone());
1212
1213 if ecx.tx().tx_type() == TransactionType::Legacy as u8 {
1214 ecx.tx_mut().set_tx_type(TransactionType::Eip2930 as u8);
1215 }
1216 }
1217 }
1218
1219 pub fn on_revert(&mut self, ecx: &mut FoundryContextFor<FEN>) {
1224 trace!(deals=?self.eth_deals.len(), "rolling back deals");
1225
1226 if self.expected_revert.is_some() {
1228 return;
1229 }
1230
1231 if ecx.journal().depth() > 0 {
1233 return;
1234 }
1235
1236 while let Some(record) = self.eth_deals.pop() {
1240 if let Some(acc) = ecx.journal_mut().evm_state_mut().get_mut(&record.address) {
1241 acc.info.balance = record.old_balance;
1242 }
1243 }
1244 }
1245
1246 fn deploy_code_caller_depth(&self, depth: usize) -> usize {
1249 if self.deploy_code_depth.is_some_and(|d| d + 1 == depth) { depth - 1 } else { depth }
1250 }
1251
1252 pub fn call_with_executor(
1257 &mut self,
1258 ecx: &mut FoundryContextFor<'_, FEN>,
1259 call: &mut CallInputs,
1260 executor: &mut dyn CheatcodesExecutor<FEN>,
1261 isolate_call: bool,
1262 ) -> Option<CallOutcome> {
1263 if let Some(spec_id) = self.execution_evm_version {
1265 EvmFactoryFor::<FEN>::set_execution_spec(ecx, spec_id);
1266 }
1267
1268 let mut gas = Gas::new(call.gas_limit);
1269 let curr_depth = ecx.journal().depth();
1270 self.start_created_accounts_frame(
1271 curr_depth == 0,
1272 CreatedAccountsFrameKind::Call,
1273 curr_depth,
1274 );
1275
1276 if curr_depth == 0 {
1280 let sender = ecx.tx().caller();
1281 let account = match super::evm::journaled_account(ecx, sender) {
1282 Ok(account) => account,
1283 Err(err) => {
1284 return Some(CallOutcome {
1285 result: InterpreterResult {
1286 result: InstructionResult::Revert,
1287 output: err.abi_encode().into(),
1288 gas,
1289 },
1290 memory_offset: call.return_memory_offset.clone(),
1291 was_precompile_called: false,
1292 precompile_call_logs: vec![],
1293 charged_new_account_state_gas: call.charged_new_account_state_gas,
1294 });
1295 }
1296 };
1297 let prev = account.info.nonce;
1298 account.info.nonce = prev.saturating_sub(1);
1299
1300 trace!(target: "cheatcodes", %sender, nonce=account.info.nonce, prev, "corrected nonce");
1301 }
1302
1303 if call.transfer_to() == CHEATCODE_ADDRESS {
1304 return match self.apply_cheatcode(ecx, call, &mut gas, executor) {
1305 Ok(retdata) => Some(CallOutcome {
1306 result: InterpreterResult {
1307 result: InstructionResult::Return,
1308 output: retdata.into(),
1309 gas,
1310 },
1311 memory_offset: call.return_memory_offset.clone(),
1312 was_precompile_called: true,
1313 precompile_call_logs: vec![],
1314 charged_new_account_state_gas: call.charged_new_account_state_gas,
1315 }),
1316 Err(err) => Some(CallOutcome {
1317 result: InterpreterResult {
1318 result: InstructionResult::Revert,
1319 output: err.abi_encode().into(),
1320 gas,
1321 },
1322 memory_offset: call.return_memory_offset.clone(),
1323 was_precompile_called: false,
1324 precompile_call_logs: vec![],
1325 charged_new_account_state_gas: call.charged_new_account_state_gas,
1326 }),
1327 };
1328 }
1329
1330 #[cfg(feature = "monad")]
1331 if crate::monad::is_monad_cheatcode_call(self.extra_cheatcode_addresses, call.transfer_to())
1332 {
1333 let checkpoint = ecx.journal_mut().checkpoint();
1334 return match self.apply_monad_cheatcode(ecx, call, &mut gas) {
1335 Ok(retdata) => {
1336 ecx.journal_mut().checkpoint_commit();
1337 Some(CallOutcome {
1338 result: InterpreterResult {
1339 result: InstructionResult::Return,
1340 output: retdata.into(),
1341 gas,
1342 },
1343 memory_offset: call.return_memory_offset.clone(),
1344 was_precompile_called: true,
1345 precompile_call_logs: vec![],
1346 charged_new_account_state_gas: call.charged_new_account_state_gas,
1347 })
1348 }
1349 Err(err) => {
1350 ecx.journal_mut().checkpoint_revert(checkpoint);
1351 Some(CallOutcome {
1352 result: InterpreterResult {
1353 result: InstructionResult::Revert,
1354 output: err.abi_encode().into(),
1355 gas,
1356 },
1357 memory_offset: call.return_memory_offset.clone(),
1358 was_precompile_called: false,
1359 precompile_call_logs: vec![],
1360 charged_new_account_state_gas: call.charged_new_account_state_gas,
1361 })
1362 }
1363 };
1364 }
1365
1366 if call.transfer_to() == HARDHAT_CONSOLE_ADDRESS {
1367 return None;
1368 }
1369
1370 if let Some(expected) = &mut self.expected_revert {
1374 expected.max_depth = max(curr_depth + 1, expected.max_depth);
1375 }
1376
1377 if let Some(expected) = self.core.expected_calls.get_mut(&call.bytecode_address) {
1379 let input = call.input.as_bytes(ecx);
1380 expect::observe_call(
1381 expected,
1382 &input,
1383 call.transfer_value(),
1384 call.gas_limit,
1385 call.scheme.into(),
1386 );
1387 }
1388
1389 if let Some(prank) = self.get_prank(curr_depth).copied() {
1391 if prank.delegate_call && curr_depth == prank.depth && call.scheme.is_delegate_call() {
1393 call.target_address = prank.new_caller;
1394 call.caller = prank.new_caller;
1395 if let Some(new_origin) = prank.new_origin {
1396 ecx.tx_mut().set_caller(new_origin);
1397 }
1398 if let Some(used) = prank.first_time_applied() {
1399 self.pranks.insert(curr_depth, used);
1400 }
1401 }
1402
1403 if let Some(changes) = prank.changes_for(curr_depth, call.transfer_from()) {
1404 if let Some(new_caller) = changes.caller {
1405 let _ = journaled_account(ecx, new_caller);
1407 call.caller = new_caller;
1408 }
1409 if let Some(new_origin) = changes.origin {
1410 ecx.tx_mut().set_caller(new_origin);
1411 }
1412 if let Some(used) = changes.used {
1413 self.pranks.insert(curr_depth, used);
1414 }
1415 }
1416 }
1417
1418 if let Some(mocks) = self.core.mocked_calls.get_mut(&call.bytecode_address) {
1420 let input = call.input.bytes(ecx);
1421 if let Some(return_data_queue) =
1422 mock::find_mock_returns(mocks, &input, call.transfer_value())
1423 && let Some(return_data) = return_data_queue.front().map(|x| x.to_owned())
1424 {
1425 if let Some(value) = call.transfer_value() {
1426 let checkpoint = ecx.journal_mut().checkpoint();
1427 match ecx.journal_mut().transfer_loaded(
1428 call.transfer_from(),
1429 call.transfer_to(),
1430 value,
1431 ) {
1432 None => {
1433 if return_data.reverts {
1434 ecx.journal_mut().checkpoint_revert(checkpoint);
1435 } else {
1436 ecx.journal_mut().checkpoint_commit();
1437 }
1438 }
1439 Some(err) => {
1440 ecx.journal_mut().checkpoint_revert(checkpoint);
1441 return Some(CallOutcome {
1442 result: InterpreterResult {
1443 result: err.into(),
1444 output: Bytes::new(),
1445 gas,
1446 },
1447 memory_offset: call.return_memory_offset.clone(),
1448 was_precompile_called: false,
1449 precompile_call_logs: vec![],
1450 charged_new_account_state_gas: call.charged_new_account_state_gas,
1451 });
1452 }
1453 }
1454 }
1455
1456 mock::advance_mock_returns(return_data_queue);
1457
1458 return Some(CallOutcome {
1459 result: InterpreterResult {
1460 result: if return_data.reverts {
1461 InstructionResult::Revert
1462 } else {
1463 InstructionResult::Return
1464 },
1465 output: return_data.data,
1466 gas,
1467 },
1468 memory_offset: call.return_memory_offset.clone(),
1469 was_precompile_called: true,
1470 precompile_call_logs: vec![],
1471 charged_new_account_state_gas: call.charged_new_account_state_gas,
1472 });
1473 }
1474 }
1475
1476 self.apply_accesslist(ecx);
1478
1479 if let Some(broadcast) = &mut self.broadcast {
1481 let is_fixed_gas_limit = call.gas_limit >= 21_000 && !self.dynamic_gas_limit;
1484 self.dynamic_gas_limit = false;
1485
1486 if (curr_depth == broadcast.depth || broadcast.deploy_from_code)
1492 && call.transfer_from() == broadcast.original_caller
1493 {
1494 if matches!(call.scheme, CallScheme::DelegateCall | CallScheme::CallCode) {
1497 let msg = "`delegatecall`s and `callcode`s cannot be broadcast; use a regular `call` instead";
1498 return Some(CallOutcome {
1499 result: InterpreterResult {
1500 result: InstructionResult::Revert,
1501 output: Error::encode(msg),
1502 gas,
1503 },
1504 memory_offset: call.return_memory_offset.clone(),
1505 was_precompile_called: false,
1506 precompile_call_logs: vec![],
1507 charged_new_account_state_gas: call.charged_new_account_state_gas,
1508 });
1509 }
1510
1511 broadcast.deploy_from_code = false;
1513
1514 ecx.tx_mut().set_caller(broadcast.new_origin);
1518
1519 call.caller = broadcast.new_origin;
1520 if !call.is_static {
1525 if let Err(err) = ecx.journal_mut().load_account(broadcast.new_origin) {
1526 return Some(CallOutcome {
1527 result: InterpreterResult {
1528 result: InstructionResult::Revert,
1529 output: Error::encode(err),
1530 gas,
1531 },
1532 memory_offset: call.return_memory_offset.clone(),
1533 was_precompile_called: false,
1534 precompile_call_logs: vec![],
1535 charged_new_account_state_gas: call.charged_new_account_state_gas,
1536 });
1537 }
1538
1539 let input = call.input.bytes(ecx);
1540 let chain_id = ecx.cfg().chain_id();
1541 let rpc = ecx.db().active_fork_url();
1542 let fee_token = ecx.tx().fee_token();
1543 let nonce =
1544 ecx.journal().evm_state().get(&broadcast.new_origin).unwrap().info.nonce;
1545
1546 let mut tx_req = TransactionRequestFor::<FEN>::default()
1547 .with_from(broadcast.new_origin)
1548 .with_to(call.transfer_to())
1549 .with_value(call.transfer_value().unwrap_or_default())
1550 .with_input(input)
1551 .with_nonce(nonce)
1552 .with_chain_id(chain_id);
1553 if is_fixed_gas_limit {
1554 tx_req.set_gas_limit(call.gas_limit)
1555 }
1556
1557 let active_delegations = std::mem::take(&mut self.active_delegations);
1558 if let Some(blob_sidecar) = self.active_blob_sidecar.take() {
1560 if !active_delegations.is_empty() {
1562 let msg = "both delegation and blob are active; `attachBlob` and `attachDelegation` are not compatible";
1563 return Some(CallOutcome {
1564 result: InterpreterResult {
1565 result: InstructionResult::Revert,
1566 output: Error::encode(msg),
1567 gas,
1568 },
1569 memory_offset: call.return_memory_offset.clone(),
1570 was_precompile_called: false,
1571 precompile_call_logs: vec![],
1572 charged_new_account_state_gas: call.charged_new_account_state_gas,
1573 });
1574 }
1575 tx_req.set_blob_sidecar(blob_sidecar);
1576 }
1577
1578 if !active_delegations.is_empty() {
1580 if let Err(err) = apply_authorization_nonces::<FEN>(
1581 ecx,
1582 &active_delegations,
1583 broadcast.new_origin,
1584 chain_id,
1585 ) {
1586 return Some(CallOutcome {
1587 result: InterpreterResult {
1588 result: InstructionResult::Revert,
1589 output: err.abi_encode().into(),
1590 gas,
1591 },
1592 memory_offset: call.return_memory_offset.clone(),
1593 was_precompile_called: false,
1594 precompile_call_logs: vec![],
1595 charged_new_account_state_gas: call.charged_new_account_state_gas,
1596 });
1597 }
1598 tx_req.set_authorization_list(active_delegations);
1599 }
1600 if let Some(fee_token) = fee_token {
1601 tx_req.set_fee_token(fee_token);
1602 }
1603 self.broadcastable_transactions.push_back(BroadcastableTransaction {
1604 rpc,
1605 transaction: TransactionMaybeSigned::new(tx_req),
1606 contract_address: None,
1607 });
1608 debug!(target: "cheatcodes", tx=?self.broadcastable_transactions.back().unwrap(), "broadcastable call");
1609
1610 if !isolate_call {
1613 let account = ecx
1614 .journal_mut()
1615 .evm_state_mut()
1616 .get_mut(&broadcast.new_origin)
1617 .unwrap();
1618 let prev = account.info.nonce;
1619 account.info.nonce += 1;
1620 debug!(target: "cheatcodes", address=%broadcast.new_origin, nonce=prev+1, prev, "incremented nonce");
1621 }
1622 } else if broadcast.single_call {
1623 let msg = "`staticcall`s are not allowed after `broadcast`; use `startBroadcast` instead";
1624 return Some(CallOutcome {
1625 result: InterpreterResult {
1626 result: InstructionResult::Revert,
1627 output: Error::encode(msg),
1628 gas,
1629 },
1630 memory_offset: call.return_memory_offset.clone(),
1631 was_precompile_called: false,
1632 precompile_call_logs: vec![],
1633 charged_new_account_state_gas: call.charged_new_account_state_gas,
1634 });
1635 }
1636 }
1637 }
1638
1639 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1641 let (initialized, old_balance, old_nonce) =
1644 if let Ok(acc) = ecx.journal_mut().load_account(call.transfer_to()) {
1645 (acc.data.info.exists(), acc.data.info.balance, acc.data.info.nonce)
1646 } else {
1647 (false, U256::ZERO, 0)
1648 };
1649
1650 let kind = match call.scheme {
1651 CallScheme::Call => crate::Vm::AccountAccessKind::Call,
1652 CallScheme::CallCode => crate::Vm::AccountAccessKind::CallCode,
1653 CallScheme::DelegateCall => crate::Vm::AccountAccessKind::DelegateCall,
1654 CallScheme::StaticCall => crate::Vm::AccountAccessKind::StaticCall,
1655 };
1656
1657 recorded_account_diffs_stack.push(vec![AccountAccess {
1663 chainInfo: crate::Vm::ChainInfo {
1664 forkId: ecx.db().active_fork_id().unwrap_or_default(),
1665 chainId: U256::from(ecx.cfg().chain_id()),
1666 },
1667 accessor: call.transfer_from(),
1668 account: call.bytecode_address,
1669 kind,
1670 initialized,
1671 oldBalance: old_balance,
1672 newBalance: U256::ZERO, oldNonce: old_nonce,
1674 newNonce: 0, value: call.call_value(),
1676 data: call.input.bytes(ecx),
1677 reverted: false,
1678 deployedCode: Bytes::new(),
1679 storageAccesses: vec![], depth: ecx.journal().depth().try_into().expect("journaled state depth exceeds u64"),
1681 }]);
1682 }
1683
1684 None
1685 }
1686
1687 pub fn arbitrary_storage_targets(&self) -> impl Iterator<Item = Address> + '_ {
1689 self.core.arbitrary_storage.as_ref().into_iter().flat_map(ArbitraryStorage::targets)
1690 }
1691
1692 pub fn arbitrary_storage_target_overwrite_modes(
1695 &self,
1696 ) -> impl Iterator<Item = (Address, bool)> + '_ {
1697 self.core
1698 .arbitrary_storage
1699 .as_ref()
1700 .into_iter()
1701 .flat_map(ArbitraryStorage::target_overwrite_modes)
1702 }
1703
1704 pub fn arbitrary_storage_copied_targets(&self) -> impl Iterator<Item = Address> + '_ {
1706 self.core.arbitrary_storage.as_ref().into_iter().flat_map(ArbitraryStorage::copied_targets)
1707 }
1708
1709 pub fn arbitrary_storage_copied_target_sources(
1711 &self,
1712 ) -> impl Iterator<Item = (Address, Address)> + '_ {
1713 self.core
1714 .arbitrary_storage
1715 .as_ref()
1716 .into_iter()
1717 .flat_map(ArbitraryStorage::copied_target_sources)
1718 }
1719
1720 pub fn cache_arbitrary_storage_value(&mut self, address: Address, slot: U256, value: U256) {
1722 if let Some(storage) = &mut self.core.arbitrary_storage {
1723 storage.cache_value(address, slot, value);
1724 }
1725 }
1726
1727 pub fn mark_arbitrary_storage_slot_explicit(&mut self, address: Address, slot: U256) {
1729 if let Some(storage) = &mut self.core.arbitrary_storage {
1730 storage.mark_explicit(address, slot);
1731 }
1732 }
1733
1734 pub fn is_arbitrary_storage_slot_explicit(&self, address: Address, slot: U256) -> bool {
1736 self.core
1737 .arbitrary_storage
1738 .as_ref()
1739 .is_some_and(|storage| storage.is_explicit(address, slot))
1740 }
1741
1742 pub fn cached_arbitrary_storage_value(&self, address: Address, slot: U256) -> Option<U256> {
1744 self.core.arbitrary_storage.as_ref().and_then(|storage| storage.cached_value(address, slot))
1745 }
1746
1747 pub fn has_arbitrary_storage(&self, address: &Address) -> bool {
1749 match &self.core.arbitrary_storage {
1750 Some(storage) => storage.values.contains_key(address),
1751 None => false,
1752 }
1753 }
1754
1755 pub fn should_overwrite_arbitrary_storage(
1759 &self,
1760 address: &Address,
1761 storage_slot: U256,
1762 ) -> bool {
1763 match &self.core.arbitrary_storage {
1764 Some(storage) => {
1765 storage.overwrites.contains(address)
1766 && storage
1767 .values
1768 .get(address)
1769 .and_then(|arbitrary_values| arbitrary_values.get(&storage_slot))
1770 .is_none()
1771 }
1772 None => false,
1773 }
1774 }
1775
1776 pub fn is_arbitrary_storage_copy(&self, address: &Address) -> bool {
1778 match &self.core.arbitrary_storage {
1779 Some(storage) => storage.copies.contains_key(address),
1780 None => false,
1781 }
1782 }
1783
1784 pub fn register_storage_load_hook(
1786 &mut self,
1787 target: Address,
1788 callback_target: Address,
1789 callback_selector: [u8; 4],
1790 ) {
1791 self.storage_load_hooks.insert(target, StorageHook { callback_target, callback_selector });
1792 self.storage_hooks_registered = true;
1793 }
1794
1795 pub fn register_storage_store_hook(
1797 &mut self,
1798 target: Address,
1799 callback_target: Address,
1800 callback_selector: [u8; 4],
1801 ) {
1802 self.storage_store_hooks.insert(target, StorageHook { callback_target, callback_selector });
1803 self.storage_hooks_registered = true;
1804 }
1805
1806 pub fn register_mapping_storage_store_hook(
1808 &mut self,
1809 target: Address,
1810 root_slot: B256,
1811 callback_target: Address,
1812 callback_selector: [u8; 4],
1813 ) -> bool {
1814 if self.storage_store_hooks.contains_key(&target) {
1815 return false;
1816 }
1817 self.storage_hook_mapping_slots.remove(&target);
1818 self.mapping_storage_store_hooks
1819 .entry(target)
1820 .or_default()
1821 .insert(root_slot, StorageHook { callback_target, callback_selector });
1822 self.storage_hooks_registered = true;
1823 true
1824 }
1825
1826 pub fn mapping_storage_store_hooks(
1828 &self,
1829 ) -> impl Iterator<Item = (Address, B256, StorageHook)> + '_ {
1830 self.mapping_storage_store_hooks
1831 .iter()
1832 .flat_map(|(target, hooks)| hooks.iter().map(|(root, hook)| (*target, *root, *hook)))
1833 }
1834
1835 pub fn has_mapping_storage_store_hooks(&self, target: Address) -> bool {
1837 self.mapping_storage_store_hooks.get(&target).is_some_and(|hooks| !hooks.is_empty())
1838 }
1839
1840 pub fn storage_load_hooks(&self) -> impl Iterator<Item = (Address, StorageHook)> + '_ {
1842 self.storage_load_hooks.iter().map(|(target, hook)| (*target, *hook))
1843 }
1844
1845 pub fn storage_store_hooks(&self) -> impl Iterator<Item = (Address, StorageHook)> + '_ {
1847 self.storage_store_hooks.iter().map(|(target, hook)| (*target, *hook))
1848 }
1849
1850 #[inline]
1852 pub const fn has_storage_hooks(&self) -> bool {
1853 self.storage_hooks_registered
1854 }
1855
1856 pub fn clear_storage_hook_mapping_slots(&mut self) {
1858 self.storage_hook_mapping_slots.clear();
1859 }
1860
1861 #[inline]
1863 pub const fn is_storage_hook_active(&self) -> bool {
1864 self.active_storage_hook.is_some()
1865 }
1866
1867 pub fn is_storage_hook_callback(
1869 &self,
1870 ecx: &FoundryContextFor<'_, FEN>,
1871 call: &CallInputs,
1872 ) -> bool {
1873 self.active_storage_hook.as_ref().is_some_and(|active| {
1874 active.outcome.is_none()
1875 && ecx.journal().depth() == active.parent_depth
1876 && call.transfer_from() == CHEATCODE_ADDRESS
1877 && call.transfer_to() == active.callback_target
1878 && call.input.bytes(ecx) == active.callback_input
1879 })
1880 }
1881
1882 fn finish_storage_hook_call(
1883 &mut self,
1884 ecx: &FoundryContextFor<'_, FEN>,
1885 call: &CallInputs,
1886 outcome: &CallOutcome,
1887 ) -> bool {
1888 let Some(active) = self.active_storage_hook.as_mut() else { return false };
1889 if active.outcome.is_some()
1890 || ecx.journal().depth() != active.parent_depth
1891 || call.transfer_from() != CHEATCODE_ADDRESS
1892 || call.transfer_to() != active.callback_target
1893 || call.input.bytes(ecx) != active.callback_input
1894 {
1895 return false;
1896 }
1897 active.outcome = Some((outcome.result.result, outcome.result.output.clone()));
1898 true
1899 }
1900
1901 #[inline(always)]
1902 pub fn has_step_hooks(&self) -> bool {
1903 self.broadcast.is_some()
1904 || self.gas_metering.paused
1905 || self.gas_metering.reset
1906 || self.recording_accesses
1907 || self.recorded_account_diffs_stack.is_some()
1908 || !self.allowed_mem_writes.is_empty()
1909 || self.core.mapping_slots.is_some()
1910 || self.gas_metering.recording
1911 || self.has_active_env_overrides()
1912 || self.has_storage_hooks()
1913 }
1914
1915 #[inline(always)]
1916 pub fn has_step_end_hooks(&self) -> bool {
1917 self.gas_metering.paused
1918 || self.gas_metering.touched
1919 || self.core.arbitrary_storage.is_some()
1920 || self.core.mapping_slots.is_some()
1921 || self.has_active_env_overrides()
1922 || self.has_storage_hooks()
1923 }
1924
1925 #[inline(always)]
1926 pub fn has_log_hooks(&self) -> bool {
1927 !self.expected_emits.is_empty() || self.core.recorded_logs.is_some()
1928 }
1929
1930 #[inline(always)]
1931 pub fn has_recording_accesses_only_step_hook(&self) -> bool {
1932 self.recording_accesses
1933 && self.broadcast.is_none()
1934 && !self.gas_metering.paused
1935 && !self.gas_metering.reset
1936 && self.recorded_account_diffs_stack.is_none()
1937 && self.allowed_mem_writes.is_empty()
1938 && self.core.mapping_slots.is_none()
1939 && !self.has_storage_hooks()
1940 && !self.gas_metering.recording
1941 && !self.has_active_env_overrides()
1942 }
1943
1944 #[inline(always)]
1945 fn has_active_env_overrides(&self) -> bool {
1946 self.env_overrides.is_any_set()
1947 }
1948}
1949
1950const fn frame_gas(result: &InterpreterResult) -> Vm::Gas {
1951 let gas = &result.gas;
1952 let regular_gas_spent = if result.is_halt() {
1954 gas.total_gas_spent()
1955 } else {
1956 gas.total_gas_spent().saturating_sub(gas.state_gas_spilled())
1957 };
1958 Vm::Gas {
1959 gasLimit: gas.limit(),
1960 gasTotalUsed: regular_gas_spent,
1961 gasMemoryUsed: 0,
1962 gasRefunded: gas.refunded(),
1963 gasRemaining: gas.remaining(),
1964 gasStateUsed: if result.is_ok() { gas.state_gas_spent() } else { 0 },
1965 }
1966}
1967
1968impl<FEN: FoundryEvmNetwork> Inspector<FoundryContextFor<'_, FEN>> for Cheatcodes<FEN> {
1969 fn initialize_interp(
1970 &mut self,
1971 interpreter: &mut Interpreter,
1972 ecx: &mut FoundryContextFor<'_, FEN>,
1973 ) {
1974 if let Some(block) = self.block.take() {
1977 ecx.set_block(block);
1978 }
1979 if let Some(gas_price) = self.gas_price.take() {
1980 ecx.tx_mut().set_gas_price(gas_price);
1981 }
1982
1983 if self.gas_metering.paused {
1985 self.gas_metering.paused_frames.push(interpreter.gas);
1986 }
1987
1988 if let Some(expected) = &mut self.expected_revert {
1990 expected.max_depth = max(ecx.journal().depth(), expected.max_depth);
1991 }
1992 }
1993
1994 fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut FoundryContextFor<'_, FEN>) {
1995 self.pc = interpreter.bytecode.pc();
1996
1997 if !self.has_step_hooks() {
1998 return;
1999 }
2000
2001 if self.finish_storage_hook_callback(interpreter, ecx) {
2002 return;
2003 }
2004
2005 if self.broadcast.is_some() {
2006 self.set_gas_limit_type(interpreter);
2007 }
2008
2009 if interpreter.bytecode.opcode() == op::CALLER
2012 && let Some(broadcast) = &self.broadcast
2013 && let Some(script_address) = self.script_address
2014 && ecx.journal().depth() == broadcast.depth
2015 && interpreter.input.target_address == script_address
2016 && interpreter.input.bytecode_address == Some(script_address)
2017 && interpreter.input.caller_address != broadcast.new_origin
2018 {
2019 interpreter.bytecode.set_action(InterpreterAction::new_return(
2020 InstructionResult::Revert,
2021 Bytes::from(
2022 format!(
2023 "Usage of `msg.sender` inside a `broadcast` in script contract detected. \
2024 `msg.sender` is `{:#x}`, not the broadcast sender `{:#x}`. \
2025 Use the `--sender` flag or pass the deployer address directly instead.",
2026 interpreter.input.caller_address, broadcast.new_origin,
2027 )
2028 .into_bytes(),
2029 ),
2030 interpreter.gas,
2031 ));
2032 return;
2033 }
2034
2035 if self.gas_metering.paused {
2037 self.meter_gas(interpreter);
2038 }
2039
2040 if self.gas_metering.reset {
2042 self.meter_gas_reset(interpreter);
2043 }
2044
2045 if self.recording_accesses {
2047 self.record_accesses(interpreter);
2048 }
2049
2050 if self.recorded_account_diffs_stack.is_some() {
2052 self.record_state_diffs(interpreter, ecx);
2053 }
2054
2055 if !self.allowed_mem_writes.is_empty() {
2057 self.check_mem_opcodes(
2058 interpreter,
2059 ecx.journal().depth().try_into().expect("journaled state depth exceeds u64"),
2060 );
2061 }
2062
2063 if self.core.mapping_slots.is_some() || !self.mapping_storage_store_hooks.is_empty() {
2064 if let Some(mapping_slots) = &mut self.core.mapping_slots {
2066 mapping_step(mapping_slots, interpreter);
2067 }
2068
2069 let account = interpreter.input.target_address;
2070 let mapping_hook_active = self.active_storage_hook.is_none()
2071 && self
2072 .mapping_storage_store_hooks
2073 .get(&account)
2074 .is_some_and(|hooks| !hooks.is_empty());
2075 if mapping_hook_active {
2076 mapping_step(&mut self.storage_hook_mapping_slots, interpreter);
2077 }
2078 self.pending_mapping_hash = if self.core.mapping_slots.is_some() || mapping_hook_active
2079 {
2080 capture_mapping_hash(interpreter)
2081 } else {
2082 None
2083 };
2084 }
2085
2086 if self.gas_metering.recording {
2088 self.meter_gas_record(interpreter, ecx);
2089 }
2090
2091 if !self.env_overrides.is_empty() {
2096 let fork_id = ecx.db().active_fork_id();
2097 if let Some(env_overrides) =
2098 self.env_overrides.get_mut(fork_id).filter(|o| o.is_any_set())
2099 {
2100 env_overrides.pending_opcode = None;
2104 env_overrides.pending_blobhash_index = None;
2105
2106 let opcode = interpreter.bytecode.opcode();
2107 match opcode {
2108 op::BASEFEE | op::GASPRICE => {
2109 env_overrides.pending_opcode = Some(opcode);
2110 }
2111 op::BLOBHASH => {
2112 env_overrides.pending_opcode = Some(opcode);
2113 env_overrides.pending_blobhash_index =
2114 interpreter.stack.peek(0).ok().and_then(|index| index.try_into().ok());
2115 }
2116 _ => {}
2117 }
2118 }
2119 }
2120
2121 if self.active_storage_hook.is_none() {
2122 self.capture_storage_hook(interpreter, ecx);
2123 }
2124 }
2125
2126 fn step_end(&mut self, interpreter: &mut Interpreter, ecx: &mut FoundryContextFor<'_, FEN>) {
2127 if !self.has_step_end_hooks() {
2128 return;
2129 }
2130
2131 if self.gas_metering.paused {
2132 self.meter_gas_end(interpreter);
2133 }
2134
2135 if self.gas_metering.touched {
2136 self.meter_gas_check(interpreter);
2137 }
2138
2139 if self.core.arbitrary_storage.is_some() {
2141 self.arbitrary_storage_end(interpreter, ecx);
2142 }
2143
2144 if let Some(pending) = self.pending_mapping_hash.take()
2145 && interpreter
2146 .bytecode
2147 .action
2148 .as_ref()
2149 .and_then(InterpreterAction::instruction_result)
2150 .is_none()
2151 {
2152 if let Some(mapping_slots) = &mut self.core.mapping_slots {
2153 record_mapping_hash(mapping_slots, interpreter, pending);
2154 }
2155 if self
2156 .mapping_storage_store_hooks
2157 .get(&pending.address)
2158 .is_some_and(|hooks| !hooks.is_empty())
2159 && self.active_storage_hook.is_none()
2160 {
2161 record_mapping_hash(&mut self.storage_hook_mapping_slots, interpreter, pending);
2162 }
2163 }
2164
2165 if self.active_storage_hook.is_none() {
2166 self.invoke_pending_storage_hook(interpreter, ecx);
2167 }
2168
2169 if !self.env_overrides.is_empty() {
2179 let fork_id = ecx.db().active_fork_id();
2180 if self.env_overrides.get(fork_id).is_some_and(|o| o.is_any_set()) {
2181 let opcode_failed = interpreter
2187 .bytecode
2188 .action
2189 .as_ref()
2190 .and_then(|a| a.instruction_result())
2191 .is_some();
2192 if opcode_failed {
2193 if let Some(env_overrides) = self.env_overrides.get_mut(fork_id) {
2194 env_overrides.pending_opcode = None;
2195 env_overrides.pending_blobhash_index = None;
2196 }
2197 } else {
2198 self.apply_env_overrides(interpreter, fork_id);
2199 }
2200 }
2201 }
2202 }
2203
2204 fn log(&mut self, _ecx: &mut FoundryContextFor<'_, FEN>, log: Log) {
2205 if !self.expected_emits.is_empty()
2206 && let Some(err) = expect::handle_expect_emit(self, &log, None)
2207 {
2208 let _ = sh_err!("{err:?}");
2212 }
2213
2214 record_logs(&mut self.core.recorded_logs, &log);
2216 }
2217
2218 fn log_full(
2219 &mut self,
2220 interpreter: &mut Interpreter,
2221 _ecx: &mut FoundryContextFor<'_, FEN>,
2222 log: Log,
2223 ) {
2224 if !self.expected_emits.is_empty() {
2225 expect::handle_expect_emit(self, &log, Some(interpreter));
2226 }
2227
2228 record_logs(&mut self.core.recorded_logs, &log);
2230 }
2231
2232 fn call(
2233 &mut self,
2234 ecx: &mut FoundryContextFor<'_, FEN>,
2235 inputs: &mut CallInputs,
2236 ) -> Option<CallOutcome> {
2237 if self.is_storage_hook_callback(ecx, inputs) {
2238 return None;
2239 }
2240 Self::call_with_executor(self, ecx, inputs, &mut TransparentCheatcodesExecutor, false)
2241 }
2242
2243 fn call_end(
2244 &mut self,
2245 ecx: &mut FoundryContextFor<'_, FEN>,
2246 call: &CallInputs,
2247 outcome: &mut CallOutcome,
2248 ) {
2249 let caller_depth = self.deploy_code_caller_depth(ecx.journal().depth());
2251 let isolated_snapshot_gas_used =
2252 self.gas_metering.take_isolated_snapshot_gas_used(caller_depth, &outcome.result.gas);
2253 if self.finish_storage_hook_call(ecx, call, outcome) {
2254 return;
2255 }
2256
2257 let cheatcode_call = call.transfer_to() == CHEATCODE_ADDRESS
2258 || call.transfer_to() == HARDHAT_CONSOLE_ADDRESS;
2259 #[cfg(feature = "monad")]
2260 let cheatcode_call = cheatcode_call
2261 || crate::monad::is_monad_cheatcode_call(
2262 self.extra_cheatcode_addresses,
2263 call.transfer_to(),
2264 );
2265 let curr_depth = ecx.journal().depth();
2266
2267 self.finish_created_accounts_frame(
2268 outcome.result.is_ok(),
2269 CreatedAccountsFrameKind::Call,
2270 curr_depth,
2271 );
2272
2273 if !cheatcode_call {
2277 if let Some(prank) = &self.get_prank(curr_depth)
2279 && curr_depth == prank.depth
2280 {
2281 ecx.tx_mut().set_caller(prank.prank_origin);
2282
2283 if prank.single_call {
2285 self.pranks.remove(&curr_depth);
2286 }
2287 }
2288
2289 if let Some(broadcast) = &self.broadcast
2291 && curr_depth == broadcast.depth
2292 {
2293 ecx.tx_mut().set_caller(broadcast.original_origin);
2294
2295 if broadcast.single_call {
2297 let _ = self.broadcast.take();
2298 }
2299 }
2300 }
2301
2302 if let Some(assume_no_revert) = &mut self.assume_no_revert {
2304 if outcome.result.is_revert() && assume_no_revert.reverted_by.is_none() {
2307 assume_no_revert.reverted_by = Some(call.transfer_to());
2308 }
2309
2310 let curr_depth = ecx.journal().depth();
2312 if curr_depth <= assume_no_revert.depth && !cheatcode_call {
2313 if outcome.result.is_revert() {
2316 let assume_no_revert = std::mem::take(&mut self.assume_no_revert).unwrap();
2317 return match revert_handlers::handle_assume_no_revert(
2318 &assume_no_revert,
2319 outcome.result.result,
2320 &outcome.result.output,
2321 &self.core.config.available_artifacts,
2322 ) {
2323 Ok(_) => {
2326 outcome.result.output = Error::from(MAGIC_ASSUME).abi_encode().into();
2327 }
2328 Err(error) => {
2331 trace!(expected=?assume_no_revert, ?error, status=?outcome.result.result, "Expected revert mismatch");
2332 outcome.result.result = InstructionResult::Revert;
2333 outcome.result.output = error.abi_encode().into();
2334 }
2335 };
2336 }
2337 self.assume_no_revert = None;
2339 }
2340 }
2341
2342 if let Some(expected_revert) = &mut self.expected_revert {
2344 let call_failed = !outcome.result.result.is_ok();
2347 if call_failed {
2348 if expected_revert.reverter.is_some()
2352 && (expected_revert.reverted_by.is_none() || expected_revert.count > 1)
2353 {
2354 expected_revert.reverted_by = Some(call.transfer_to());
2355 }
2356 }
2357
2358 let curr_depth = ecx.journal().depth();
2359 if curr_depth <= expected_revert.depth {
2360 let needs_processing = expected_revert.needs_processing(
2361 cheatcode_call,
2362 call_failed,
2363 curr_depth,
2364 self.core.config.internal_expect_revert,
2365 );
2366
2367 if needs_processing {
2368 let mut expected_revert = std::mem::take(&mut self.expected_revert).unwrap();
2369 let clear_last_frame_gas =
2370 matches!(expected_revert.kind, ExpectedRevertKind::Default);
2371 return match revert_handlers::handle_expect_revert(
2372 cheatcode_call,
2373 false,
2374 self.core.config.internal_expect_revert,
2375 &expected_revert,
2376 outcome.result.result,
2377 outcome.result.output.clone(),
2378 &self.core.config.available_artifacts,
2379 ) {
2380 Err(error) => {
2381 trace!(expected=?expected_revert, ?error, status=?outcome.result.result, "Expected revert mismatch");
2382 outcome.result.result = InstructionResult::Revert;
2383 outcome.result.output = error.abi_encode().into();
2384 }
2385 Ok((_, retdata)) => {
2386 expected_revert.actual_count += 1;
2387 if expected_revert.actual_count < expected_revert.count {
2388 self.expected_revert = Some(expected_revert);
2389 }
2390 if clear_last_frame_gas {
2391 self.gas_metering.last_frame_gas = None;
2392 }
2393 outcome.result.result = InstructionResult::Return;
2394 outcome.result.output = retdata;
2395 }
2396 };
2397 }
2398
2399 if let ExpectedRevertKind::Cheatcode { pending_processing } =
2402 &mut self.expected_revert.as_mut().unwrap().kind
2403 {
2404 *pending_processing = false;
2405 }
2406 }
2407 }
2408
2409 if cheatcode_call {
2412 return;
2413 }
2414
2415 let frame_gas = frame_gas(&outcome.result);
2418 let snapshot_gas_used =
2419 isolated_snapshot_gas_used.unwrap_or_else(|| outcome.result.gas.total_gas_spent());
2420 self.gas_metering.last_call_gas = Some(frame_gas.clone());
2421 self.gas_metering.last_frame_gas = Some(frame_gas);
2422 self.gas_metering.last_call_snapshot_gas_used = snapshot_gas_used;
2423 self.gas_metering.last_frame_snapshot_gas_used = snapshot_gas_used;
2424
2425 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
2428 if ecx.journal().depth() > 0
2430 && let Some(mut last_recorded_depth) = recorded_account_diffs_stack.pop()
2431 {
2432 if outcome.result.is_revert() {
2435 mark_account_accesses_reverted(&mut last_recorded_depth);
2436 }
2437
2438 if let Some(call_access) = last_recorded_depth.first_mut() {
2439 let curr_depth = ecx.journal().depth();
2444 if call_access.depth == curr_depth as u64
2445 && let Ok(acc) = ecx.journal_mut().load_account(call.transfer_to())
2446 {
2447 debug_assert!(access_is_call(call_access.kind));
2448 call_access.newBalance = acc.data.info.balance;
2449 call_access.newNonce = acc.data.info.nonce;
2450 }
2451 merge_recorded_frame(recorded_account_diffs_stack, last_recorded_depth);
2452 }
2453 }
2454 }
2455
2456 let diag = self.fork_revert_diagnostic.take();
2459
2460 if outcome.result.is_revert() {
2463 if let Some(err) = diag {
2466 outcome.result.output = Error::encode(err.to_error_msg(&self.core.labels));
2467 }
2468 return;
2469 }
2470
2471 if let Some(unmet) = expected_emit::check_call_emits(
2472 &mut self.expected_emits,
2473 ecx.journal().depth(),
2474 call.is_static,
2475 outcome.result.is_ok(),
2476 ) {
2477 outcome.result.result = InstructionResult::Revert;
2478 outcome.result.output = unmet.encode(|| self.signatures_identifier());
2479 return;
2480 }
2481
2482 if let TxKind::Call(test_contract) = ecx.tx().kind() {
2485 if ecx.db().is_forked_mode()
2488 && outcome.result.result == InstructionResult::Stop
2489 && call.transfer_to() != test_contract
2490 {
2491 self.fork_revert_diagnostic =
2492 ecx.db().diagnose_revert(call.transfer_to(), ecx.journal().evm_state());
2493 }
2494 }
2495
2496 if ecx.journal().depth() == 0 {
2498 if outcome.result.is_revert() {
2502 return;
2503 }
2504
2505 if let Some(msg) =
2510 expect::first_unmet_call(&self.core.expected_calls, outcome.result.is_ok())
2511 {
2512 outcome.result.result = InstructionResult::Revert;
2513 outcome.result.output = Error::encode(msg);
2514 return;
2515 }
2516
2517 if let Some(msg) = expected_emit::first_unmet_root_emit(
2519 &mut self.expected_emits,
2520 outcome.result.is_ok(),
2521 ) {
2522 outcome.result.result = InstructionResult::Revert;
2523 outcome.result.output = Error::encode(msg);
2524 return;
2525 }
2526
2527 if let Some(msg) = expect::first_unmet_create(&self.expected_creates) {
2529 outcome.result.result = InstructionResult::Revert;
2530 outcome.result.output = Error::encode(msg);
2531 }
2532 }
2533 }
2534
2535 fn create(
2536 &mut self,
2537 ecx: &mut FoundryContextFor<'_, FEN>,
2538 mut input: &mut CreateInputs,
2539 ) -> Option<CreateOutcome> {
2540 if let Some(spec_id) = self.execution_evm_version {
2542 EvmFactoryFor::<FEN>::set_execution_spec(ecx, spec_id);
2543 }
2544
2545 let gas = Gas::new(input.gas_limit());
2546 let curr_depth = ecx.journal().depth();
2547 self.start_created_accounts_frame(
2548 curr_depth == 0,
2549 CreatedAccountsFrameKind::Create,
2550 curr_depth,
2551 );
2552
2553 if self.core.intercept_next_create_call {
2555 self.core.intercept_next_create_call = false;
2557
2558 let output = input.init_code();
2560
2561 return Some(CreateOutcome {
2563 result: InterpreterResult { result: InstructionResult::Revert, output, gas },
2564 address: None,
2565 charged_create_state_gas: input.charged_create_state_gas(),
2566 });
2567 }
2568
2569 if let Some(prank) = self.get_prank(curr_depth)
2571 && let Some(changes) = prank.changes_for(curr_depth, input.caller())
2572 {
2573 if let Some(new_caller) = changes.caller {
2574 let _ = journaled_account(ecx, new_caller);
2576 input.set_caller(new_caller);
2577 }
2578 if let Some(new_origin) = changes.origin {
2579 ecx.tx_mut().set_caller(new_origin);
2580 }
2581 if let Some(used) = changes.used {
2582 self.pranks.insert(curr_depth, used);
2583 }
2584 }
2585
2586 self.apply_accesslist(ecx);
2588
2589 if let Some(broadcast) = &mut self.broadcast
2591 && curr_depth >= broadcast.depth
2592 && input.caller() == broadcast.original_caller
2593 {
2594 if let Err(err) = ecx.journal_mut().load_account(broadcast.new_origin) {
2595 return Some(CreateOutcome {
2596 result: InterpreterResult {
2597 result: InstructionResult::Revert,
2598 output: Error::encode(err),
2599 gas,
2600 },
2601 address: None,
2602 charged_create_state_gas: input.charged_create_state_gas(),
2603 });
2604 }
2605
2606 ecx.tx_mut().set_caller(broadcast.new_origin);
2607
2608 if curr_depth == broadcast.depth || broadcast.deploy_from_code {
2609 broadcast.deploy_from_code = false;
2611
2612 input.set_caller(broadcast.new_origin);
2613
2614 let rpc = ecx.db().active_fork_url();
2615 let fee_token = ecx.tx().fee_token();
2616 let account = &ecx.journal().evm_state()[&broadcast.new_origin];
2617 let mut tx_req = TransactionRequestFor::<FEN>::default()
2618 .with_from(broadcast.new_origin)
2619 .with_kind(TxKind::Create)
2620 .with_value(input.value())
2621 .with_input(input.init_code())
2622 .with_nonce(account.info.nonce);
2623 if let Some(fee_token) = fee_token {
2624 tx_req.set_fee_token(fee_token);
2625 }
2626 self.broadcastable_transactions.push_back(BroadcastableTransaction {
2627 rpc,
2628 transaction: TransactionMaybeSigned::new(tx_req),
2629 contract_address: None,
2630 });
2631
2632 input.log_debug(self, &input.scheme().unwrap_or(CreateScheme::Create));
2633 }
2634 }
2635
2636 let address = input.allow_cheatcodes(self, ecx);
2638
2639 self.record_created_account(ecx.db().active_fork_id(), address);
2640
2641 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
2643 recorded_account_diffs_stack.push(vec![AccountAccess {
2644 chainInfo: crate::Vm::ChainInfo {
2645 forkId: ecx.db().active_fork_id().unwrap_or_default(),
2646 chainId: U256::from(ecx.cfg().chain_id()),
2647 },
2648 accessor: input.caller(),
2649 account: address,
2650 kind: crate::Vm::AccountAccessKind::Create,
2651 initialized: true,
2652 oldBalance: U256::ZERO, newBalance: U256::ZERO, oldNonce: 0, newNonce: 1, value: input.value(),
2657 data: input.init_code(),
2658 reverted: false,
2659 deployedCode: Bytes::new(), storageAccesses: vec![], depth: curr_depth as u64,
2662 }]);
2663 }
2664
2665 None
2666 }
2667
2668 fn create_end(
2669 &mut self,
2670 ecx: &mut FoundryContextFor<'_, FEN>,
2671 call: &CreateInputs,
2672 outcome: &mut CreateOutcome,
2673 ) {
2674 let caller_depth = self.deploy_code_caller_depth(ecx.journal().depth());
2676 let isolated_snapshot_gas_used =
2677 self.gas_metering.take_isolated_snapshot_gas_used(caller_depth, &outcome.result.gas);
2678 let call = Some(call);
2679 let curr_depth = ecx.journal().depth();
2680
2681 self.finish_created_accounts_frame(
2682 outcome.result.is_ok(),
2683 CreatedAccountsFrameKind::Create,
2684 curr_depth,
2685 );
2686
2687 if let Some(prank) = &self.get_prank(curr_depth)
2689 && curr_depth == prank.depth
2690 {
2691 ecx.tx_mut().set_caller(prank.prank_origin);
2692
2693 if prank.single_call {
2695 self.pranks.remove(&curr_depth);
2696 }
2697 }
2698
2699 if let Some(broadcast) = &self.broadcast
2701 && curr_depth == broadcast.depth
2702 {
2703 ecx.tx_mut().set_caller(broadcast.original_origin);
2704
2705 if broadcast.single_call {
2707 std::mem::take(&mut self.broadcast);
2708 }
2709 }
2710
2711 if let Some(expected_revert) = &mut self.expected_revert {
2713 if outcome.result.is_revert()
2725 && expected_revert.reverter.is_some()
2726 && expected_revert.reverted_by.is_none()
2727 && let Some(addr) = outcome.address
2728 {
2729 expected_revert.reverted_by = Some(addr);
2730 }
2731
2732 if curr_depth <= expected_revert.depth
2733 && matches!(expected_revert.kind, ExpectedRevertKind::Default)
2734 {
2735 let mut expected_revert = std::mem::take(&mut self.expected_revert).unwrap();
2736 return match revert_handlers::handle_expect_revert(
2737 false,
2738 true,
2739 self.core.config.internal_expect_revert,
2740 &expected_revert,
2741 outcome.result.result,
2742 outcome.result.output.clone(),
2743 &self.core.config.available_artifacts,
2744 ) {
2745 Ok((address, retdata)) => {
2746 expected_revert.actual_count += 1;
2747 if expected_revert.actual_count < expected_revert.count {
2748 expected_revert.reverted_by = None;
2750 self.expected_revert = Some(expected_revert.clone());
2751 }
2752
2753 outcome.result.result = InstructionResult::Return;
2754 outcome.result.output = retdata;
2755 outcome.address = address;
2756 self.gas_metering.last_frame_gas = None;
2757 }
2758 Err(err) => {
2759 outcome.result.result = InstructionResult::Revert;
2760 outcome.result.output = err.abi_encode().into();
2761 }
2762 };
2763 }
2764 }
2765
2766 if curr_depth > 0 {
2767 self.gas_metering.last_frame_gas = Some(frame_gas(&outcome.result));
2770 self.gas_metering.last_frame_snapshot_gas_used =
2771 isolated_snapshot_gas_used.unwrap_or_else(|| outcome.result.gas.total_gas_spent());
2772 }
2773
2774 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack
2777 && let Some(mut last_depth) = recorded_account_diffs_stack.pop()
2778 {
2779 if outcome.result.is_revert() {
2782 mark_account_accesses_reverted(&mut last_depth);
2783 }
2784
2785 if let Some(create_access) = last_depth.first_mut() {
2786 if create_access.depth == curr_depth as u64 {
2788 debug_assert_eq!(
2789 create_access.kind as u8,
2790 crate::Vm::AccountAccessKind::Create as u8
2791 );
2792 if let Some(address) = outcome.address
2793 && let Ok(created_acc) = ecx.journal_mut().load_account(address)
2794 {
2795 create_access.newBalance = created_acc.data.info.balance;
2796 create_access.newNonce = created_acc.data.info.nonce;
2797 create_access.deployedCode =
2798 created_acc.data.info.code.clone().unwrap_or_default().original_bytes();
2799 }
2800 }
2801 }
2802 merge_recorded_frame(recorded_account_diffs_stack, last_depth);
2803 }
2804
2805 if !self.expected_creates.is_empty()
2807 && let (Some(address), Some(call)) = (outcome.address, call)
2808 && let Ok(created_acc) = ecx.journal_mut().load_account(address)
2809 {
2810 let bytecode = created_acc.data.info.code.clone().unwrap_or_default().original_bytes();
2811 expect::observe_create(
2812 &mut self.expected_creates,
2813 call.caller(),
2814 || call.scheme().into(),
2815 &bytecode,
2816 );
2817 }
2818 }
2819}
2820
2821impl<FEN: FoundryEvmNetwork> InspectorExt for Cheatcodes<FEN> {
2822 fn should_use_create2_factory(&mut self, depth: usize, inputs: &CreateInputs) -> bool {
2823 let depth = self.deploy_code_caller_depth(depth);
2826 let target_depth = if let Some(prank) = &self.get_prank(depth) {
2827 prank.depth
2828 } else if let Some(broadcast) = &self.broadcast {
2829 broadcast.depth
2830 } else {
2831 1
2832 };
2833
2834 if depth != target_depth {
2835 return false;
2836 }
2837
2838 match inputs.scheme() {
2839 CreateScheme::Create2 { .. } => {
2840 self.broadcast.is_some() || self.core.config.always_use_create_2_factory
2841 }
2842 CreateScheme::Create => {
2843 self.core.config.batch_rewrite_creates && self.broadcast.is_some()
2844 }
2845 _ => false,
2846 }
2847 }
2848
2849 fn create2_deployer(&self) -> Address {
2850 self.core.config.evm_opts.create2_deployer
2851 }
2852}
2853
2854impl<FEN: FoundryEvmNetwork> Cheatcodes<FEN> {
2855 #[cold]
2856 fn meter_gas(&mut self, interpreter: &mut Interpreter) {
2857 if let Some(paused_gas) = self.gas_metering.paused_frames.last() {
2858 let memory = *interpreter.gas.memory();
2861 interpreter.gas = *paused_gas;
2862 interpreter.gas.memory_mut().words_num = memory.words_num;
2863 interpreter.gas.memory_mut().expansion_cost = memory.expansion_cost;
2864 } else {
2865 self.gas_metering.paused_frames.push(interpreter.gas);
2867 }
2868 }
2869
2870 #[cold]
2871 fn meter_gas_record(
2872 &mut self,
2873 interpreter: &mut Interpreter,
2874 ecx: &mut FoundryContextFor<'_, FEN>,
2875 ) {
2876 if interpreter.bytecode.action.as_ref().and_then(|i| i.instruction_result()).is_none() {
2877 let curr_depth = ecx.journal().depth();
2878 let isolated_region_gas = match self.gas_metering.pending_isolated_region_gas {
2879 Some((depth, charged, used)) if depth == curr_depth => {
2880 self.gas_metering.pending_isolated_region_gas = None;
2881 Some((charged, used))
2882 }
2883 _ => None,
2884 };
2885 self.gas_metering.gas_records.iter_mut().for_each(|record| {
2886 if curr_depth == record.depth {
2887 if self.gas_metering.last_gas_used != 0 {
2890 let mut gas_diff = interpreter
2891 .gas
2892 .total_gas_spent()
2893 .saturating_sub(self.gas_metering.last_gas_used);
2894 if let Some((charged, used)) = isolated_region_gas {
2895 gas_diff = gas_diff.saturating_sub(charged).saturating_add(used);
2896 }
2897 record.gas_used = record.gas_used.saturating_add(gas_diff);
2898 }
2899
2900 self.gas_metering.last_gas_used = interpreter.gas.total_gas_spent();
2903 }
2904 });
2905 }
2906 }
2907
2908 #[cold]
2909 fn meter_gas_end(&mut self, interpreter: &mut Interpreter) {
2910 if let Some(interpreter_action) = interpreter.bytecode.action.as_ref()
2912 && will_exit(interpreter_action)
2913 {
2914 self.gas_metering.paused_frames.pop();
2915 }
2916 }
2917
2918 #[cold]
2919 const fn meter_gas_reset(&mut self, interpreter: &mut Interpreter) {
2920 let mut gas = Gas::new(interpreter.gas.limit());
2921 gas.memory_mut().words_num = interpreter.gas.memory().words_num;
2922 gas.memory_mut().expansion_cost = interpreter.gas.memory().expansion_cost;
2923 interpreter.gas = gas;
2924 self.gas_metering.reset = false;
2925 }
2926
2927 #[cold]
2928 fn meter_gas_check(&mut self, interpreter: &mut Interpreter) {
2929 if let Some(interpreter_action) = interpreter.bytecode.action.as_ref()
2930 && will_exit(interpreter_action)
2931 {
2932 if interpreter.gas.total_gas_spent()
2936 < u64::try_from(interpreter.gas.refunded()).unwrap_or_default()
2937 {
2938 interpreter.gas = Gas::new(interpreter.gas.limit());
2939 }
2940 }
2941 }
2942
2943 #[cold]
2957 fn apply_env_overrides(&mut self, interpreter: &mut Interpreter, fork_id: Option<U256>) {
2958 let Some(env_overrides) = self.env_overrides.get_mut(fork_id) else { return };
2959 let Some(opcode) = env_overrides.pending_opcode.take() else { return };
2960 let value = match opcode {
2962 op::BASEFEE => env_overrides.basefee_override().map(U256::from),
2963 op::GASPRICE => env_overrides.gas_price_override().map(U256::from),
2964 op::BLOBHASH => env_overrides
2966 .pending_blobhash_index
2967 .take()
2968 .and_then(|index| env_overrides.blob_hash_override(index))
2969 .map(Into::into),
2970 _ => None,
2971 };
2972 if let Some(value) = value {
2973 Self::replace_top_of_stack(interpreter, value);
2974 }
2975 }
2976
2977 fn replace_top_of_stack(interpreter: &mut Interpreter, value: U256) {
2985 if interpreter.stack.pop().is_err() {
2986 debug_assert!(false, "env override expected opcode result on stack");
2987 return;
2988 }
2989 let _ = interpreter.stack.push(value);
2990 }
2991
2992 #[cold]
3000 fn arbitrary_storage_end(
3001 &mut self,
3002 interpreter: &mut Interpreter,
3003 ecx: &mut FoundryContextFor<'_, FEN>,
3004 ) {
3005 let (key, target_address) = if interpreter.bytecode.opcode() == op::SLOAD {
3006 (try_or_return!(interpreter.stack.peek(0)), interpreter.input.target_address)
3007 } else {
3008 return;
3009 };
3010
3011 if self.is_arbitrary_storage_slot_explicit(target_address, key) {
3012 return;
3013 }
3014
3015 let Some(value) = ecx.sload(target_address, key) else {
3016 return;
3017 };
3018
3019 if (value.is_cold && value.data.is_zero())
3020 || self.should_overwrite_arbitrary_storage(&target_address, key)
3021 {
3022 if self.has_arbitrary_storage(&target_address) {
3023 let arbitrary_value = self
3024 .cached_arbitrary_storage_value(target_address, key)
3025 .unwrap_or_else(|| self.core.rng().random());
3026 self.core.arbitrary_storage.as_mut().unwrap().save(
3027 ecx,
3028 target_address,
3029 key,
3030 arbitrary_value,
3031 );
3032 } else if self.is_arbitrary_storage_copy(&target_address) {
3033 let arbitrary_value = self.core.rng().random();
3034 self.core.arbitrary_storage.as_mut().unwrap().copy(
3035 ecx,
3036 target_address,
3037 key,
3038 arbitrary_value,
3039 );
3040 }
3041 }
3042 }
3043
3044 #[inline]
3048 pub fn finish_storage_hook_callback(
3049 &mut self,
3050 interpreter: &mut Interpreter,
3051 ecx: &mut FoundryContextFor<'_, FEN>,
3052 ) -> bool {
3053 let Some(active) = self.active_storage_hook.as_ref() else { return false };
3054 let Some((result, output)) = active.outcome.clone() else { return false };
3055
3056 let active = self.active_storage_hook.take().expect("active storage hook exists");
3057 Self::restore_storage_hook_access(ecx, active.journal_start);
3058 self.restore_storage_hook_inspector_state(active.inspector_state);
3059 let _ = interpreter.stack.pop();
3060 if let Some(item) = active.saved_stack_item {
3061 let result = interpreter.stack.push(item);
3062 debug_assert!(result, "reserved storage-hook stack slot must be available");
3063 }
3064 interpreter.gas = active.saved_gas;
3065 interpreter.return_data.set_buffer(active.saved_return_data);
3066
3067 if result.is_ok() {
3068 false
3069 } else {
3070 interpreter.bytecode.set_action(InterpreterAction::new_return(
3071 InstructionResult::Revert,
3072 output,
3073 interpreter.gas,
3074 ));
3075 true
3076 }
3077 }
3078
3079 fn take_storage_hook_inspector_state(&mut self) -> StorageHookInspectorState {
3080 StorageHookInspectorState {
3081 accesses: std::mem::take(&mut self.accesses),
3082 recording_accesses: std::mem::replace(&mut self.recording_accesses, false),
3083 mapping_slots: self.core.mapping_slots.take(),
3084 recorded_logs: self.core.recorded_logs.take(),
3085 recorded_logs_generation: self.core.recorded_logs_generation,
3086 mocked_calls: std::mem::take(&mut self.core.mocked_calls),
3087 mocked_functions: std::mem::take(&mut self.core.mocked_functions),
3088 expected_revert: self.expected_revert.take(),
3089 assume_no_revert: self.assume_no_revert.take(),
3090 expected_calls: std::mem::take(&mut self.core.expected_calls),
3091 expected_emits: std::mem::take(&mut self.expected_emits),
3092 expected_creates: std::mem::take(&mut self.expected_creates),
3093 }
3094 }
3095
3096 fn restore_storage_hook_inspector_state(&mut self, state: StorageHookInspectorState) {
3097 self.accesses = state.accesses;
3098 self.recording_accesses = state.recording_accesses;
3099 self.core.mapping_slots = state.mapping_slots;
3100 self.core.recorded_logs = state.recorded_logs;
3101 self.core.recorded_logs_generation = state.recorded_logs_generation;
3102 self.core.mocked_calls = state.mocked_calls;
3103 self.core.mocked_functions = state.mocked_functions;
3104 self.expected_revert = state.expected_revert;
3105 self.assume_no_revert = state.assume_no_revert;
3106 self.core.expected_calls = state.expected_calls;
3107 self.expected_emits = state.expected_emits;
3108 self.expected_creates = state.expected_creates;
3109 }
3110
3111 fn restore_storage_hook_access(ecx: &mut FoundryContextFor<'_, FEN>, journal_start: usize) {
3112 let (_, journal) = ecx.db_journal_inner_mut();
3113 let entries =
3114 journal.journal.drain(journal_start.min(journal.journal.len())..).collect_vec();
3115 for entry in entries {
3116 match entry {
3117 JournalEntry::AccountWarmed { address } => {
3118 journal.state.get_mut(&address).expect("warmed account exists").mark_cold();
3119 }
3120 JournalEntry::StorageWarmed { address, key } => {
3121 journal
3125 .state
3126 .get_mut(&address)
3127 .expect("warmed account exists")
3128 .storage
3129 .get_mut(&key)
3130 .expect("warmed storage slot exists")
3131 .mark_cold();
3132 }
3133 entry => journal.journal.push(entry),
3134 }
3135 }
3136 }
3137
3138 fn capture_storage_hook(
3139 &mut self,
3140 interpreter: &Interpreter,
3141 ecx: &mut FoundryContextFor<'_, FEN>,
3142 ) {
3143 self.pending_storage_hook = None;
3144 if self.active_storage_hook.is_some() {
3145 return;
3146 }
3147 let account = interpreter.input.target_address;
3148 match interpreter.bytecode.opcode() {
3149 op::SLOAD => {
3150 let slot = try_or_return!(interpreter.stack.peek(0));
3151 let Some(hook) = self.storage_load_hooks.get(&account).copied() else { return };
3152 self.pending_storage_hook = Some(PendingStorageHook::Load { account, slot, hook });
3153 }
3154 op::SSTORE => {
3155 let slot = try_or_return!(interpreter.stack.peek(0));
3156 let (hook, mapping) = if let Some(hook) = self.storage_store_hooks.get(&account) {
3157 (*hook, None)
3158 } else {
3159 let Some(provenance) = self
3160 .storage_hook_mapping_slots
3161 .get(&account)
3162 .and_then(|slots| slots.resolve(slot.into()))
3163 else {
3164 return;
3165 };
3166 let Some(hook) = self
3167 .mapping_storage_store_hooks
3168 .get(&account)
3169 .and_then(|hooks| hooks.get(&provenance.root_slot))
3170 .copied()
3171 else {
3172 return;
3173 };
3174 (hook, Some((provenance.root_slot, provenance.keys)))
3175 };
3176 let checkpoint = ecx.journal_mut().checkpoint();
3177 let old_value =
3178 ecx.sload(account, slot).map(|value| value.data).unwrap_or_default();
3179 ecx.journal_mut().checkpoint_revert(checkpoint);
3180 self.pending_storage_hook =
3181 Some(PendingStorageHook::Store { account, slot, old_value, mapping, hook });
3182 }
3183 _ => {}
3184 }
3185 }
3186
3187 fn invoke_pending_storage_hook(
3188 &mut self,
3189 interpreter: &mut Interpreter,
3190 ecx: &mut FoundryContextFor<'_, FEN>,
3191 ) {
3192 let Some(pending) = self.pending_storage_hook.take() else { return };
3193 if interpreter
3194 .bytecode
3195 .action
3196 .as_ref()
3197 .and_then(InterpreterAction::instruction_result)
3198 .is_some()
3199 {
3200 return;
3201 }
3202
3203 let (hook, input, saved_stack_item) = match pending {
3204 PendingStorageHook::Load { account, slot, hook } => {
3205 let value = try_or_return!(interpreter.stack.peek(0));
3206 let mut input = Vec::with_capacity(4 + 32 * 3);
3207 input.extend_from_slice(&hook.callback_selector);
3208 input.extend_from_slice(account.into_word().as_slice());
3209 input.extend_from_slice(&slot.to_be_bytes::<32>());
3210 input.extend_from_slice(&value.to_be_bytes::<32>());
3211 (hook, Bytes::from(input), Some(value))
3212 }
3213 PendingStorageHook::Store { account, slot, old_value, mapping, hook } => {
3214 let new_value =
3215 ecx.sload(account, slot).map(|value| value.data).unwrap_or_default();
3216 let mut input = Vec::with_capacity(4 + 32 * 4);
3217 input.extend_from_slice(&hook.callback_selector);
3218 input.extend_from_slice(account.into_word().as_slice());
3219 input.extend_from_slice(&slot.to_be_bytes::<32>());
3220 if let Some((root, keys)) = mapping {
3221 input.extend_from_slice(root.as_slice());
3222 input.extend_from_slice(&U256::from(32 * 6).to_be_bytes::<32>());
3223 input.extend_from_slice(&old_value.to_be_bytes::<32>());
3224 input.extend_from_slice(&new_value.to_be_bytes::<32>());
3225 input.extend_from_slice(&U256::from(keys.len()).to_be_bytes::<32>());
3226 for key in keys {
3227 input.extend_from_slice(key.as_slice());
3228 }
3229 } else {
3230 input.extend_from_slice(&old_value.to_be_bytes::<32>());
3231 input.extend_from_slice(&new_value.to_be_bytes::<32>());
3232 }
3233 (hook, Bytes::from(input), None)
3234 }
3235 };
3236
3237 let journal_start = ecx.db_journal_inner_mut().1.journal.len();
3238 let account = match ecx.journal_mut().load_account_with_code(hook.callback_target) {
3239 Ok(account) => account,
3240 Err(err) => {
3241 interpreter.bytecode.set_action(InterpreterAction::new_return(
3242 InstructionResult::Revert,
3243 Error::encode(err),
3244 interpreter.gas,
3245 ));
3246 return;
3247 }
3248 };
3249 let known_bytecode =
3250 (account.info.code_hash(), account.info.code.clone().unwrap_or_default());
3251 let saved_gas = interpreter.gas;
3252 let saved_return_data = Bytes::copy_from_slice(interpreter.return_data.buffer());
3253 let gas_limit = interpreter.gas.remaining();
3254 let parent_depth = ecx.journal().depth();
3255 if saved_stack_item.is_some() {
3256 let result = interpreter.stack.pop();
3257 debug_assert!(result.is_ok(), "captured SLOAD result must be on the stack");
3258 }
3259 let inspector_state = self.take_storage_hook_inspector_state();
3260
3261 self.active_storage_hook = Some(ActiveStorageHook {
3262 parent_depth,
3263 callback_target: hook.callback_target,
3264 callback_input: input.clone(),
3265 saved_gas,
3266 saved_return_data,
3267 saved_stack_item,
3268 journal_start,
3269 inspector_state,
3270 outcome: None,
3271 });
3272 interpreter.bytecode.set_action(InterpreterAction::NewFrame(FrameInput::Call(Box::new(
3273 CallInputs {
3274 input: CallInput::Bytes(input),
3275 return_memory_offset: 0..0,
3276 gas_limit,
3277 reservoir: 0,
3278 bytecode_address: hook.callback_target,
3279 known_bytecode,
3280 target_address: hook.callback_target,
3281 caller: CHEATCODE_ADDRESS,
3282 value: CallValue::Transfer(U256::ZERO),
3283 scheme: CallScheme::Call,
3284 is_static: false,
3285 charged_new_account_state_gas: false,
3286 },
3287 ))));
3288 }
3289
3290 #[cold]
3292 fn record_accesses(&mut self, interpreter: &mut Interpreter) {
3293 let access = &mut self.accesses;
3294 match interpreter.bytecode.opcode() {
3295 op::SLOAD => {
3296 let key = try_or_return!(interpreter.stack.peek(0));
3297 access.record_read(interpreter.input.target_address, key);
3298 }
3299 op::SSTORE => {
3300 let key = try_or_return!(interpreter.stack.peek(0));
3301 access.record_write(interpreter.input.target_address, key);
3302 }
3303 _ => {}
3304 }
3305 }
3306
3307 #[cold]
3308 fn record_state_diffs(
3309 &mut self,
3310 interpreter: &mut Interpreter,
3311 ecx: &mut FoundryContextFor<'_, FEN>,
3312 ) {
3313 let Some(account_accesses) = &mut self.recorded_account_diffs_stack else { return };
3314 match interpreter.bytecode.opcode() {
3315 op::SELFDESTRUCT => {
3316 let Some(last) = account_accesses.last_mut() else { return };
3318
3319 let target = try_or_return!(interpreter.stack.peek(0));
3321 let target = Address::from_word(B256::from(target));
3322 let (initialized, old_balance, old_nonce) = ecx
3323 .journal_mut()
3324 .load_account(target)
3325 .map(|account| {
3326 (
3327 account.data.info.exists(),
3328 account.data.info.balance,
3329 account.data.info.nonce,
3330 )
3331 })
3332 .unwrap_or_default();
3333
3334 let value = ecx
3336 .balance(interpreter.input.target_address)
3337 .map(|b| b.data)
3338 .unwrap_or(U256::ZERO);
3339
3340 last.push(crate::Vm::AccountAccess {
3342 chainInfo: crate::Vm::ChainInfo {
3343 forkId: ecx.db().active_fork_id().unwrap_or_default(),
3344 chainId: U256::from(ecx.cfg().chain_id()),
3345 },
3346 accessor: interpreter.input.target_address,
3347 account: target,
3348 kind: crate::Vm::AccountAccessKind::SelfDestruct,
3349 initialized,
3350 oldBalance: old_balance,
3351 newBalance: old_balance + value,
3352 oldNonce: old_nonce,
3353 newNonce: old_nonce, value,
3355 data: Bytes::new(),
3356 reverted: false,
3357 deployedCode: Bytes::new(),
3358 storageAccesses: vec![],
3359 depth: ecx
3360 .journal()
3361 .depth()
3362 .try_into()
3363 .expect("journaled state depth exceeds u64"),
3364 });
3365 }
3366
3367 op::SLOAD => {
3368 let Some(last) = account_accesses.last_mut() else { return };
3369
3370 let key = try_or_return!(interpreter.stack.peek(0));
3371 let address = interpreter.input.target_address;
3372
3373 let checkpoint = ecx.journal_mut().checkpoint();
3377 let present_value =
3378 ecx.sload(address, key).map(|previous| previous.data).unwrap_or_default();
3379 ecx.journal_mut().checkpoint_revert(checkpoint);
3380 let access = crate::Vm::StorageAccess {
3381 account: interpreter.input.target_address,
3382 slot: key.into(),
3383 isWrite: false,
3384 previousValue: present_value.into(),
3385 newValue: present_value.into(),
3386 reverted: false,
3387 };
3388 let curr_depth =
3389 ecx.journal().depth().try_into().expect("journaled state depth exceeds u64");
3390 append_storage_access(last, access, curr_depth);
3391 }
3392 op::SSTORE => {
3393 let Some(last) = account_accesses.last_mut() else { return };
3394
3395 let key = try_or_return!(interpreter.stack.peek(0));
3396 let value = try_or_return!(interpreter.stack.peek(1));
3397 let address = interpreter.input.target_address;
3398 let checkpoint = ecx.journal_mut().checkpoint();
3402 let previous_value =
3403 ecx.sload(address, key).map(|previous| previous.data).unwrap_or_default();
3404 ecx.journal_mut().checkpoint_revert(checkpoint);
3405
3406 let access = crate::Vm::StorageAccess {
3407 account: address,
3408 slot: key.into(),
3409 isWrite: true,
3410 previousValue: previous_value.into(),
3411 newValue: value.into(),
3412 reverted: false,
3413 };
3414 let curr_depth =
3415 ecx.journal().depth().try_into().expect("journaled state depth exceeds u64");
3416 append_storage_access(last, access, curr_depth);
3417 }
3418
3419 op::EXTCODECOPY | op::EXTCODESIZE | op::EXTCODEHASH | op::BALANCE => {
3421 let kind = match interpreter.bytecode.opcode() {
3422 op::EXTCODECOPY => crate::Vm::AccountAccessKind::Extcodecopy,
3423 op::EXTCODESIZE => crate::Vm::AccountAccessKind::Extcodesize,
3424 op::EXTCODEHASH => crate::Vm::AccountAccessKind::Extcodehash,
3425 op::BALANCE => crate::Vm::AccountAccessKind::Balance,
3426 _ => unreachable!(),
3427 };
3428 let address =
3429 Address::from_word(B256::from(try_or_return!(interpreter.stack.peek(0))));
3430 let checkpoint = ecx.journal_mut().checkpoint();
3431 let (initialized, balance, nonce) = ecx
3432 .journal_mut()
3433 .load_account(address)
3434 .map(|acc| (acc.data.info.exists(), acc.data.info.balance, acc.data.info.nonce))
3435 .unwrap_or_default();
3436 ecx.journal_mut().checkpoint_revert(checkpoint);
3437 let curr_depth =
3438 ecx.journal().depth().try_into().expect("journaled state depth exceeds u64");
3439 let account_access = crate::Vm::AccountAccess {
3440 chainInfo: crate::Vm::ChainInfo {
3441 forkId: ecx.db().active_fork_id().unwrap_or_default(),
3442 chainId: U256::from(ecx.cfg().chain_id()),
3443 },
3444 accessor: interpreter.input.target_address,
3445 account: address,
3446 kind,
3447 initialized,
3448 oldBalance: balance,
3449 newBalance: balance,
3450 oldNonce: nonce,
3451 newNonce: nonce, value: U256::ZERO,
3453 data: Bytes::new(),
3454 reverted: false,
3455 deployedCode: Bytes::new(),
3456 storageAccesses: vec![],
3457 depth: curr_depth,
3458 };
3459 if let Some(last) = account_accesses.last_mut() {
3462 last.push(account_access);
3463 } else {
3464 account_accesses.push(vec![account_access]);
3465 }
3466 }
3467 _ => {}
3468 }
3469 }
3470
3471 #[cold]
3476 fn check_mem_opcodes(&self, interpreter: &mut Interpreter, depth: u64) {
3477 let Some(ranges) = self.allowed_mem_writes.get(&depth) else {
3478 return;
3479 };
3480
3481 macro_rules! mem_opcode_match {
3490 ($(($opcode:ident, $offset_depth:expr, $size_depth:expr, $writes:expr)),* $(,)?) => {
3491 match interpreter.bytecode.opcode() {
3492 op::MSTORE => {
3497 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
3499
3500 if !ranges.iter().any(|range| {
3503 range.contains(&offset) && range.contains(&(offset + 31))
3504 }) {
3505 let value = try_or_return!(interpreter.stack.peek(1)).to_be_bytes::<32>();
3510 if value[..SELECTOR_LEN] == stopExpectSafeMemoryCall::SELECTOR {
3511 return
3512 }
3513
3514 disallowed_mem_write(offset, 32, interpreter, ranges);
3515 return
3516 }
3517 }
3518 op::MSTORE8 => {
3519 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
3521
3522 if !ranges.iter().any(|range| range.contains(&offset)) {
3525 disallowed_mem_write(offset, 1, interpreter, ranges);
3526 return
3527 }
3528 }
3529
3530 op::MLOAD => {
3535 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
3537
3538 if offset >= interpreter.memory.size() as u64 && !ranges.iter().any(|range| {
3542 range.contains(&offset) && range.contains(&(offset + 31))
3543 }) {
3544 disallowed_mem_write(offset, 32, interpreter, ranges);
3545 return
3546 }
3547 }
3548
3549 op::CALL => {
3554 let dest_offset = try_or_return!(interpreter.stack.peek(5)).saturating_to::<u64>();
3556
3557 let size = try_or_return!(interpreter.stack.peek(6)).saturating_to::<u64>();
3559
3560 let fail_cond = !ranges.iter().any(|range| {
3564 range.contains(&dest_offset) &&
3565 range.contains(&(dest_offset + size.saturating_sub(1)))
3566 });
3567
3568 if fail_cond {
3571 let to = Address::from_word(try_or_return!(interpreter.stack.peek(1)).to_be_bytes::<32>().into());
3575 if to == CHEATCODE_ADDRESS {
3576 let args_offset = try_or_return!(interpreter.stack.peek(3)).saturating_to::<usize>();
3577 let args_size = try_or_return!(interpreter.stack.peek(4)).saturating_to::<usize>();
3578 if args_size >= SELECTOR_LEN
3580 && args_offset.saturating_add(args_size) <= interpreter.memory.size()
3581 {
3582 let memory_word = interpreter.memory.slice_len(args_offset, args_size);
3583 if memory_word[..SELECTOR_LEN] == stopExpectSafeMemoryCall::SELECTOR {
3584 return
3585 }
3586 }
3587 }
3588
3589 disallowed_mem_write(dest_offset, size, interpreter, ranges);
3590 return
3591 }
3592 }
3593
3594 $(op::$opcode => {
3595 let dest_offset = try_or_return!(interpreter.stack.peek($offset_depth)).saturating_to::<u64>();
3597
3598 let size = try_or_return!(interpreter.stack.peek($size_depth)).saturating_to::<u64>();
3600
3601 let fail_cond = !ranges.iter().any(|range| {
3605 range.contains(&dest_offset) &&
3606 range.contains(&(dest_offset + size.saturating_sub(1)))
3607 }) && ($writes ||
3608 [dest_offset, (dest_offset + size).saturating_sub(1)].into_iter().any(|offset| {
3609 offset >= interpreter.memory.size() as u64
3610 })
3611 );
3612
3613 if fail_cond {
3616 disallowed_mem_write(dest_offset, size, interpreter, ranges);
3617 return
3618 }
3619 })*
3620
3621 _ => {}
3622 }
3623 }
3624 }
3625
3626 mem_opcode_match!(
3629 (CALLDATACOPY, 0, 2, true),
3630 (CODECOPY, 0, 2, true),
3631 (RETURNDATACOPY, 0, 2, true),
3632 (EXTCODECOPY, 1, 3, true),
3633 (CALLCODE, 5, 6, true),
3634 (STATICCALL, 4, 5, true),
3635 (DELEGATECALL, 4, 5, true),
3636 (KECCAK256, 0, 1, false),
3637 (LOG0, 0, 1, false),
3638 (LOG1, 0, 1, false),
3639 (LOG2, 0, 1, false),
3640 (LOG3, 0, 1, false),
3641 (LOG4, 0, 1, false),
3642 (CREATE, 1, 2, false),
3643 (CREATE2, 1, 2, false),
3644 (RETURN, 0, 1, false),
3645 (REVERT, 0, 1, false),
3646 );
3647 }
3648
3649 #[cold]
3650 fn set_gas_limit_type(&mut self, interpreter: &mut Interpreter) {
3651 match interpreter.bytecode.opcode() {
3652 op::CREATE | op::CREATE2 => self.dynamic_gas_limit = true,
3654 op::CALL => {
3655 self.dynamic_gas_limit =
3658 try_or_return!(interpreter.stack.peek(0)) >= interpreter.gas.remaining() - 100
3659 }
3660 _ => self.dynamic_gas_limit = false,
3661 }
3662 }
3663}
3664
3665fn disallowed_mem_write(
3671 dest_offset: u64,
3672 size: u64,
3673 interpreter: &mut Interpreter,
3674 ranges: &[Range<u64>],
3675) {
3676 let revert_string = format!(
3677 "memory write at offset 0x{:02X} of size 0x{:02X} not allowed; safe range: {}",
3678 dest_offset,
3679 size,
3680 ranges.iter().map(|r| format!("[0x{:02X}, 0x{:02X})", r.start, r.end)).join(" U ")
3681 );
3682
3683 interpreter.bytecode.set_action(InterpreterAction::new_return(
3684 InstructionResult::Revert,
3685 Bytes::from(revert_string.into_bytes()),
3686 interpreter.gas,
3687 ));
3688}
3689
3690const fn access_is_call(kind: crate::Vm::AccountAccessKind) -> bool {
3692 matches!(
3693 kind,
3694 crate::Vm::AccountAccessKind::Call
3695 | crate::Vm::AccountAccessKind::StaticCall
3696 | crate::Vm::AccountAccessKind::CallCode
3697 | crate::Vm::AccountAccessKind::DelegateCall
3698 )
3699}
3700
3701fn record_logs(recorded_logs: &mut Option<Vec<Vm::Log>>, log: &Log) {
3703 if let Some(storage_recorded_logs) = recorded_logs {
3704 storage_recorded_logs.push(Vm::Log {
3705 topics: log.data.topics().to_vec(),
3706 data: log.data.data.clone(),
3707 emitter: log.address,
3708 });
3709 }
3710}
3711
3712const fn cheatcode_of<T: spec::CheatcodeDef>(_: &T) -> &'static spec::Cheatcode<'static> {
3714 T::CHEATCODE
3715}
3716
3717const fn cheatcode_id(cheat: &spec::Cheatcode<'static>) -> &'static str {
3718 cheat.func.id
3719}
3720
3721const fn cheatcode_signature(cheat: &spec::Cheatcode<'static>) -> &'static str {
3722 cheat.func.signature
3723}
3724
3725fn apply_dispatch<FEN: FoundryEvmNetwork>(
3727 calls: &Vm::VmCalls,
3728 ccx: &mut CheatsCtxt<'_, '_, FEN>,
3729 executor: &mut dyn CheatcodesExecutor<FEN>,
3730) -> Result {
3731 macro_rules! get_cheatcode {
3733 ($($variant:ident),*) => {
3734 match calls {
3735 $(Vm::VmCalls::$variant(cheat) => cheatcode_of(cheat),)*
3736 }
3737 };
3738 }
3739 let cheat = vm_calls!(get_cheatcode);
3740
3741 let _guard = debug_span!(target: "cheatcodes", "apply", id = %cheatcode_id(cheat)).entered();
3742 trace!(target: "cheatcodes", cheat = %cheatcode_signature(cheat), "applying");
3743
3744 if let spec::Status::Deprecated(replacement) = cheat.status {
3745 ccx.state.deprecated.insert(cheatcode_signature(cheat), replacement);
3746 }
3747
3748 macro_rules! dispatch {
3750 ($($variant:ident),*) => {
3751 match calls {
3752 $(Vm::VmCalls::$variant(cheat) => Cheatcode::apply_full(cheat, ccx, executor),)*
3753 }
3754 };
3755 }
3756 let result = if ccx.state.core.config.blocked_cheatcodes.contains(&cheat.func.selector_bytes) {
3757 Err(fmt_err!("disabled during restricted execution"))
3758 } else {
3759 vm_calls!(dispatch)
3760 }
3761 .map_err(|error| error.with_cheatcode_name(cheat));
3762
3763 trace!(
3764 target: "cheatcodes",
3765 return = %match &result {
3766 Ok(b) => hex::encode(b),
3767 Err(e) => e.to_string(),
3768 }
3769 );
3770
3771 result
3772}
3773
3774fn apply_authorization_nonces<FEN: FoundryEvmNetwork>(
3780 ecx: &mut FoundryContextFor<'_, FEN>,
3781 authorizations: &[SignedAuthorization],
3782 sender: Address,
3783 chain_id: u64,
3784) -> Result<()> {
3785 for auth in authorizations {
3786 if (!auth.chain_id.is_zero() && auth.chain_id != U256::from(chain_id))
3787 || auth.nonce() == u64::MAX
3788 {
3789 continue;
3790 }
3791 let Ok(authority) = auth.recover_authority() else { continue };
3792 let account = journaled_account(ecx, authority)?;
3795 if auth.nonce() == account.info.nonce + u64::from(authority == sender) {
3797 account.info.nonce += 1;
3798 }
3799 }
3800 Ok(())
3801}
3802
3803const fn will_exit(action: &InterpreterAction) -> bool {
3805 match action {
3806 InterpreterAction::Return(result) => {
3807 result.result.is_ok_or_revert() || result.result.is_halt()
3808 }
3809 _ => false,
3810 }
3811}
3812
3813#[cfg(test)]
3814mod tests {
3815 use super::*;
3816 use env_overrides::EnvOverrides;
3817
3818 fn cheats(flag: bool, broadcast: Option<Broadcast>) -> Cheatcodes {
3819 let config = CheatsConfig { batch_rewrite_creates: flag, ..Default::default() };
3820 let mut cheats = Cheatcodes::new(Arc::new(config));
3821 cheats.broadcast = broadcast;
3822 cheats
3823 }
3824
3825 fn create_inputs() -> CreateInputs {
3826 CreateInputs::new(Address::ZERO, CreateScheme::Create, U256::ZERO, Bytes::new(), 100_000, 0)
3827 }
3828
3829 fn broadcast_at(depth: usize) -> Broadcast {
3830 Broadcast { depth, ..Default::default() }
3831 }
3832
3833 #[test]
3834 fn flag_off_with_broadcast_returns_false() {
3835 let mut cheats = cheats(false, Some(broadcast_at(1)));
3836 assert!(!cheats.should_use_create2_factory(1, &create_inputs()));
3837 }
3838
3839 #[test]
3840 fn flag_on_without_broadcast_returns_false() {
3841 let mut cheats = cheats(true, None);
3842 assert!(!cheats.should_use_create2_factory(1, &create_inputs()));
3843 }
3844
3845 #[test]
3846 fn flag_on_with_broadcast_depth_mismatch_returns_false() {
3847 let mut cheats = cheats(true, Some(broadcast_at(2)));
3848 assert!(!cheats.should_use_create2_factory(1, &create_inputs()));
3849 }
3850
3851 #[test]
3852 fn flag_on_with_broadcast_depth_match_returns_true() {
3853 let mut cheats = cheats(true, Some(broadcast_at(1)));
3854 assert!(cheats.should_use_create2_factory(1, &create_inputs()));
3855 }
3856
3857 #[test]
3858 fn default_cheatcodes_have_no_opcode_hooks() {
3859 let cheats = Cheatcodes::<EthEvmNetwork>::new(Arc::default());
3860 assert!(!cheats.has_step_hooks());
3861 assert!(!cheats.has_step_end_hooks());
3862 assert!(!cheats.has_log_hooks());
3863 }
3864
3865 #[test]
3866 fn active_cheatcode_state_enables_opcode_hooks() {
3867 let mut cheats = Cheatcodes::<EthEvmNetwork>::new(Arc::default());
3868
3869 cheats.recording_accesses = true;
3870 assert!(cheats.has_step_hooks());
3871 assert!(!cheats.has_step_end_hooks());
3872 assert!(cheats.has_recording_accesses_only_step_hook());
3873
3874 cheats.recording_accesses = false;
3875 cheats.gas_metering.touched = true;
3876 assert!(!cheats.has_step_hooks());
3877 assert!(cheats.has_step_end_hooks());
3878 assert!(!cheats.has_recording_accesses_only_step_hook());
3879
3880 cheats.gas_metering.touched = false;
3881 cheats.register_storage_load_hook(Address::ZERO, Address::ZERO, [0; 4]);
3882 assert!(cheats.has_step_hooks());
3883 assert!(cheats.has_step_end_hooks());
3884 assert!(!cheats.has_recording_accesses_only_step_hook());
3885 }
3886
3887 #[test]
3888 fn mixed_step_hooks_disable_record_access_fast_path() {
3889 let mut cheats = Cheatcodes::<EthEvmNetwork>::new(Arc::default());
3890 cheats.recording_accesses = true;
3891
3892 cheats.gas_metering.reset = true;
3893 assert!(!cheats.has_recording_accesses_only_step_hook());
3894
3895 cheats.gas_metering.reset = false;
3896 cheats.env_overrides.update(None, |o| o.basefee = Some(1));
3897 assert!(!cheats.has_recording_accesses_only_step_hook());
3898 }
3899
3900 #[test]
3901 fn env_override_hook_predicates() {
3902 fn assert_hooks(cheats: &Cheatcodes, active: bool, case: &str) {
3903 assert_eq!(cheats.has_step_hooks(), active, "step hooks: {case}");
3904 assert_eq!(cheats.has_step_end_hooks(), active, "step_end hooks: {case}");
3905 }
3906
3907 let mut cheats = Cheatcodes::<EthEvmNetwork>::new(Arc::default());
3908 assert_hooks(&cheats, false, "empty map");
3909 let snapshot_id = U256::from(7);
3910 cheats.env_overrides.save_snapshot(snapshot_id, None, 0, 0, &[]);
3911
3912 cheats.env_overrides.update(None, |_| {});
3913 assert_hooks(&cheats, false, "inactive entry");
3914
3915 let active = [
3916 EnvOverrides { basefee: Some(1), ..Default::default() },
3917 EnvOverrides { implicit_basefee: Some(1), ..Default::default() },
3918 EnvOverrides { gas_price: Some(1), ..Default::default() },
3919 EnvOverrides { blob_hashes: Some(vec![B256::ZERO]), ..Default::default() },
3920 ];
3921 for overrides in active {
3922 let case = format!("{overrides:?}");
3923 cheats.env_overrides.update(None, |o| *o = overrides);
3924 assert_hooks(&cheats, true, &case);
3925 }
3926
3927 cheats.env_overrides.restore_snapshot(snapshot_id, false);
3929 cheats.env_overrides.update(Some(U256::from(1)), |o| o.basefee = Some(1));
3930 assert_hooks(&cheats, true, "override on another fork");
3931
3932 cheats.env_overrides.restore_snapshot(snapshot_id, false);
3934 assert_hooks(&cheats, false, "after restoring an empty snapshot");
3935 }
3936
3937 #[test]
3938 fn active_log_state_enables_log_hooks() {
3939 let mut cheats = Cheatcodes::<EthEvmNetwork>::new(Arc::default());
3940
3941 cheats.core.recorded_logs = Some(Default::default());
3942 assert!(cheats.has_log_hooks());
3943
3944 cheats.core.recorded_logs = None;
3945 cheats.expected_emits.push_back((
3946 crate::expected_emit::ExpectedEmit {
3947 depth: 0,
3948 log: None,
3949 checks: [false; 5],
3950 address: None,
3951 anonymous: false,
3952 found: false,
3953 count: 1,
3954 mismatch_error: None,
3955 },
3956 Default::default(),
3957 ));
3958 assert!(cheats.has_log_hooks());
3959 }
3960
3961 #[test]
3962 fn frame_gas_reports_settled_components() {
3963 for mut gas in [Gas::new(100_000), Gas::new_with_regular_gas_and_reservoir(100_000, 50_000)]
3964 {
3965 assert!(gas.record_regular_cost(1_000));
3966 assert!(gas.record_state_cost(20_000));
3967
3968 let mut result = InterpreterResult::new(InstructionResult::Stop, Bytes::new(), gas);
3969 let reported = frame_gas(&result);
3970 assert_eq!(reported.gasTotalUsed, 1_000);
3971 assert_eq!(reported.gasStateUsed, 20_000);
3972
3973 result.result = InstructionResult::Revert;
3974 assert_eq!(frame_gas(&result).gasStateUsed, 0);
3975 }
3976
3977 let mut gas = Gas::new(100_000);
3978 gas.refill_reservoir(20_000);
3979 let result = InterpreterResult::new(InstructionResult::Stop, Bytes::new(), gas);
3980 assert_eq!(frame_gas(&result).gasStateUsed, -20_000);
3981
3982 let mut gas = Gas::new(100_000);
3983 assert!(gas.record_state_cost(20_000));
3984 gas.spend_all();
3985 let result = InterpreterResult::new(InstructionResult::OutOfGas, Bytes::new(), gas);
3986 let reported = frame_gas(&result);
3987 assert_eq!(reported.gasTotalUsed, 100_000);
3988 assert_eq!(reported.gasStateUsed, 0);
3989 }
3990
3991 #[test]
3992 fn arbitrary_storage_cache_value_routes_copied_targets_to_source() {
3993 let mut storage = ArbitraryStorage::default();
3994 let source = Address::repeat_byte(0x11);
3995 let copied = Address::repeat_byte(0x22);
3996 let slot = U256::from(7);
3997
3998 storage.mark_arbitrary(&source, false);
3999 storage.mark_copy(&source, &copied);
4000 storage.cache_value(copied, slot, U256::ZERO);
4001
4002 assert_eq!(storage.cached_value(source, slot), Some(U256::ZERO));
4003 }
4004
4005 #[test]
4006 fn set_arbitrary_storage_applies_through_core_context() {
4007 let mut cheats = cheats(false, None);
4008 let target = Address::repeat_byte(0x11);
4009 let slot = U256::from(7);
4010 assert!(!cheats.has_step_end_hooks());
4011
4012 Vm::setArbitraryStorage_1Call { target, overwrite: true }
4013 .apply_context(&mut cheats.core)
4014 .unwrap();
4015 assert!(cheats.has_step_end_hooks());
4016 assert!(cheats.has_arbitrary_storage(&target));
4017 assert!(cheats.should_overwrite_arbitrary_storage(&target, slot));
4018
4019 cheats.mark_arbitrary_storage_slot_explicit(target, slot);
4020 cheats.cache_arbitrary_storage_value(target, slot, U256::from(42));
4021 assert!(cheats.is_arbitrary_storage_slot_explicit(target, slot));
4022 assert_eq!(cheats.cached_arbitrary_storage_value(target, slot), Some(U256::from(42)));
4023
4024 Vm::setArbitraryStorage_0Call { target }.apply_context(&mut cheats.core).unwrap();
4026 assert_eq!(
4027 cheats.arbitrary_storage_target_overwrite_modes().collect_vec(),
4028 [(target, false)]
4029 );
4030 assert!(!cheats.is_arbitrary_storage_slot_explicit(target, slot));
4031 assert_eq!(cheats.cached_arbitrary_storage_value(target, slot), None);
4032 }
4033}