1use crate::{
4 Cheatcode, CheatsConfig, CheatsCtxt, Error, Result,
5 Vm::{self, AccountAccess},
6 evm::{
7 DealRecord, GasRecord, RecordAccess, journaled_account,
8 mock::{MockCallDataContext, MockCallReturnData},
9 prank::Prank,
10 },
11 inspector::utils::CommonCreateInput,
12 script::{Broadcast, Wallets},
13 test::{
14 assume::AssumeNoRevert,
15 expect::{
16 self, ExpectedCallData, ExpectedCallTracker, ExpectedCallType, ExpectedCreate,
17 ExpectedEmitTracker, ExpectedRevert, ExpectedRevertKind,
18 },
19 revert_handlers,
20 },
21 utils::IgnoredTraces,
22};
23use alloy_consensus::BlobTransactionSidecarVariant;
24use alloy_network::{Ethereum, Network, TransactionBuilder};
25use alloy_primitives::{
26 Address, B256, Bytes, Log, TxKind, U256, hex,
27 map::{AddressHashMap, HashMap, HashSet},
28};
29use alloy_rpc_types::AccessList;
30use alloy_signer_local::PrivateKeySigner;
31use alloy_sol_types::{SolCall, SolInterface, SolValue};
32use foundry_common::{
33 FoundryTransactionBuilder, SELECTOR_LEN, TransactionMaybeSigned,
34 mapping_slots::{
35 MappingSlots, PendingMappingHash, capture_hash as capture_mapping_hash,
36 record_hash as record_mapping_hash, step as mapping_step,
37 },
38};
39use foundry_evm_core::{
40 Breakpoints, EvmEnv, FoundryTransaction, InspectorExt,
41 abi::Vm::stopExpectSafeMemoryCall,
42 backend::{ContextUpdateFor, DatabaseError, DatabaseExt, LocalForkId, RevertDiagnostic},
43 constants::{CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS, MAGIC_ASSUME},
44 env::FoundryContextExt,
45 evm::{
46 BlockEnvFor, ChainFor, EthEvmNetwork, EvmFactoryFor, FoundryContextFor, FoundryEvmFactory,
47 FoundryEvmNetwork, NestedEvmClosureFor, SpecFor, TransactionRequestFor, TxEnvFor,
48 with_inherited_evm,
49 },
50};
51use foundry_evm_traces::{
52 TracingInspector, TracingInspectorConfig, identifier::SignaturesIdentifier,
53};
54use foundry_wallets::wallet_multi::MultiWallet;
55use itertools::Itertools;
56use proptest::test_runner::{RngAlgorithm, TestRng, TestRunner};
57use rand::Rng;
58use revm::{
59 Inspector, JournalEntry,
60 bytecode::opcode as op,
61 context::{Cfg, ContextTr, Host, JournalTr, Transaction, TransactionType, result::EVMError},
62 context_interface::{CreateScheme, transaction::SignedAuthorization},
63 handler::FrameResult,
64 interpreter::{
65 CallInput, CallInputs, CallOutcome, CallScheme, CallValue, CreateInputs, CreateOutcome,
66 FrameInput, Gas, InstructionResult, Interpreter, InterpreterAction, InterpreterResult,
67 interpreter_types::{Jumps, LoopControl, MemoryTr, ReturnData},
68 return_ok,
69 },
70};
71use serde_json::Value;
72use std::{
73 cmp::max,
74 collections::{BTreeMap, VecDeque},
75 fmt::Debug,
76 fs::File,
77 io::BufReader,
78 ops::Range,
79 path::PathBuf,
80 sync::{Arc, OnceLock},
81};
82
83mod utils;
84
85pub mod analysis;
86pub use analysis::CheatcodeAnalysis;
87
88pub trait CheatcodesExecutor<FEN: FoundryEvmNetwork> {
90 fn with_nested_evm(
93 &mut self,
94 cheats: &mut Cheatcodes<FEN>,
95 ecx: &mut FoundryContextFor<'_, FEN>,
96 f: NestedEvmClosureFor<'_, FEN>,
97 ) -> Result<(), EVMError<DatabaseError>>;
98
99 fn transact_on_db(
101 &mut self,
102 cheats: &mut Cheatcodes<FEN>,
103 ecx: &mut FoundryContextFor<'_, FEN>,
104 fork_id: Option<U256>,
105 transaction: B256,
106 ) -> eyre::Result<ContextUpdateFor<EvmFactoryFor<FEN>>>;
107
108 fn transact_from_tx_on_db(
110 &mut self,
111 cheats: &mut Cheatcodes<FEN>,
112 ecx: &mut FoundryContextFor<'_, FEN>,
113 tx: TxEnvFor<FEN>,
114 ) -> eyre::Result<()>;
115
116 #[allow(clippy::type_complexity)]
121 fn with_fresh_nested_evm(
122 &mut self,
123 cheats: &mut Cheatcodes<FEN>,
124 db: &mut <FoundryContextFor<'_, FEN> as ContextTr>::Db,
125 evm_env: EvmEnv<SpecFor<FEN>, BlockEnvFor<FEN>>,
126 chain_context: ChainFor<FEN>,
127 f: NestedEvmClosureFor<'_, FEN>,
128 ) -> Result<EvmEnv<SpecFor<FEN>, BlockEnvFor<FEN>>, EVMError<DatabaseError>>;
129
130 fn console_log(&mut self, msg: &str);
132
133 fn tracing_inspector(&mut self) -> Option<&mut TracingInspector> {
135 None
136 }
137
138 fn set_in_inner_context(&mut self, _enabled: bool, _original_origin: Option<Address>) {}
142}
143
144pub(crate) fn exec_create<FEN: FoundryEvmNetwork>(
146 executor: &mut dyn CheatcodesExecutor<FEN>,
147 inputs: CreateInputs,
148 ccx: &mut CheatsCtxt<'_, '_, FEN>,
149) -> std::result::Result<CreateOutcome, EVMError<DatabaseError>> {
150 let fee_token = ccx.ecx.tx().fee_token();
151 let tx_origin = ccx.ecx.tx().caller();
152 let mut inputs = Some(inputs);
153 let mut outcome = None;
154 executor.with_nested_evm(ccx.state, ccx.ecx, &mut |evm| {
155 evm.tx_mut().set_fee_token(fee_token);
156 evm.tx_mut().set_caller(tx_origin);
157 let inputs = inputs.take().unwrap();
158 evm.journal_inner_mut().depth += 1;
159
160 let frame = FrameInput::Create(Box::new(inputs));
161
162 let result = match evm.run_execution(frame)? {
163 FrameResult::Call(_) => unreachable!(),
164 FrameResult::Create(create) => create,
165 };
166
167 evm.journal_inner_mut().depth -= 1;
168
169 outcome = Some(result);
170 Ok(())
171 })?;
172 Ok(outcome.unwrap())
173}
174
175#[derive(Debug, Default, Clone, Copy)]
178struct TransparentCheatcodesExecutor;
179
180impl<FEN: FoundryEvmNetwork> CheatcodesExecutor<FEN> for TransparentCheatcodesExecutor {
181 fn with_nested_evm(
182 &mut self,
183 cheats: &mut Cheatcodes<FEN>,
184 ecx: &mut FoundryContextFor<'_, FEN>,
185 f: NestedEvmClosureFor<'_, FEN>,
186 ) -> Result<(), EVMError<DatabaseError>> {
187 with_inherited_evm::<FEN::EvmFactory, _>(ecx, cheats, f)
188 }
189
190 fn with_fresh_nested_evm(
191 &mut self,
192 cheats: &mut Cheatcodes<FEN>,
193 db: &mut <FoundryContextFor<'_, FEN> as ContextTr>::Db,
194 evm_env: EvmEnv<SpecFor<FEN>, BlockEnvFor<FEN>>,
195 chain_context: ChainFor<FEN>,
196 f: NestedEvmClosureFor<'_, FEN>,
197 ) -> Result<EvmEnv<SpecFor<FEN>, BlockEnvFor<FEN>>, EVMError<DatabaseError>> {
198 let mut evm =
199 FEN::EvmFactory::default().create_nested_evm_with_inspector(db, evm_env, cheats);
200 *evm.chain_mut() = chain_context;
201 f(&mut *evm)?;
202 Ok(evm.to_evm_env())
203 }
204
205 fn transact_on_db(
206 &mut self,
207 cheats: &mut Cheatcodes<FEN>,
208 ecx: &mut FoundryContextFor<'_, FEN>,
209 fork_id: Option<U256>,
210 transaction: B256,
211 ) -> eyre::Result<ContextUpdateFor<EvmFactoryFor<FEN>>> {
212 let evm_env = ecx.evm_clone();
213 let outer_tx_env = ecx.tx_clone();
214 let (db, inner) = ecx.db_journal_inner_mut();
215 db.transact(fork_id, transaction, evm_env, &outer_tx_env, inner, cheats)
216 }
217
218 fn transact_from_tx_on_db(
219 &mut self,
220 cheats: &mut Cheatcodes<FEN>,
221 ecx: &mut FoundryContextFor<'_, FEN>,
222 tx: TxEnvFor<FEN>,
223 ) -> eyre::Result<()> {
224 let evm_env = ecx.evm_clone();
225 let (db, inner) = ecx.db_journal_inner_mut();
226 db.transact_from_tx(tx, evm_env, inner, cheats)
227 }
228
229 fn console_log(&mut self, _msg: &str) {}
230}
231
232macro_rules! try_or_return {
233 ($e:expr) => {
234 match $e {
235 Ok(v) => v,
236 Err(_) => return,
237 }
238 };
239}
240
241#[derive(Debug, Default)]
243pub struct TestContext {
244 pub opened_read_files: HashMap<PathBuf, BufReader<File>>,
246}
247
248impl Clone for TestContext {
250 fn clone(&self) -> Self {
251 Default::default()
252 }
253}
254
255impl TestContext {
256 pub fn clear(&mut self) {
258 self.opened_read_files.clear();
259 }
260}
261
262#[derive(Clone, Debug)]
264pub struct BroadcastableTransaction<N: Network = Ethereum> {
265 pub rpc: Option<String>,
267 pub transaction: TransactionMaybeSigned<N>,
269}
270
271#[derive(Clone, Debug, Copy)]
272pub struct RecordDebugStepInfo {
273 pub start_node_idx: usize,
275 pub original_tracer_config: TracingInspectorConfig,
277}
278
279#[derive(Clone, Debug, Default)]
308pub struct EnvOverrides {
309 pub basefee: Option<u64>,
311 pub gas_price: Option<u128>,
313 pub blob_hashes: Option<Vec<B256>>,
315 pub pre_override_gas_price: Option<u128>,
319 pub pre_override_tx_type: Option<u8>,
323 pub pre_override_blob_hashes: Option<Vec<B256>>,
326 pending_opcode: Option<u8>,
331 pending_blobhash_index: Option<u64>,
335}
336
337impl EnvOverrides {
338 #[inline]
340 pub const fn is_any_set(&self) -> bool {
341 self.basefee.is_some() || self.gas_price.is_some() || self.blob_hashes.is_some()
342 }
343}
344
345#[derive(Clone, Copy, Debug, PartialEq, Eq)]
347pub struct StorageHook {
348 pub callback_target: Address,
350 pub callback_selector: [u8; 4],
352}
353
354#[derive(Clone, Debug)]
355enum PendingStorageHook {
356 Load {
357 account: Address,
358 slot: U256,
359 hook: StorageHook,
360 },
361 Store {
362 account: Address,
363 slot: U256,
364 old_value: U256,
365 mapping: Option<(B256, Vec<B256>)>,
366 hook: StorageHook,
367 },
368}
369
370#[derive(Clone, Debug)]
371struct ActiveStorageHook {
372 parent_depth: usize,
373 callback_target: Address,
374 callback_input: Bytes,
375 saved_gas: Gas,
376 saved_return_data: Bytes,
377 saved_stack_item: Option<U256>,
378 journal_start: usize,
379 inspector_state: StorageHookInspectorState,
380 outcome: Option<(InstructionResult, Bytes)>,
381}
382
383#[derive(Clone, Debug)]
384struct StorageHookInspectorState {
385 accesses: RecordAccess,
386 recording_accesses: bool,
387 mapping_slots: Option<AddressHashMap<MappingSlots>>,
388 recorded_logs: Option<Vec<Vm::Log>>,
389 mocked_calls: HashMap<Address, BTreeMap<MockCallDataContext, VecDeque<MockCallReturnData>>>,
390 mocked_functions: HashMap<Address, HashMap<Bytes, Address>>,
391 expected_revert: Option<ExpectedRevert>,
392 assume_no_revert: Option<AssumeNoRevert>,
393 expected_calls: ExpectedCallTracker,
394 expected_emits: ExpectedEmitTracker,
395 expected_creates: Vec<ExpectedCreate>,
396}
397
398#[derive(Clone, Debug, Default)]
400pub struct GasMetering {
401 pub paused: bool,
403 pub touched: bool,
406 pub reset: bool,
408 pub paused_frames: Vec<Gas>,
410
411 pub active_gas_snapshot: Option<(String, String)>,
413
414 pub last_call_gas: Option<crate::Vm::Gas>,
417 pub(crate) last_call_snapshot_gas_used: u64,
419
420 pub last_frame_gas: Option<crate::Vm::Gas>,
423 pub(crate) last_frame_snapshot_gas_used: u64,
425
426 isolated_snapshot_gas_used: Option<u64>,
428
429 pending_isolated_refund: Option<(usize, u64)>,
431
432 pub recording: bool,
434 pub last_gas_used: u64,
436 pub gas_records: Vec<GasRecord>,
438}
439
440impl GasMetering {
441 pub const fn start(&mut self) {
443 self.recording = true;
444 self.pending_isolated_refund = None;
445 }
446
447 pub const fn stop(&mut self) {
449 self.recording = false;
450 }
451
452 pub fn resume(&mut self) {
454 if self.paused {
455 self.paused = false;
456 self.touched = true;
457 }
458 self.paused_frames.clear();
459 }
460
461 pub fn reset(&mut self) {
463 self.paused = false;
464 self.touched = true;
465 self.reset = true;
466 self.paused_frames.clear();
467 }
468
469 pub const fn set_isolated_snapshot_gas_used(&mut self, gas_used: u64) {
471 self.isolated_snapshot_gas_used = Some(gas_used);
472 }
473
474 const fn record_isolated_refund(
476 &mut self,
477 depth: usize,
478 gas: &Gas,
479 snapshot_gas_used: Option<u64>,
480 ) {
481 if self.recording
482 && let Some(snapshot_gas_used) = snapshot_gas_used
483 {
484 self.pending_isolated_refund =
485 Some((depth, gas.total_gas_spent().saturating_sub(snapshot_gas_used)));
486 }
487 }
488}
489
490#[derive(Clone, Debug, Default)]
492pub struct ArbitraryStorage {
493 values: HashMap<Address, HashMap<U256, U256>>,
497 copies: HashMap<Address, Address>,
499 overwrites: HashSet<Address>,
501 explicit_slots: HashMap<Address, HashSet<U256>>,
503}
504
505impl ArbitraryStorage {
506 pub fn mark_arbitrary(&mut self, address: &Address, overwrite: bool) {
508 self.values.insert(*address, HashMap::default());
509 self.explicit_slots.remove(address);
510 if overwrite {
511 self.overwrites.insert(*address);
512 } else {
513 self.overwrites.remove(address);
514 }
515 }
516
517 pub fn mark_copy(&mut self, from: &Address, to: &Address) {
519 if self.values.contains_key(from) {
520 self.copies.insert(*to, *from);
521 if let Some(slots) = self.explicit_slots.get(from).cloned() {
522 self.explicit_slots.insert(*to, slots);
523 } else {
524 self.explicit_slots.remove(to);
525 }
526 }
527 }
528
529 fn mark_explicit(&mut self, address: Address, slot: U256) {
531 if self.values.contains_key(&address) || self.copies.contains_key(&address) {
532 self.explicit_slots.entry(address).or_default().insert(slot);
533 }
534 }
535
536 fn is_explicit(&self, address: Address, slot: U256) -> bool {
538 self.explicit_slots.get(&address).is_some_and(|slots| slots.contains(&slot))
539 }
540
541 fn targets(&self) -> impl Iterator<Item = Address> + '_ {
543 self.values.keys().copied()
544 }
545
546 fn target_overwrite_modes(&self) -> impl Iterator<Item = (Address, bool)> + '_ {
549 self.values.keys().map(|address| (*address, self.overwrites.contains(address)))
550 }
551
552 fn copied_targets(&self) -> impl Iterator<Item = Address> + '_ {
554 self.copies.keys().copied()
555 }
556
557 fn copied_target_sources(&self) -> impl Iterator<Item = (Address, Address)> + '_ {
559 self.copies.iter().map(|(target, source)| (*target, *source))
560 }
561
562 fn cache_value(&mut self, address: Address, slot: U256, data: U256) {
564 if let Some(values) = self.values.get_mut(&address) {
565 values.insert(slot, data);
566 return;
567 }
568
569 let Some(source) = self.copies.get(&address).copied() else {
570 return;
571 };
572 if let Some(values) = self.values.get_mut(&source) {
573 values.insert(slot, data);
574 }
575 }
576
577 fn cached_value(&self, address: Address, slot: U256) -> Option<U256> {
579 self.values.get(&address).and_then(|values| values.get(&slot)).copied()
580 }
581
582 pub fn save<CTX: ContextTr>(
586 &mut self,
587 ecx: &mut CTX,
588 address: Address,
589 slot: U256,
590 data: U256,
591 ) {
592 self.values.get_mut(&address).expect("missing arbitrary address entry").insert(slot, data);
593 if ecx.journal_mut().load_account(address).is_ok() {
594 ecx.journal_mut()
595 .sstore(address, slot, data)
596 .expect("could not set arbitrary storage value");
597 }
598 }
599
600 pub fn copy<CTX: ContextTr>(
606 &mut self,
607 ecx: &mut CTX,
608 target: Address,
609 slot: U256,
610 new_value: U256,
611 ) -> U256 {
612 let source = self.copies.get(&target).expect("missing arbitrary copy target entry");
613 let storage_cache = self.values.get_mut(source).expect("missing arbitrary source storage");
614 let value = match storage_cache.get(&slot) {
615 Some(value) => *value,
616 None => {
617 storage_cache.insert(slot, new_value);
618 if ecx.journal_mut().load_account(*source).is_ok() {
620 ecx.journal_mut()
621 .sstore(*source, slot, new_value)
622 .expect("could not copy arbitrary storage value");
623 }
624 new_value
625 }
626 };
627 if ecx.journal_mut().load_account(target).is_ok() {
629 ecx.journal_mut().sstore(target, slot, value).expect("could not set storage");
630 }
631 value
632 }
633}
634
635pub type BroadcastableTransactions<N> = VecDeque<BroadcastableTransaction<N>>;
637
638#[derive(Clone, Copy, Debug, PartialEq, Eq)]
639enum CreatedAccountsFrameKind {
640 Call,
641 Create,
642}
643
644#[derive(Clone, Copy, Debug)]
645struct CreatedAccountsFrame {
646 kind: CreatedAccountsFrameKind,
647 depth: usize,
648 checkpoint: usize,
649}
650
651#[derive(Clone, Copy, Debug)]
652struct CreatedAccountChange {
653 fork_id: Option<LocalForkId>,
654 address: Address,
655 creation: usize,
656 previous: Option<usize>,
657 committed: bool,
658}
659
660#[derive(Clone, Debug)]
661struct CreatedAccountsSnapshot {
662 fork_id: Option<LocalForkId>,
663 bindings: AddressHashMap<usize>,
664}
665
666#[derive(Clone, Debug)]
684pub struct Cheatcodes<FEN: FoundryEvmNetwork = EthEvmNetwork> {
685 pub analysis: Option<CheatcodeAnalysis>,
687
688 pub block: Option<BlockEnvFor<FEN>>,
693
694 pub fork_block_number_override: Option<u64>,
698
699 pub active_delegations: Vec<SignedAuthorization>,
703
704 pub active_blob_sidecar: Option<BlobTransactionSidecarVariant>,
706
707 pub gas_price: Option<u128>,
712
713 pub labels: AddressHashMap<String>,
715
716 pub pranks: BTreeMap<usize, Prank>,
718
719 pub expected_revert: Option<ExpectedRevert>,
721
722 pub assume_no_revert: Option<AssumeNoRevert>,
724
725 pub fork_revert_diagnostic: Option<RevertDiagnostic>,
727
728 pub accesses: RecordAccess,
730
731 pub recording_accesses: bool,
733
734 pub recorded_account_diffs_stack: Option<Vec<Vec<AccountAccess>>>,
740
741 pending_account_diffs: Option<Arc<[AccountAccess]>>,
743
744 recorded_account_diffs_prefix: Option<Arc<[AccountAccess]>>,
746
747 created_accounts: Vec<Address>,
749
750 created_account_bindings: HashMap<(Option<LocalForkId>, Address), usize>,
752
753 created_account_changes: Vec<CreatedAccountChange>,
755
756 created_accounts_frames: Vec<CreatedAccountsFrame>,
758
759 created_accounts_snapshots: HashMap<U256, CreatedAccountsSnapshot>,
761
762 pub record_debug_steps_info: Option<RecordDebugStepInfo>,
764
765 pub recorded_logs: Option<Vec<crate::Vm::Log>>,
767
768 pub mocked_calls: HashMap<Address, BTreeMap<MockCallDataContext, VecDeque<MockCallReturnData>>>,
771
772 pub mocked_functions: HashMap<Address, HashMap<Bytes, Address>>,
774
775 pub expected_calls: ExpectedCallTracker,
777 pub expected_emits: ExpectedEmitTracker,
779 pub expected_creates: Vec<ExpectedCreate>,
781
782 pub allowed_mem_writes: HashMap<u64, Vec<Range<u64>>>,
784
785 pub broadcast: Option<Broadcast>,
787
788 pub broadcastable_transactions: BroadcastableTransactions<FEN::Network>,
790
791 pub access_list: Option<AccessList>,
793
794 pub config: Arc<CheatsConfig>,
796
797 pub extra_cheatcode_addresses: &'static [Address],
799
800 pub test_context: TestContext,
802
803 pub skip_payloads: Vec<Bytes>,
808
809 pub fs_commit: bool,
812
813 pub serialized_jsons: BTreeMap<String, BTreeMap<String, Value>>,
816
817 pub eth_deals: Vec<DealRecord>,
819
820 pub gas_metering: GasMetering,
822
823 pub gas_snapshots: BTreeMap<String, BTreeMap<String, String>>,
826
827 pub mapping_slots: Option<AddressHashMap<MappingSlots>>,
829
830 pub pc: usize,
832 pub breakpoints: Breakpoints,
835
836 pub intercept_next_create_call: bool,
838
839 test_runner: Option<TestRunner>,
842
843 pub ignored_traces: IgnoredTraces,
845
846 pub arbitrary_storage: Option<ArbitraryStorage>,
848
849 storage_load_hooks: AddressHashMap<StorageHook>,
851 storage_store_hooks: AddressHashMap<StorageHook>,
853 mapping_storage_store_hooks: AddressHashMap<HashMap<B256, StorageHook>>,
855 storage_hook_mapping_slots: AddressHashMap<MappingSlots>,
857 pending_mapping_hash: Option<PendingMappingHash>,
859 storage_hooks_registered: bool,
861 pending_storage_hook: Option<PendingStorageHook>,
863 active_storage_hook: Option<ActiveStorageHook>,
865
866 pub deprecated: HashMap<&'static str, Option<&'static str>>,
868 pub script_address: Option<Address>,
870 pub wallets: Option<Wallets>,
872 pub private_key_signers: HashMap<U256, PrivateKeySigner>,
874 signatures_identifier: OnceLock<Option<SignaturesIdentifier>>,
876 pub dynamic_gas_limit: bool,
878 pub execution_evm_version: Option<SpecFor<FEN>>,
880
881 pub env_overrides: HashMap<Option<LocalForkId>, EnvOverrides>,
891
892 pub env_overrides_snapshots: HashMap<U256, HashMap<Option<LocalForkId>, EnvOverrides>>,
902
903 pub fork_block_number_override_snapshots: HashMap<U256, Option<u64>>,
905
906 #[cfg(feature = "monad")]
909 pub context_snapshots:
910 HashMap<U256, (ChainFor<FEN>, monad_revm::reserve_balance::tracker::ReserveBalanceTracker)>,
911
912 pub in_isolation_context: bool,
922}
923
924impl Default for Cheatcodes {
928 fn default() -> Self {
929 Self::new(Arc::default())
930 }
931}
932
933impl<FEN: FoundryEvmNetwork> Cheatcodes<FEN> {
934 pub fn new(config: Arc<CheatsConfig>) -> Self {
936 Self {
937 analysis: None,
938 fs_commit: true,
939 labels: config.labels.clone(),
940 config,
941 extra_cheatcode_addresses: &[],
942 block: Default::default(),
943 fork_block_number_override: Default::default(),
944 active_delegations: Default::default(),
945 active_blob_sidecar: Default::default(),
946 gas_price: Default::default(),
947 pranks: Default::default(),
948 expected_revert: Default::default(),
949 assume_no_revert: Default::default(),
950 fork_revert_diagnostic: Default::default(),
951 accesses: Default::default(),
952 recording_accesses: Default::default(),
953 recorded_account_diffs_stack: Default::default(),
954 pending_account_diffs: Default::default(),
955 recorded_account_diffs_prefix: Default::default(),
956 created_accounts: Default::default(),
957 created_account_bindings: Default::default(),
958 created_account_changes: Default::default(),
959 created_accounts_frames: Default::default(),
960 created_accounts_snapshots: Default::default(),
961 recorded_logs: Default::default(),
962 record_debug_steps_info: Default::default(),
963 mocked_calls: Default::default(),
964 mocked_functions: Default::default(),
965 expected_calls: Default::default(),
966 expected_emits: Default::default(),
967 expected_creates: Default::default(),
968 allowed_mem_writes: Default::default(),
969 broadcast: Default::default(),
970 broadcastable_transactions: Default::default(),
971 access_list: Default::default(),
972 test_context: Default::default(),
973 skip_payloads: Default::default(),
974 serialized_jsons: Default::default(),
975 eth_deals: Default::default(),
976 gas_metering: Default::default(),
977 gas_snapshots: Default::default(),
978 mapping_slots: Default::default(),
979 pc: Default::default(),
980 breakpoints: Default::default(),
981 intercept_next_create_call: Default::default(),
982 test_runner: Default::default(),
983 ignored_traces: Default::default(),
984 arbitrary_storage: Default::default(),
985 storage_load_hooks: Default::default(),
986 storage_store_hooks: Default::default(),
987 mapping_storage_store_hooks: Default::default(),
988 storage_hook_mapping_slots: Default::default(),
989 pending_mapping_hash: Default::default(),
990 storage_hooks_registered: Default::default(),
991 pending_storage_hook: Default::default(),
992 active_storage_hook: Default::default(),
993 deprecated: Default::default(),
994 script_address: Default::default(),
995 wallets: Default::default(),
996 private_key_signers: Default::default(),
997 signatures_identifier: Default::default(),
998 dynamic_gas_limit: Default::default(),
999 execution_evm_version: None,
1000 env_overrides: Default::default(),
1001 env_overrides_snapshots: Default::default(),
1002 fork_block_number_override_snapshots: Default::default(),
1003 #[cfg(feature = "monad")]
1004 context_snapshots: Default::default(),
1005 in_isolation_context: false,
1006 }
1007 }
1008
1009 #[inline]
1011 pub const fn set_extra_cheatcode_addresses(&mut self, addresses: &'static [Address]) {
1012 self.extra_cheatcode_addresses = addresses;
1013 }
1014
1015 pub fn set_analysis(&mut self, analysis: CheatcodeAnalysis) {
1017 self.analysis = Some(analysis);
1018 }
1019
1020 pub fn start_internal_state_diff_recording(&mut self) -> bool {
1022 if self.recorded_account_diffs_stack.is_some()
1023 || self.recorded_account_diffs_prefix.is_some()
1024 {
1025 return false;
1026 }
1027 self.recorded_account_diffs_stack = Some(Default::default());
1028 true
1029 }
1030
1031 pub fn stop_internal_state_diff_recording(&mut self) -> Vec<AccountAccess> {
1033 self.recorded_account_diffs_stack.take().unwrap_or_default().into_iter().flatten().collect()
1034 }
1035
1036 pub fn set_pending_account_diffs(&mut self, accesses: Vec<AccountAccess>) {
1038 self.pending_account_diffs = (!accesses.is_empty()).then(|| Arc::from(accesses));
1039 }
1040
1041 pub fn start_state_diff_recording(&mut self) {
1043 self.recorded_account_diffs_prefix = self.pending_account_diffs.take();
1044 self.recorded_account_diffs_stack = Some(Default::default());
1045 }
1046
1047 pub fn recorded_account_diffs(&self) -> impl Iterator<Item = &AccountAccess> {
1049 self.recorded_account_diffs_prefix
1050 .iter()
1051 .flat_map(|prefix| prefix.iter())
1052 .chain(self.recorded_account_diffs_stack.iter().flatten().flatten())
1053 }
1054
1055 pub fn take_recorded_account_diffs_prefix(&mut self) -> Vec<AccountAccess> {
1057 self.recorded_account_diffs_prefix
1058 .take()
1059 .map(|prefix| prefix.as_ref().to_vec())
1060 .unwrap_or_default()
1061 }
1062
1063 pub(crate) fn created_account_bindings(
1065 &self,
1066 fork_id: Option<LocalForkId>,
1067 ) -> AddressHashMap<usize> {
1068 self.created_account_bindings
1069 .iter()
1070 .filter_map(|(&(event_fork_id, address), &creation)| {
1071 (event_fork_id == fork_id).then_some((address, creation))
1072 })
1073 .collect()
1074 }
1075
1076 pub(crate) fn created_accounts(&self, fork_id: Option<LocalForkId>) -> Vec<Address> {
1078 let bindings = self.created_account_bindings(fork_id);
1079 self.created_accounts
1080 .iter()
1081 .enumerate()
1082 .filter_map(|(index, &address)| {
1083 (bindings.get(&address) == Some(&index)).then_some(address)
1084 })
1085 .collect()
1086 }
1087
1088 pub(crate) fn record_created_account(
1090 &mut self,
1091 fork_id: Option<LocalForkId>,
1092 address: Address,
1093 ) {
1094 let creation = self.created_accounts.len();
1095 self.created_accounts.push(address);
1096 let previous = self.created_account_bindings.insert((fork_id, address), creation);
1097 self.created_account_changes.push(CreatedAccountChange {
1098 fork_id,
1099 address,
1100 creation,
1101 previous,
1102 committed: false,
1103 });
1104 }
1105
1106 pub(crate) fn commit_created_account_changes(&mut self, fork_id: Option<LocalForkId>) {
1108 for change in &mut self.created_account_changes {
1109 if change.fork_id == fork_id {
1110 change.committed = true;
1111 }
1112 }
1113 }
1114
1115 pub(crate) fn record_initial_created_accounts(
1117 &mut self,
1118 fork_id: Option<LocalForkId>,
1119 accounts: impl IntoIterator<Item = (Address, usize)>,
1120 ) {
1121 for (address, creation) in accounts {
1122 self.created_account_bindings.entry((fork_id, address)).or_insert(creation);
1123 }
1124 }
1125
1126 pub(crate) fn record_propagated_accounts(
1128 &mut self,
1129 fork_id: Option<LocalForkId>,
1130 accounts: impl IntoIterator<Item = (Address, usize)>,
1131 ) {
1132 self.created_account_bindings
1133 .extend(accounts.into_iter().map(|(address, creation)| ((fork_id, address), creation)));
1134 }
1135
1136 pub(crate) fn snapshot_created_accounts(
1138 &mut self,
1139 snapshot_id: U256,
1140 fork_id: Option<LocalForkId>,
1141 ) {
1142 let bindings = self.created_account_bindings(fork_id);
1143 self.created_accounts_snapshots
1144 .insert(snapshot_id, CreatedAccountsSnapshot { fork_id, bindings });
1145 }
1146
1147 pub(crate) fn revert_created_accounts(&mut self, snapshot_id: U256, remove: bool) {
1149 let snapshot = if remove {
1150 self.created_accounts_snapshots.remove(&snapshot_id)
1151 } else {
1152 self.created_accounts_snapshots.get(&snapshot_id).cloned()
1153 };
1154 if let Some(snapshot) = snapshot {
1155 self.created_account_bindings.retain(|(fork_id, _), _| *fork_id != snapshot.fork_id);
1156 self.created_account_bindings.extend(
1157 snapshot
1158 .bindings
1159 .into_iter()
1160 .map(|(address, creation)| ((snapshot.fork_id, address), creation)),
1161 );
1162 }
1163 }
1164
1165 pub(crate) fn delete_created_accounts_snapshot(&mut self, snapshot_id: U256) {
1167 self.created_accounts_snapshots.remove(&snapshot_id);
1168 }
1169
1170 pub(crate) fn clear_created_accounts_snapshots(&mut self) {
1172 self.created_accounts_snapshots.clear();
1173 }
1174
1175 fn start_created_accounts_frame(
1176 &mut self,
1177 reset: bool,
1178 kind: CreatedAccountsFrameKind,
1179 depth: usize,
1180 ) {
1181 if reset {
1182 self.created_accounts.clear();
1183 self.created_account_bindings.clear();
1184 self.created_account_changes.clear();
1185 self.created_accounts_frames.clear();
1186 for snapshot in self.created_accounts_snapshots.values_mut() {
1188 snapshot.bindings.clear();
1189 }
1190 }
1191 self.created_accounts_frames.push(CreatedAccountsFrame {
1192 kind,
1193 depth,
1194 checkpoint: self.created_account_changes.len(),
1195 });
1196 }
1197
1198 fn finish_created_accounts_frame(
1199 &mut self,
1200 success: bool,
1201 kind: CreatedAccountsFrameKind,
1202 depth: usize,
1203 ) {
1204 let Some(frame) = self
1205 .created_accounts_frames
1206 .last()
1207 .copied()
1208 .filter(|frame| frame.kind == kind && frame.depth == depth)
1209 else {
1210 return;
1211 };
1212 let checkpoint = frame.checkpoint;
1213 self.created_accounts_frames.pop();
1214 if !success {
1215 while self.created_account_changes.len() > checkpoint {
1216 let change = self.created_account_changes.pop().expect("length checked");
1217 if change.committed {
1218 continue;
1219 }
1220 let key = (change.fork_id, change.address);
1221 if self.created_account_bindings.get(&key) != Some(&change.creation) {
1222 continue;
1223 }
1224 if let Some(previous) = change.previous {
1225 self.created_account_bindings.insert(key, previous);
1226 } else {
1227 self.created_account_bindings.remove(&key);
1228 }
1229 }
1230 }
1231 }
1232
1233 pub fn env_overrides_for(&self, fork_id: Option<U256>) -> Option<&EnvOverrides> {
1235 self.env_overrides.get(&fork_id).filter(|o| o.is_any_set())
1236 }
1237
1238 pub fn env_overrides_for_mut(&mut self, fork_id: Option<U256>) -> &mut EnvOverrides {
1241 self.env_overrides.entry(fork_id).or_default()
1242 }
1243
1244 pub fn get_prank(&self, depth: usize) -> Option<&Prank> {
1248 self.pranks.range(..=depth).last().map(|(_, prank)| prank)
1249 }
1250
1251 pub fn wallets(&mut self) -> &Wallets {
1253 self.wallets.get_or_insert_with(|| Wallets::new(MultiWallet::default(), None))
1254 }
1255
1256 pub fn set_wallets(&mut self, wallets: Wallets) {
1258 self.wallets = Some(wallets);
1259 }
1260
1261 pub fn add_delegation(&mut self, authorization: SignedAuthorization) {
1263 self.active_delegations.push(authorization);
1264 }
1265
1266 pub fn signatures_identifier(&self) -> Option<&SignaturesIdentifier> {
1268 self.signatures_identifier
1269 .get_or_init(|| {
1270 if let Some(artifacts) = &self.config.available_artifacts {
1271 return SignaturesIdentifier::new_offline_with_abis(
1272 artifacts.values().map(|contract| &contract.abi),
1273 )
1274 .ok();
1275 }
1276 SignaturesIdentifier::new(true).ok()
1277 })
1278 .as_ref()
1279 }
1280
1281 fn apply_cheatcode(
1283 &mut self,
1284 ecx: &mut FoundryContextFor<'_, FEN>,
1285 call: &CallInputs,
1286 executor: &mut dyn CheatcodesExecutor<FEN>,
1287 ) -> Result {
1288 let decoded = Vm::VmCalls::abi_decode(&call.input.bytes(ecx)).map_err(|e| {
1290 if let alloy_sol_types::Error::UnknownSelector { name: _, selector } = e {
1291 let msg = format!(
1292 "unknown cheatcode with selector {selector}; \
1293 you may have a mismatch between the `Vm` interface (likely in `forge-std`) \
1294 and the `forge` version"
1295 );
1296 return alloy_sol_types::Error::Other(std::borrow::Cow::Owned(msg));
1297 }
1298 e
1299 })?;
1300
1301 let caller = call.caller;
1302
1303 ecx.db_mut().ensure_cheatcode_access_forking_mode(&caller)?;
1306
1307 apply_dispatch(
1308 &decoded,
1309 &mut CheatsCtxt { state: self, ecx, gas_limit: call.gas_limit, caller },
1310 executor,
1311 )
1312 }
1313
1314 #[cfg(feature = "monad")]
1316 fn apply_monad_cheatcode(
1317 &mut self,
1318 ecx: &mut FoundryContextFor<'_, FEN>,
1319 call: &CallInputs,
1320 ) -> Result {
1321 let input = call.input.bytes(ecx);
1322 let caller = call.caller;
1323
1324 ecx.db_mut().ensure_cheatcode_access_forking_mode(&caller)?;
1327
1328 crate::monad::apply_monad_cheatcode(
1329 &mut CheatsCtxt { state: self, ecx, gas_limit: call.gas_limit, caller },
1330 &input,
1331 )
1332 }
1333
1334 fn allow_cheatcodes_on_create(
1340 &self,
1341 ecx: &mut FoundryContextFor<FEN>,
1342 caller: Address,
1343 created_address: Address,
1344 ) {
1345 if ecx.journal().depth() <= 1 || ecx.db().has_cheatcode_access(&caller) {
1346 ecx.db_mut().allow_cheatcode_access(created_address);
1347 }
1348 }
1349
1350 fn apply_accesslist(&mut self, ecx: &mut FoundryContextFor<FEN>) {
1356 if let Some(access_list) = &self.access_list {
1357 ecx.tx_mut().set_access_list(access_list.clone());
1358
1359 if ecx.tx().tx_type() == TransactionType::Legacy as u8 {
1360 ecx.tx_mut().set_tx_type(TransactionType::Eip2930 as u8);
1361 }
1362 }
1363 }
1364
1365 pub fn on_revert(&mut self, ecx: &mut FoundryContextFor<FEN>) {
1370 trace!(deals=?self.eth_deals.len(), "rolling back deals");
1371
1372 if self.expected_revert.is_some() {
1374 return;
1375 }
1376
1377 if ecx.journal().depth() > 0 {
1379 return;
1380 }
1381
1382 while let Some(record) = self.eth_deals.pop() {
1386 if let Some(acc) = ecx.journal_mut().evm_state_mut().get_mut(&record.address) {
1387 acc.info.balance = record.old_balance;
1388 }
1389 }
1390 }
1391
1392 pub fn call_with_executor(
1397 &mut self,
1398 ecx: &mut FoundryContextFor<'_, FEN>,
1399 call: &mut CallInputs,
1400 executor: &mut dyn CheatcodesExecutor<FEN>,
1401 isolate_call: bool,
1402 ) -> Option<CallOutcome> {
1403 if let Some(spec_id) = self.execution_evm_version {
1405 ecx.set_spec_and_gas_params(spec_id);
1406 }
1407
1408 let gas = Gas::new(call.gas_limit);
1409 let curr_depth = ecx.journal().depth();
1410 self.start_created_accounts_frame(
1411 curr_depth == 0,
1412 CreatedAccountsFrameKind::Call,
1413 curr_depth,
1414 );
1415
1416 if curr_depth == 0 {
1420 let sender = ecx.tx().caller();
1421 let account = match super::evm::journaled_account(ecx, sender) {
1422 Ok(account) => account,
1423 Err(err) => {
1424 return Some(CallOutcome {
1425 result: InterpreterResult {
1426 result: InstructionResult::Revert,
1427 output: err.abi_encode().into(),
1428 gas,
1429 },
1430 memory_offset: call.return_memory_offset.clone(),
1431 was_precompile_called: false,
1432 precompile_call_logs: vec![],
1433 charged_new_account_state_gas: call.charged_new_account_state_gas,
1434 });
1435 }
1436 };
1437 let prev = account.info.nonce;
1438 account.info.nonce = prev.saturating_sub(1);
1439
1440 trace!(target: "cheatcodes", %sender, nonce=account.info.nonce, prev, "corrected nonce");
1441 }
1442
1443 if call.target_address == CHEATCODE_ADDRESS {
1444 return match self.apply_cheatcode(ecx, call, executor) {
1445 Ok(retdata) => Some(CallOutcome {
1446 result: InterpreterResult {
1447 result: InstructionResult::Return,
1448 output: retdata.into(),
1449 gas,
1450 },
1451 memory_offset: call.return_memory_offset.clone(),
1452 was_precompile_called: true,
1453 precompile_call_logs: vec![],
1454 charged_new_account_state_gas: call.charged_new_account_state_gas,
1455 }),
1456 Err(err) => Some(CallOutcome {
1457 result: InterpreterResult {
1458 result: InstructionResult::Revert,
1459 output: err.abi_encode().into(),
1460 gas,
1461 },
1462 memory_offset: call.return_memory_offset.clone(),
1463 was_precompile_called: false,
1464 precompile_call_logs: vec![],
1465 charged_new_account_state_gas: call.charged_new_account_state_gas,
1466 }),
1467 };
1468 }
1469
1470 #[cfg(feature = "monad")]
1471 if crate::monad::is_monad_cheatcode_call(
1472 self.extra_cheatcode_addresses,
1473 call.target_address,
1474 ) {
1475 let checkpoint = ecx.journal_mut().checkpoint();
1476 return match self.apply_monad_cheatcode(ecx, call) {
1477 Ok(retdata) => {
1478 ecx.journal_mut().checkpoint_commit();
1479 Some(CallOutcome {
1480 result: InterpreterResult {
1481 result: InstructionResult::Return,
1482 output: retdata.into(),
1483 gas,
1484 },
1485 memory_offset: call.return_memory_offset.clone(),
1486 was_precompile_called: true,
1487 precompile_call_logs: vec![],
1488 charged_new_account_state_gas: call.charged_new_account_state_gas,
1489 })
1490 }
1491 Err(err) => {
1492 ecx.journal_mut().checkpoint_revert(checkpoint);
1493 Some(CallOutcome {
1494 result: InterpreterResult {
1495 result: InstructionResult::Revert,
1496 output: err.abi_encode().into(),
1497 gas,
1498 },
1499 memory_offset: call.return_memory_offset.clone(),
1500 was_precompile_called: false,
1501 precompile_call_logs: vec![],
1502 charged_new_account_state_gas: call.charged_new_account_state_gas,
1503 })
1504 }
1505 };
1506 }
1507
1508 if call.target_address == HARDHAT_CONSOLE_ADDRESS {
1509 return None;
1510 }
1511
1512 if let Some(expected) = &mut self.expected_revert {
1516 expected.max_depth = max(curr_depth + 1, expected.max_depth);
1517 }
1518
1519 if let Some(expected_calls_for_target) = self.expected_calls.get_mut(&call.bytecode_address)
1523 {
1524 let input = call.input.as_bytes(ecx);
1525 let value = call.transfer_value();
1526
1527 for ((calldata, expected_scheme), (expected, actual_count)) in expected_calls_for_target
1529 {
1530 if calldata.len() <= input.len() &&
1533 input.get(..calldata.len()) == Some(calldata.as_ref()) &&
1535 expected.value.is_none_or(|expected_value| Some(expected_value) == value) &&
1537 expected.gas.is_none_or(|gas| gas == call.gas_limit) &&
1539 expected.min_gas.is_none_or(|min_gas| min_gas <= call.gas_limit) &&
1541 expected_scheme.is_none_or(|scheme| scheme == call.scheme)
1543 {
1544 *actual_count += 1;
1545 }
1546 }
1547 }
1548
1549 if let Some(prank) = &self.get_prank(curr_depth) {
1551 if prank.delegate_call
1553 && curr_depth == prank.depth
1554 && call.scheme == CallScheme::DelegateCall
1555 {
1556 call.target_address = prank.new_caller;
1557 call.caller = prank.new_caller;
1558 if let Some(new_origin) = prank.new_origin {
1559 ecx.tx_mut().set_caller(new_origin);
1560 }
1561 }
1562
1563 if curr_depth >= prank.depth && call.caller == prank.prank_caller {
1564 let prank_applied = if curr_depth == prank.depth {
1566 let _ = journaled_account(ecx, prank.new_caller);
1568 call.caller = prank.new_caller;
1569 true
1570 } else {
1571 false
1572 };
1573
1574 let prank_applied = if let Some(new_origin) = prank.new_origin {
1576 ecx.tx_mut().set_caller(new_origin);
1577 true
1578 } else {
1579 prank_applied
1580 };
1581
1582 if prank_applied && let Some(applied_prank) = prank.first_time_applied() {
1584 self.pranks.insert(curr_depth, applied_prank);
1585 }
1586 }
1587 }
1588
1589 if let Some(mocks) = self.mocked_calls.get_mut(&call.bytecode_address) {
1591 let input = call.input.bytes(ecx);
1592 let value = call.transfer_value();
1593 let ctx = MockCallDataContext { calldata: input.clone(), value };
1594
1595 if let Some(return_data_queue) = match mocks.get_mut(&ctx) {
1596 Some(queue) => Some(queue),
1597 None => mocks
1598 .iter_mut()
1599 .find(|(mock, _)| {
1600 input.get(..mock.calldata.len()) == Some(&mock.calldata[..])
1601 && mock.value.is_none_or(|mock_value| Some(mock_value) == value)
1602 })
1603 .map(|(_, v)| v),
1604 } && let Some(return_data) = return_data_queue.front().map(|x| x.to_owned())
1605 {
1606 if let Some(value) = call.transfer_value() {
1607 let checkpoint = ecx.journal_mut().checkpoint();
1608 match ecx.journal_mut().transfer_loaded(
1609 call.transfer_from(),
1610 call.transfer_to(),
1611 value,
1612 ) {
1613 None => {
1614 if return_data.ret_type.is_ok() {
1615 ecx.journal_mut().checkpoint_commit();
1616 } else {
1617 ecx.journal_mut().checkpoint_revert(checkpoint);
1618 }
1619 }
1620 Some(err) => {
1621 ecx.journal_mut().checkpoint_revert(checkpoint);
1622 return Some(CallOutcome {
1623 result: InterpreterResult {
1624 result: err.into(),
1625 output: Bytes::new(),
1626 gas,
1627 },
1628 memory_offset: call.return_memory_offset.clone(),
1629 was_precompile_called: false,
1630 precompile_call_logs: vec![],
1631 charged_new_account_state_gas: call.charged_new_account_state_gas,
1632 });
1633 }
1634 }
1635 }
1636
1637 if return_data_queue.len() > 1 {
1639 return_data_queue.pop_front();
1640 }
1641
1642 return Some(CallOutcome {
1643 result: InterpreterResult {
1644 result: return_data.ret_type,
1645 output: return_data.data,
1646 gas,
1647 },
1648 memory_offset: call.return_memory_offset.clone(),
1649 was_precompile_called: true,
1650 precompile_call_logs: vec![],
1651 charged_new_account_state_gas: call.charged_new_account_state_gas,
1652 });
1653 }
1654 }
1655
1656 self.apply_accesslist(ecx);
1658
1659 if let Some(broadcast) = &self.broadcast {
1661 let is_fixed_gas_limit = call.gas_limit >= 21_000 && !self.dynamic_gas_limit;
1664 self.dynamic_gas_limit = false;
1665
1666 if curr_depth == broadcast.depth && call.caller == broadcast.original_caller {
1671 ecx.tx_mut().set_caller(broadcast.new_origin);
1675
1676 call.caller = broadcast.new_origin;
1677 if !call.is_static {
1682 if let Err(err) = ecx.journal_mut().load_account(broadcast.new_origin) {
1683 return Some(CallOutcome {
1684 result: InterpreterResult {
1685 result: InstructionResult::Revert,
1686 output: Error::encode(err),
1687 gas,
1688 },
1689 memory_offset: call.return_memory_offset.clone(),
1690 was_precompile_called: false,
1691 precompile_call_logs: vec![],
1692 charged_new_account_state_gas: call.charged_new_account_state_gas,
1693 });
1694 }
1695
1696 let input = call.input.bytes(ecx);
1697 let chain_id = ecx.cfg().chain_id();
1698 let rpc = ecx.db().active_fork_url();
1699 let fee_token = ecx.tx().fee_token();
1700 let account =
1701 ecx.journal_mut().evm_state_mut().get_mut(&broadcast.new_origin).unwrap();
1702
1703 let mut tx_req = TransactionRequestFor::<FEN>::default()
1704 .with_from(broadcast.new_origin)
1705 .with_to(call.target_address)
1706 .with_value(call.transfer_value().unwrap_or_default())
1707 .with_input(input)
1708 .with_nonce(account.info.nonce)
1709 .with_chain_id(chain_id);
1710 if is_fixed_gas_limit {
1711 tx_req.set_gas_limit(call.gas_limit)
1712 }
1713
1714 let active_delegations = std::mem::take(&mut self.active_delegations);
1715 if let Some(blob_sidecar) = self.active_blob_sidecar.take() {
1717 if !active_delegations.is_empty() {
1719 let msg = "both delegation and blob are active; `attachBlob` and `attachDelegation` are not compatible";
1720 return Some(CallOutcome {
1721 result: InterpreterResult {
1722 result: InstructionResult::Revert,
1723 output: Error::encode(msg),
1724 gas,
1725 },
1726 memory_offset: call.return_memory_offset.clone(),
1727 was_precompile_called: false,
1728 precompile_call_logs: vec![],
1729 charged_new_account_state_gas: call.charged_new_account_state_gas,
1730 });
1731 }
1732 tx_req.set_blob_sidecar(blob_sidecar);
1733 }
1734
1735 if !active_delegations.is_empty() {
1737 for auth in &active_delegations {
1738 let Ok(authority) = auth.recover_authority() else {
1739 continue;
1740 };
1741 if authority == broadcast.new_origin {
1742 account.info.nonce += 1;
1745 }
1746 }
1747 tx_req.set_authorization_list(active_delegations);
1748 }
1749 if let Some(fee_token) = fee_token {
1750 tx_req.set_fee_token(fee_token);
1751 }
1752 self.broadcastable_transactions.push_back(BroadcastableTransaction {
1753 rpc,
1754 transaction: TransactionMaybeSigned::new(tx_req),
1755 });
1756 debug!(target: "cheatcodes", tx=?self.broadcastable_transactions.back().unwrap(), "broadcastable call");
1757
1758 if !isolate_call {
1761 let prev = account.info.nonce;
1762 account.info.nonce += 1;
1763 debug!(target: "cheatcodes", address=%broadcast.new_origin, nonce=prev+1, prev, "incremented nonce");
1764 }
1765 } else if broadcast.single_call {
1766 let msg = "`staticcall`s are not allowed after `broadcast`; use `startBroadcast` instead";
1767 return Some(CallOutcome {
1768 result: InterpreterResult {
1769 result: InstructionResult::Revert,
1770 output: Error::encode(msg),
1771 gas,
1772 },
1773 memory_offset: call.return_memory_offset.clone(),
1774 was_precompile_called: false,
1775 precompile_call_logs: vec![],
1776 charged_new_account_state_gas: call.charged_new_account_state_gas,
1777 });
1778 }
1779 }
1780 }
1781
1782 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1784 let (initialized, old_balance, old_nonce) =
1787 if let Ok(acc) = ecx.journal_mut().load_account(call.target_address) {
1788 (acc.data.info.exists(), acc.data.info.balance, acc.data.info.nonce)
1789 } else {
1790 (false, U256::ZERO, 0)
1791 };
1792
1793 let kind = match call.scheme {
1794 CallScheme::Call => crate::Vm::AccountAccessKind::Call,
1795 CallScheme::CallCode => crate::Vm::AccountAccessKind::CallCode,
1796 CallScheme::DelegateCall => crate::Vm::AccountAccessKind::DelegateCall,
1797 CallScheme::StaticCall => crate::Vm::AccountAccessKind::StaticCall,
1798 };
1799
1800 recorded_account_diffs_stack.push(vec![AccountAccess {
1806 chainInfo: crate::Vm::ChainInfo {
1807 forkId: ecx.db().active_fork_id().unwrap_or_default(),
1808 chainId: U256::from(ecx.cfg().chain_id()),
1809 },
1810 accessor: call.caller,
1811 account: call.bytecode_address,
1812 kind,
1813 initialized,
1814 oldBalance: old_balance,
1815 newBalance: U256::ZERO, oldNonce: old_nonce,
1817 newNonce: 0, value: call.call_value(),
1819 data: call.input.bytes(ecx),
1820 reverted: false,
1821 deployedCode: Bytes::new(),
1822 storageAccesses: vec![], depth: ecx.journal().depth().try_into().expect("journaled state depth exceeds u64"),
1824 }]);
1825 }
1826
1827 None
1828 }
1829
1830 pub fn rng(&mut self) -> &mut impl Rng {
1831 self.test_runner().rng()
1832 }
1833
1834 pub fn test_runner(&mut self) -> &mut TestRunner {
1835 self.test_runner.get_or_insert_with(|| match self.config.seed {
1836 Some(seed) => TestRunner::new_with_rng(
1837 proptest::test_runner::Config::default(),
1838 TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>()),
1839 ),
1840 None => TestRunner::new(proptest::test_runner::Config::default()),
1841 })
1842 }
1843
1844 pub fn set_seed(&mut self, seed: U256) {
1845 self.test_runner = Some(TestRunner::new_with_rng(
1846 proptest::test_runner::Config::default(),
1847 TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>()),
1848 ));
1849 }
1850
1851 pub fn arbitrary_storage(&mut self) -> &mut ArbitraryStorage {
1854 self.arbitrary_storage.get_or_insert_with(ArbitraryStorage::default)
1855 }
1856
1857 pub fn arbitrary_storage_targets(&self) -> impl Iterator<Item = Address> + '_ {
1859 self.arbitrary_storage.as_ref().into_iter().flat_map(ArbitraryStorage::targets)
1860 }
1861
1862 pub fn arbitrary_storage_target_overwrite_modes(
1865 &self,
1866 ) -> impl Iterator<Item = (Address, bool)> + '_ {
1867 self.arbitrary_storage
1868 .as_ref()
1869 .into_iter()
1870 .flat_map(ArbitraryStorage::target_overwrite_modes)
1871 }
1872
1873 pub fn arbitrary_storage_copied_targets(&self) -> impl Iterator<Item = Address> + '_ {
1875 self.arbitrary_storage.as_ref().into_iter().flat_map(ArbitraryStorage::copied_targets)
1876 }
1877
1878 pub fn arbitrary_storage_copied_target_sources(
1880 &self,
1881 ) -> impl Iterator<Item = (Address, Address)> + '_ {
1882 self.arbitrary_storage
1883 .as_ref()
1884 .into_iter()
1885 .flat_map(ArbitraryStorage::copied_target_sources)
1886 }
1887
1888 pub fn cache_arbitrary_storage_value(&mut self, address: Address, slot: U256, value: U256) {
1890 if let Some(storage) = &mut self.arbitrary_storage {
1891 storage.cache_value(address, slot, value);
1892 }
1893 }
1894
1895 pub fn mark_arbitrary_storage_slot_explicit(&mut self, address: Address, slot: U256) {
1897 if let Some(storage) = &mut self.arbitrary_storage {
1898 storage.mark_explicit(address, slot);
1899 }
1900 }
1901
1902 pub fn is_arbitrary_storage_slot_explicit(&self, address: Address, slot: U256) -> bool {
1904 self.arbitrary_storage.as_ref().is_some_and(|storage| storage.is_explicit(address, slot))
1905 }
1906
1907 pub fn cached_arbitrary_storage_value(&self, address: Address, slot: U256) -> Option<U256> {
1909 self.arbitrary_storage.as_ref().and_then(|storage| storage.cached_value(address, slot))
1910 }
1911
1912 pub fn has_arbitrary_storage(&self, address: &Address) -> bool {
1914 match &self.arbitrary_storage {
1915 Some(storage) => storage.values.contains_key(address),
1916 None => false,
1917 }
1918 }
1919
1920 pub fn should_overwrite_arbitrary_storage(
1924 &self,
1925 address: &Address,
1926 storage_slot: U256,
1927 ) -> bool {
1928 match &self.arbitrary_storage {
1929 Some(storage) => {
1930 storage.overwrites.contains(address)
1931 && storage
1932 .values
1933 .get(address)
1934 .and_then(|arbitrary_values| arbitrary_values.get(&storage_slot))
1935 .is_none()
1936 }
1937 None => false,
1938 }
1939 }
1940
1941 pub fn is_arbitrary_storage_copy(&self, address: &Address) -> bool {
1943 match &self.arbitrary_storage {
1944 Some(storage) => storage.copies.contains_key(address),
1945 None => false,
1946 }
1947 }
1948
1949 pub fn register_storage_load_hook(
1951 &mut self,
1952 target: Address,
1953 callback_target: Address,
1954 callback_selector: [u8; 4],
1955 ) {
1956 self.storage_load_hooks.insert(target, StorageHook { callback_target, callback_selector });
1957 self.storage_hooks_registered = true;
1958 }
1959
1960 pub fn register_storage_store_hook(
1962 &mut self,
1963 target: Address,
1964 callback_target: Address,
1965 callback_selector: [u8; 4],
1966 ) {
1967 self.storage_store_hooks.insert(target, StorageHook { callback_target, callback_selector });
1968 self.storage_hooks_registered = true;
1969 }
1970
1971 pub fn register_mapping_storage_store_hook(
1973 &mut self,
1974 target: Address,
1975 root_slot: B256,
1976 callback_target: Address,
1977 callback_selector: [u8; 4],
1978 ) -> bool {
1979 if self.storage_store_hooks.contains_key(&target) {
1980 return false;
1981 }
1982 self.storage_hook_mapping_slots.remove(&target);
1983 self.mapping_storage_store_hooks
1984 .entry(target)
1985 .or_default()
1986 .insert(root_slot, StorageHook { callback_target, callback_selector });
1987 self.storage_hooks_registered = true;
1988 true
1989 }
1990
1991 pub fn mapping_storage_store_hooks(
1993 &self,
1994 ) -> impl Iterator<Item = (Address, B256, StorageHook)> + '_ {
1995 self.mapping_storage_store_hooks
1996 .iter()
1997 .flat_map(|(target, hooks)| hooks.iter().map(|(root, hook)| (*target, *root, *hook)))
1998 }
1999
2000 pub fn has_mapping_storage_store_hooks(&self, target: Address) -> bool {
2002 self.mapping_storage_store_hooks.get(&target).is_some_and(|hooks| !hooks.is_empty())
2003 }
2004
2005 pub fn storage_load_hooks(&self) -> impl Iterator<Item = (Address, StorageHook)> + '_ {
2007 self.storage_load_hooks.iter().map(|(target, hook)| (*target, *hook))
2008 }
2009
2010 pub fn storage_store_hooks(&self) -> impl Iterator<Item = (Address, StorageHook)> + '_ {
2012 self.storage_store_hooks.iter().map(|(target, hook)| (*target, *hook))
2013 }
2014
2015 #[inline]
2017 pub const fn has_storage_hooks(&self) -> bool {
2018 self.storage_hooks_registered
2019 }
2020
2021 pub fn clear_storage_hook_mapping_slots(&mut self) {
2023 self.storage_hook_mapping_slots.clear();
2024 }
2025
2026 #[inline]
2028 pub const fn is_storage_hook_active(&self) -> bool {
2029 self.active_storage_hook.is_some()
2030 }
2031
2032 pub fn is_storage_hook_callback(
2034 &self,
2035 ecx: &FoundryContextFor<'_, FEN>,
2036 call: &CallInputs,
2037 ) -> bool {
2038 self.active_storage_hook.as_ref().is_some_and(|active| {
2039 active.outcome.is_none()
2040 && ecx.journal().depth() == active.parent_depth
2041 && call.caller == CHEATCODE_ADDRESS
2042 && call.target_address == active.callback_target
2043 && call.input.bytes(ecx) == active.callback_input
2044 })
2045 }
2046
2047 fn finish_storage_hook_call(
2048 &mut self,
2049 ecx: &FoundryContextFor<'_, FEN>,
2050 call: &CallInputs,
2051 outcome: &CallOutcome,
2052 ) -> bool {
2053 let Some(active) = self.active_storage_hook.as_mut() else { return false };
2054 if active.outcome.is_some()
2055 || ecx.journal().depth() != active.parent_depth
2056 || call.caller != CHEATCODE_ADDRESS
2057 || call.target_address != active.callback_target
2058 || call.input.bytes(ecx) != active.callback_input
2059 {
2060 return false;
2061 }
2062 active.outcome = Some((outcome.result.result, outcome.result.output.clone()));
2063 true
2064 }
2065
2066 #[inline(always)]
2067 pub fn has_step_hooks(&self) -> bool {
2068 self.broadcast.is_some()
2069 || self.gas_metering.paused
2070 || self.gas_metering.reset
2071 || self.recording_accesses
2072 || self.recorded_account_diffs_stack.is_some()
2073 || !self.allowed_mem_writes.is_empty()
2074 || self.mapping_slots.is_some()
2075 || self.gas_metering.recording
2076 || self.has_active_env_overrides()
2077 || self.has_storage_hooks()
2078 }
2079
2080 #[inline(always)]
2081 pub fn has_step_end_hooks(&self) -> bool {
2082 self.gas_metering.paused
2083 || self.gas_metering.touched
2084 || self.arbitrary_storage.is_some()
2085 || self.mapping_slots.is_some()
2086 || self.has_active_env_overrides()
2087 || self.has_storage_hooks()
2088 }
2089
2090 #[inline(always)]
2091 pub fn has_log_hooks(&self) -> bool {
2092 !self.expected_emits.is_empty() || self.recorded_logs.is_some()
2093 }
2094
2095 #[inline(always)]
2096 pub fn has_recording_accesses_only_step_hook(&self) -> bool {
2097 self.recording_accesses
2098 && self.broadcast.is_none()
2099 && !self.gas_metering.paused
2100 && !self.gas_metering.reset
2101 && self.recorded_account_diffs_stack.is_none()
2102 && self.allowed_mem_writes.is_empty()
2103 && self.mapping_slots.is_none()
2104 && !self.has_storage_hooks()
2105 && !self.gas_metering.recording
2106 && !self.has_active_env_overrides()
2107 }
2108
2109 #[inline(always)]
2110 fn has_active_env_overrides(&self) -> bool {
2111 self.env_overrides.values().any(EnvOverrides::is_any_set)
2112 }
2113
2114 pub fn struct_defs(&self) -> Option<&foundry_common::fmt::StructDefinitions> {
2116 self.analysis.as_ref().and_then(|analysis| analysis.struct_defs().ok())
2117 }
2118}
2119
2120const fn frame_gas(result: &InterpreterResult) -> Vm::Gas {
2121 let gas = &result.gas;
2122 let regular_gas_spent = if result.is_halt() {
2124 gas.total_gas_spent()
2125 } else {
2126 gas.total_gas_spent().saturating_sub(gas.state_gas_spilled())
2127 };
2128 Vm::Gas {
2129 gasLimit: gas.limit(),
2130 gasTotalUsed: regular_gas_spent,
2131 gasMemoryUsed: 0,
2132 gasRefunded: gas.refunded(),
2133 gasRemaining: gas.remaining(),
2134 gasStateUsed: if result.is_ok() { gas.state_gas_spent() } else { 0 },
2135 }
2136}
2137
2138impl<FEN: FoundryEvmNetwork> Inspector<FoundryContextFor<'_, FEN>> for Cheatcodes<FEN> {
2139 fn initialize_interp(
2140 &mut self,
2141 interpreter: &mut Interpreter,
2142 ecx: &mut FoundryContextFor<'_, FEN>,
2143 ) {
2144 if let Some(block) = self.block.take() {
2147 ecx.set_block(block);
2148 }
2149 if let Some(gas_price) = self.gas_price.take() {
2150 ecx.tx_mut().set_gas_price(gas_price);
2151 }
2152
2153 if self.gas_metering.paused {
2155 self.gas_metering.paused_frames.push(interpreter.gas);
2156 }
2157
2158 if let Some(expected) = &mut self.expected_revert {
2160 expected.max_depth = max(ecx.journal().depth(), expected.max_depth);
2161 }
2162 }
2163
2164 fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut FoundryContextFor<'_, FEN>) {
2165 self.pc = interpreter.bytecode.pc();
2166
2167 if !self.has_step_hooks() {
2168 return;
2169 }
2170
2171 if self.finish_storage_hook_callback(interpreter, ecx) {
2172 return;
2173 }
2174
2175 if self.broadcast.is_some() {
2176 self.set_gas_limit_type(interpreter);
2177 }
2178
2179 if interpreter.bytecode.opcode() == op::CALLER
2182 && let Some(broadcast) = &self.broadcast
2183 && let Some(script_address) = self.script_address
2184 && ecx.journal().depth() == broadcast.depth
2185 && interpreter.input.target_address == script_address
2186 && interpreter.input.bytecode_address == Some(script_address)
2187 && interpreter.input.caller_address != broadcast.new_origin
2188 {
2189 interpreter.bytecode.set_action(InterpreterAction::new_return(
2190 InstructionResult::Revert,
2191 Bytes::from(
2192 format!(
2193 "Usage of `msg.sender` inside a `broadcast` in script contract detected. \
2194 `msg.sender` is `{:#x}`, not the broadcast sender `{:#x}`. \
2195 Use the `--sender` flag or pass the deployer address directly instead.",
2196 interpreter.input.caller_address, broadcast.new_origin,
2197 )
2198 .into_bytes(),
2199 ),
2200 interpreter.gas,
2201 ));
2202 return;
2203 }
2204
2205 if self.gas_metering.paused {
2207 self.meter_gas(interpreter);
2208 }
2209
2210 if self.gas_metering.reset {
2212 self.meter_gas_reset(interpreter);
2213 }
2214
2215 if self.recording_accesses {
2217 self.record_accesses(interpreter);
2218 }
2219
2220 if self.recorded_account_diffs_stack.is_some() {
2222 self.record_state_diffs(interpreter, ecx);
2223 }
2224
2225 if !self.allowed_mem_writes.is_empty() {
2227 self.check_mem_opcodes(
2228 interpreter,
2229 ecx.journal().depth().try_into().expect("journaled state depth exceeds u64"),
2230 );
2231 }
2232
2233 if self.mapping_slots.is_some() || !self.mapping_storage_store_hooks.is_empty() {
2234 if let Some(mapping_slots) = &mut self.mapping_slots {
2236 mapping_step(mapping_slots, interpreter);
2237 }
2238
2239 let account = interpreter.input.target_address;
2240 let mapping_hook_active = self.active_storage_hook.is_none()
2241 && self
2242 .mapping_storage_store_hooks
2243 .get(&account)
2244 .is_some_and(|hooks| !hooks.is_empty());
2245 if mapping_hook_active {
2246 mapping_step(&mut self.storage_hook_mapping_slots, interpreter);
2247 }
2248 self.pending_mapping_hash = if self.mapping_slots.is_some() || mapping_hook_active {
2249 capture_mapping_hash(interpreter)
2250 } else {
2251 None
2252 };
2253 }
2254
2255 if self.gas_metering.recording {
2257 self.meter_gas_record(interpreter, ecx);
2258 }
2259
2260 if !self.env_overrides.is_empty() {
2265 let fork_id = ecx.db().active_fork_id();
2266 if let Some(env_overrides) =
2267 self.env_overrides.get_mut(&fork_id).filter(|o| o.is_any_set())
2268 {
2269 env_overrides.pending_opcode = None;
2273 env_overrides.pending_blobhash_index = None;
2274
2275 let opcode = interpreter.bytecode.opcode();
2276 match opcode {
2277 op::BASEFEE | op::GASPRICE => {
2278 env_overrides.pending_opcode = Some(opcode);
2279 }
2280 op::BLOBHASH => {
2281 env_overrides.pending_opcode = Some(opcode);
2282 env_overrides.pending_blobhash_index =
2283 interpreter.stack.peek(0).ok().and_then(|index| index.try_into().ok());
2284 }
2285 _ => {}
2286 }
2287 }
2288 }
2289
2290 if self.active_storage_hook.is_none() {
2291 self.capture_storage_hook(interpreter, ecx);
2292 }
2293 }
2294
2295 fn step_end(&mut self, interpreter: &mut Interpreter, ecx: &mut FoundryContextFor<'_, FEN>) {
2296 if !self.has_step_end_hooks() {
2297 return;
2298 }
2299
2300 if self.gas_metering.paused {
2301 self.meter_gas_end(interpreter);
2302 }
2303
2304 if self.gas_metering.touched {
2305 self.meter_gas_check(interpreter);
2306 }
2307
2308 if self.arbitrary_storage.is_some() {
2310 self.arbitrary_storage_end(interpreter, ecx);
2311 }
2312
2313 if let Some(pending) = self.pending_mapping_hash.take()
2314 && interpreter
2315 .bytecode
2316 .action
2317 .as_ref()
2318 .and_then(InterpreterAction::instruction_result)
2319 .is_none()
2320 {
2321 if let Some(mapping_slots) = &mut self.mapping_slots {
2322 record_mapping_hash(mapping_slots, interpreter, pending);
2323 }
2324 if self
2325 .mapping_storage_store_hooks
2326 .get(&pending.address)
2327 .is_some_and(|hooks| !hooks.is_empty())
2328 && self.active_storage_hook.is_none()
2329 {
2330 record_mapping_hash(&mut self.storage_hook_mapping_slots, interpreter, pending);
2331 }
2332 }
2333
2334 if self.active_storage_hook.is_none() {
2335 self.invoke_pending_storage_hook(interpreter, ecx);
2336 }
2337
2338 if !self.env_overrides.is_empty() {
2348 let fork_id = ecx.db().active_fork_id();
2349 if self.env_overrides.get(&fork_id).is_some_and(|o| o.is_any_set()) {
2350 let opcode_failed = interpreter
2356 .bytecode
2357 .action
2358 .as_ref()
2359 .and_then(|a| a.instruction_result())
2360 .is_some();
2361 if opcode_failed {
2362 if let Some(env_overrides) = self.env_overrides.get_mut(&fork_id) {
2363 env_overrides.pending_opcode = None;
2364 env_overrides.pending_blobhash_index = None;
2365 }
2366 } else {
2367 self.apply_env_overrides(interpreter, fork_id);
2368 }
2369 }
2370 }
2371 }
2372
2373 fn log(&mut self, _ecx: &mut FoundryContextFor<'_, FEN>, log: Log) {
2374 if !self.expected_emits.is_empty()
2375 && let Some(err) = expect::handle_expect_emit(self, &log, None)
2376 {
2377 let _ = sh_err!("{err:?}");
2381 }
2382
2383 record_logs(&mut self.recorded_logs, &log);
2385 }
2386
2387 fn log_full(
2388 &mut self,
2389 interpreter: &mut Interpreter,
2390 _ecx: &mut FoundryContextFor<'_, FEN>,
2391 log: Log,
2392 ) {
2393 if !self.expected_emits.is_empty() {
2394 expect::handle_expect_emit(self, &log, Some(interpreter));
2395 }
2396
2397 record_logs(&mut self.recorded_logs, &log);
2399 }
2400
2401 fn call(
2402 &mut self,
2403 ecx: &mut FoundryContextFor<'_, FEN>,
2404 inputs: &mut CallInputs,
2405 ) -> Option<CallOutcome> {
2406 if self.is_storage_hook_callback(ecx, inputs) {
2407 return None;
2408 }
2409 Self::call_with_executor(self, ecx, inputs, &mut TransparentCheatcodesExecutor, false)
2410 }
2411
2412 fn call_end(
2413 &mut self,
2414 ecx: &mut FoundryContextFor<'_, FEN>,
2415 call: &CallInputs,
2416 outcome: &mut CallOutcome,
2417 ) {
2418 let isolated_snapshot_gas_used = self.gas_metering.isolated_snapshot_gas_used.take();
2419 self.gas_metering.record_isolated_refund(
2420 ecx.journal().depth(),
2421 &outcome.result.gas,
2422 isolated_snapshot_gas_used,
2423 );
2424 if self.finish_storage_hook_call(ecx, call, outcome) {
2425 return;
2426 }
2427
2428 let cheatcode_call = call.target_address == CHEATCODE_ADDRESS
2429 || call.target_address == HARDHAT_CONSOLE_ADDRESS;
2430 #[cfg(feature = "monad")]
2431 let cheatcode_call = cheatcode_call
2432 || crate::monad::is_monad_cheatcode_call(
2433 self.extra_cheatcode_addresses,
2434 call.target_address,
2435 );
2436 let curr_depth = ecx.journal().depth();
2437
2438 self.finish_created_accounts_frame(
2439 outcome.result.is_ok(),
2440 CreatedAccountsFrameKind::Call,
2441 curr_depth,
2442 );
2443
2444 if !cheatcode_call {
2448 if let Some(prank) = &self.get_prank(curr_depth)
2450 && curr_depth == prank.depth
2451 {
2452 ecx.tx_mut().set_caller(prank.prank_origin);
2453
2454 if prank.single_call {
2456 self.pranks.remove(&curr_depth);
2457 }
2458 }
2459
2460 if let Some(broadcast) = &self.broadcast
2462 && curr_depth == broadcast.depth
2463 {
2464 ecx.tx_mut().set_caller(broadcast.original_origin);
2465
2466 if broadcast.single_call {
2468 let _ = self.broadcast.take();
2469 }
2470 }
2471 }
2472
2473 if let Some(assume_no_revert) = &mut self.assume_no_revert {
2475 if outcome.result.is_revert() && assume_no_revert.reverted_by.is_none() {
2478 assume_no_revert.reverted_by = Some(call.target_address);
2479 }
2480
2481 let curr_depth = ecx.journal().depth();
2483 if curr_depth <= assume_no_revert.depth && !cheatcode_call {
2484 if outcome.result.is_revert() {
2487 let assume_no_revert = std::mem::take(&mut self.assume_no_revert).unwrap();
2488 return match revert_handlers::handle_assume_no_revert(
2489 &assume_no_revert,
2490 outcome.result.result,
2491 &outcome.result.output,
2492 &self.config.available_artifacts,
2493 ) {
2494 Ok(_) => {
2497 outcome.result.output = Error::from(MAGIC_ASSUME).abi_encode().into();
2498 }
2499 Err(error) => {
2502 trace!(expected=?assume_no_revert, ?error, status=?outcome.result.result, "Expected revert mismatch");
2503 outcome.result.result = InstructionResult::Revert;
2504 outcome.result.output = error.abi_encode().into();
2505 }
2506 };
2507 }
2508 self.assume_no_revert = None;
2510 }
2511 }
2512
2513 if let Some(expected_revert) = &mut self.expected_revert {
2515 let call_failed = !matches!(outcome.result.result, return_ok!());
2518 if call_failed {
2519 if expected_revert.reverter.is_some()
2523 && (expected_revert.reverted_by.is_none() || expected_revert.count > 1)
2524 {
2525 expected_revert.reverted_by = Some(call.target_address);
2526 }
2527 }
2528
2529 let curr_depth = ecx.journal().depth();
2530 if curr_depth <= expected_revert.depth {
2531 let internal = self.config.internal_expect_revert;
2536 let went_deeper = expected_revert.max_depth > expected_revert.depth;
2537 let needs_processing = match expected_revert.kind {
2538 ExpectedRevertKind::Default => (|| {
2539 if cheatcode_call {
2541 return false;
2542 }
2543 if call_failed {
2545 return true;
2546 }
2547 if !internal && went_deeper {
2549 return true;
2550 }
2551 if curr_depth == 0 {
2553 return true;
2554 }
2555 !internal
2558 })(),
2559 ExpectedRevertKind::Cheatcode { pending_processing } => {
2562 cheatcode_call && !pending_processing
2563 }
2564 };
2565
2566 if needs_processing {
2567 let mut expected_revert = std::mem::take(&mut self.expected_revert).unwrap();
2568 let clear_last_frame_gas =
2569 matches!(expected_revert.kind, ExpectedRevertKind::Default);
2570 return match revert_handlers::handle_expect_revert(
2571 cheatcode_call,
2572 false,
2573 self.config.internal_expect_revert,
2574 &expected_revert,
2575 outcome.result.result,
2576 outcome.result.output.clone(),
2577 &self.config.available_artifacts,
2578 ) {
2579 Err(error) => {
2580 trace!(expected=?expected_revert, ?error, status=?outcome.result.result, "Expected revert mismatch");
2581 outcome.result.result = InstructionResult::Revert;
2582 outcome.result.output = error.abi_encode().into();
2583 }
2584 Ok((_, retdata)) => {
2585 expected_revert.actual_count += 1;
2586 if expected_revert.actual_count < expected_revert.count {
2587 self.expected_revert = Some(expected_revert);
2588 }
2589 if clear_last_frame_gas {
2590 self.gas_metering.last_frame_gas = None;
2591 }
2592 outcome.result.result = InstructionResult::Return;
2593 outcome.result.output = retdata;
2594 }
2595 };
2596 }
2597
2598 if let ExpectedRevertKind::Cheatcode { pending_processing } =
2601 &mut self.expected_revert.as_mut().unwrap().kind
2602 {
2603 *pending_processing = false;
2604 }
2605 }
2606 }
2607
2608 if cheatcode_call {
2611 return;
2612 }
2613
2614 let frame_gas = frame_gas(&outcome.result);
2617 let snapshot_gas_used =
2618 isolated_snapshot_gas_used.unwrap_or_else(|| outcome.result.gas.total_gas_spent());
2619 self.gas_metering.last_call_gas = Some(frame_gas.clone());
2620 self.gas_metering.last_frame_gas = Some(frame_gas);
2621 self.gas_metering.last_call_snapshot_gas_used = snapshot_gas_used;
2622 self.gas_metering.last_frame_snapshot_gas_used = snapshot_gas_used;
2623
2624 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
2627 if ecx.journal().depth() > 0
2629 && let Some(mut last_recorded_depth) = recorded_account_diffs_stack.pop()
2630 {
2631 if outcome.result.is_revert() {
2634 for element in &mut *last_recorded_depth {
2635 element.reverted = true;
2636 for storage_access in &mut element.storageAccesses {
2637 storage_access.reverted = true;
2638 }
2639 }
2640 }
2641
2642 if let Some(call_access) = last_recorded_depth.first_mut() {
2643 let curr_depth = ecx.journal().depth();
2648 if call_access.depth == curr_depth as u64
2649 && let Ok(acc) = ecx.journal_mut().load_account(call.target_address)
2650 {
2651 debug_assert!(access_is_call(call_access.kind));
2652 call_access.newBalance = acc.data.info.balance;
2653 call_access.newNonce = acc.data.info.nonce;
2654 }
2655 if let Some(last) = recorded_account_diffs_stack.last_mut() {
2660 last.extend(last_recorded_depth);
2661 } else {
2662 recorded_account_diffs_stack.push(last_recorded_depth);
2663 }
2664 }
2665 }
2666 }
2667
2668 let diag = self.fork_revert_diagnostic.take();
2671
2672 if outcome.result.is_revert() {
2675 if let Some(err) = diag {
2678 outcome.result.output = Error::encode(err.to_error_msg(&self.labels));
2679 }
2680 return;
2681 }
2682
2683 let should_check_emits = self
2695 .expected_emits
2696 .iter()
2697 .any(|(expected, _)| {
2698 let curr_depth = ecx.journal().depth();
2699 expected.depth == curr_depth
2700 }) &&
2701 !call.is_static;
2703 if should_check_emits {
2704 let expected_counts = self
2705 .expected_emits
2706 .iter()
2707 .filter_map(|(expected, count_map)| {
2708 let count = match expected.address {
2709 Some(emitter) => match count_map.get(&emitter) {
2710 Some(log_count) => expected
2711 .log
2712 .as_ref()
2713 .map(|l| log_count.count(l))
2714 .unwrap_or_else(|| log_count.count_unchecked()),
2715 None => 0,
2716 },
2717 None => match &expected.log {
2718 Some(log) => count_map.values().map(|logs| logs.count(log)).sum(),
2719 None => count_map.values().map(|logs| logs.count_unchecked()).sum(),
2720 },
2721 };
2722
2723 (count != expected.count).then_some((expected, count))
2724 })
2725 .collect::<Vec<_>>();
2726
2727 if let Some((expected, _)) = self
2729 .expected_emits
2730 .iter()
2731 .find(|(expected, _)| !expected.found && expected.count > 0)
2732 {
2733 outcome.result.result = InstructionResult::Revert;
2734 let mismatch_error = expected.mismatch_error.clone();
2735 let expected_log = expected.log.clone();
2736 let checks = expected.checks;
2737 let anonymous = expected.anonymous;
2738 let error_msg = mismatch_error
2739 .as_ref()
2740 .map(|mismatch| {
2741 mismatch.to_error_msg(self, checks, expected_log.as_ref(), anonymous)
2742 })
2743 .unwrap_or_else(|| "log != expected log".to_string());
2744 outcome.result.output = error_msg.abi_encode().into();
2745 return;
2746 }
2747
2748 if !expected_counts.is_empty() {
2749 let msg = if outcome.result.is_ok() {
2750 let (expected, count) = expected_counts.first().unwrap();
2751 format!("log emitted {count} times, expected {}", expected.count)
2752 } else {
2753 "expected an emit, but the call reverted instead. \
2754 ensure you're testing the happy path when using `expectEmit`"
2755 .to_string()
2756 };
2757
2758 outcome.result.result = InstructionResult::Revert;
2759 outcome.result.output = Error::encode(msg);
2760 return;
2761 }
2762
2763 self.expected_emits.clear()
2767 }
2768
2769 if let TxKind::Call(test_contract) = ecx.tx().kind() {
2772 if ecx.db().is_forked_mode()
2775 && outcome.result.result == InstructionResult::Stop
2776 && call.target_address != test_contract
2777 {
2778 self.fork_revert_diagnostic =
2779 ecx.db().diagnose_revert(call.target_address, ecx.journal().evm_state());
2780 }
2781 }
2782
2783 if ecx.journal().depth() == 0 {
2785 if outcome.result.is_revert() {
2789 return;
2790 }
2791
2792 for (address, calldatas) in &self.expected_calls {
2797 for ((calldata, scheme), (expected, actual_count)) in calldatas {
2799 let ExpectedCallData { gas, min_gas, value, count, call_type } = expected;
2801
2802 let failed = match call_type {
2803 ExpectedCallType::Count => *count != *actual_count,
2807 ExpectedCallType::NonCount => *count > *actual_count,
2812 };
2813 if failed {
2814 let expected_values = [
2815 Some(format!("data {}", hex::encode_prefixed(calldata))),
2816 value.as_ref().map(|v| format!("value {v}")),
2817 gas.map(|g| format!("gas {g}")),
2818 min_gas.map(|g| format!("minimum gas {g}")),
2819 scheme.map(|scheme| format!("call type {scheme:?}")),
2820 ]
2821 .into_iter()
2822 .flatten()
2823 .join(", ");
2824 let but = if outcome.result.is_ok() {
2825 let s = if *actual_count == 1 { "" } else { "s" };
2826 format!("was called {actual_count} time{s}")
2827 } else {
2828 "the call reverted instead; \
2829 ensure you're testing the happy path when using `expectCall`"
2830 .to_string()
2831 };
2832 let s = if *count == 1 { "" } else { "s" };
2833 let msg = format!(
2834 "expected call to {address} with {expected_values} \
2835 to be called {count} time{s}, but {but}"
2836 );
2837 outcome.result.result = InstructionResult::Revert;
2838 outcome.result.output = Error::encode(msg);
2839
2840 return;
2841 }
2842 }
2843 }
2844
2845 for (expected, _) in &mut self.expected_emits {
2849 if expected.count == 0 && !expected.found {
2850 expected.found = true;
2851 }
2852 }
2853 self.expected_emits.retain(|(expected, _)| !expected.found);
2854 if !self.expected_emits.is_empty() {
2856 let msg = if outcome.result.is_ok() {
2857 "expected an emit, but no logs were emitted afterwards. \
2858 you might have mismatched events or not enough events were emitted"
2859 } else {
2860 "expected an emit, but the call reverted instead. \
2861 ensure you're testing the happy path when using `expectEmit`"
2862 };
2863 outcome.result.result = InstructionResult::Revert;
2864 outcome.result.output = Error::encode(msg);
2865 return;
2866 }
2867
2868 if let Some(expected_create) = self.expected_creates.first() {
2870 let msg = format!(
2871 "expected {} call by address {} for bytecode {} but not found",
2872 expected_create.create_scheme,
2873 hex::encode_prefixed(expected_create.deployer),
2874 hex::encode_prefixed(&expected_create.bytecode),
2875 );
2876 outcome.result.result = InstructionResult::Revert;
2877 outcome.result.output = Error::encode(msg);
2878 }
2879 }
2880 }
2881
2882 fn create(
2883 &mut self,
2884 ecx: &mut FoundryContextFor<'_, FEN>,
2885 mut input: &mut CreateInputs,
2886 ) -> Option<CreateOutcome> {
2887 if let Some(spec_id) = self.execution_evm_version {
2889 ecx.set_spec_and_gas_params(spec_id);
2890 }
2891
2892 let gas = Gas::new(input.gas_limit());
2893 let curr_depth = ecx.journal().depth();
2894 self.start_created_accounts_frame(
2895 curr_depth == 0,
2896 CreatedAccountsFrameKind::Create,
2897 curr_depth,
2898 );
2899
2900 if self.intercept_next_create_call {
2902 self.intercept_next_create_call = false;
2904
2905 let output = input.init_code();
2907
2908 return Some(CreateOutcome {
2910 result: InterpreterResult { result: InstructionResult::Revert, output, gas },
2911 address: None,
2912 charged_create_state_gas: input.charged_create_state_gas(),
2913 });
2914 }
2915
2916 if let Some(prank) = &self.get_prank(curr_depth)
2918 && curr_depth >= prank.depth
2919 && input.caller() == prank.prank_caller
2920 {
2921 let prank_applied = if curr_depth == prank.depth {
2923 let _ = journaled_account(ecx, prank.new_caller);
2925 input.set_caller(prank.new_caller);
2926 true
2927 } else {
2928 false
2929 };
2930
2931 let prank_applied = if let Some(new_origin) = prank.new_origin {
2933 ecx.tx_mut().set_caller(new_origin);
2934 true
2935 } else {
2936 prank_applied
2937 };
2938
2939 if prank_applied && let Some(applied_prank) = prank.first_time_applied() {
2941 self.pranks.insert(curr_depth, applied_prank);
2942 }
2943 }
2944
2945 self.apply_accesslist(ecx);
2947
2948 if let Some(broadcast) = &mut self.broadcast
2950 && curr_depth >= broadcast.depth
2951 && input.caller() == broadcast.original_caller
2952 {
2953 if let Err(err) = ecx.journal_mut().load_account(broadcast.new_origin) {
2954 return Some(CreateOutcome {
2955 result: InterpreterResult {
2956 result: InstructionResult::Revert,
2957 output: Error::encode(err),
2958 gas,
2959 },
2960 address: None,
2961 charged_create_state_gas: input.charged_create_state_gas(),
2962 });
2963 }
2964
2965 ecx.tx_mut().set_caller(broadcast.new_origin);
2966
2967 if curr_depth == broadcast.depth || broadcast.deploy_from_code {
2968 broadcast.deploy_from_code = false;
2970
2971 input.set_caller(broadcast.new_origin);
2972
2973 let rpc = ecx.db().active_fork_url();
2974 let fee_token = ecx.tx().fee_token();
2975 let account = &ecx.journal().evm_state()[&broadcast.new_origin];
2976 let mut tx_req = TransactionRequestFor::<FEN>::default()
2977 .with_from(broadcast.new_origin)
2978 .with_kind(TxKind::Create)
2979 .with_value(input.value())
2980 .with_input(input.init_code())
2981 .with_nonce(account.info.nonce);
2982 if let Some(fee_token) = fee_token {
2983 tx_req.set_fee_token(fee_token);
2984 }
2985 self.broadcastable_transactions.push_back(BroadcastableTransaction {
2986 rpc,
2987 transaction: TransactionMaybeSigned::new(tx_req),
2988 });
2989
2990 input.log_debug(self, &input.scheme().unwrap_or(CreateScheme::Create));
2991 }
2992 }
2993
2994 let address = input.allow_cheatcodes(self, ecx);
2996
2997 self.record_created_account(ecx.db().active_fork_id(), address);
2998
2999 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
3001 recorded_account_diffs_stack.push(vec![AccountAccess {
3002 chainInfo: crate::Vm::ChainInfo {
3003 forkId: ecx.db().active_fork_id().unwrap_or_default(),
3004 chainId: U256::from(ecx.cfg().chain_id()),
3005 },
3006 accessor: input.caller(),
3007 account: address,
3008 kind: crate::Vm::AccountAccessKind::Create,
3009 initialized: true,
3010 oldBalance: U256::ZERO, newBalance: U256::ZERO, oldNonce: 0, newNonce: 1, value: input.value(),
3015 data: input.init_code(),
3016 reverted: false,
3017 deployedCode: Bytes::new(), storageAccesses: vec![], depth: curr_depth as u64,
3020 }]);
3021 }
3022
3023 None
3024 }
3025
3026 fn create_end(
3027 &mut self,
3028 ecx: &mut FoundryContextFor<'_, FEN>,
3029 call: &CreateInputs,
3030 outcome: &mut CreateOutcome,
3031 ) {
3032 let isolated_snapshot_gas_used = self.gas_metering.isolated_snapshot_gas_used.take();
3033 self.gas_metering.record_isolated_refund(
3034 ecx.journal().depth(),
3035 &outcome.result.gas,
3036 isolated_snapshot_gas_used,
3037 );
3038 let call = Some(call);
3039 let curr_depth = ecx.journal().depth();
3040
3041 self.finish_created_accounts_frame(
3042 outcome.result.is_ok(),
3043 CreatedAccountsFrameKind::Create,
3044 curr_depth,
3045 );
3046
3047 if let Some(prank) = &self.get_prank(curr_depth)
3049 && curr_depth == prank.depth
3050 {
3051 ecx.tx_mut().set_caller(prank.prank_origin);
3052
3053 if prank.single_call {
3055 std::mem::take(&mut self.pranks);
3056 }
3057 }
3058
3059 if let Some(broadcast) = &self.broadcast
3061 && curr_depth == broadcast.depth
3062 {
3063 ecx.tx_mut().set_caller(broadcast.original_origin);
3064
3065 if broadcast.single_call {
3067 std::mem::take(&mut self.broadcast);
3068 }
3069 }
3070
3071 if let Some(expected_revert) = &mut self.expected_revert {
3073 if outcome.result.is_revert()
3085 && expected_revert.reverter.is_some()
3086 && expected_revert.reverted_by.is_none()
3087 && let Some(addr) = outcome.address
3088 {
3089 expected_revert.reverted_by = Some(addr);
3090 }
3091
3092 if curr_depth <= expected_revert.depth
3093 && matches!(expected_revert.kind, ExpectedRevertKind::Default)
3094 {
3095 let mut expected_revert = std::mem::take(&mut self.expected_revert).unwrap();
3096 return match revert_handlers::handle_expect_revert(
3097 false,
3098 true,
3099 self.config.internal_expect_revert,
3100 &expected_revert,
3101 outcome.result.result,
3102 outcome.result.output.clone(),
3103 &self.config.available_artifacts,
3104 ) {
3105 Ok((address, retdata)) => {
3106 expected_revert.actual_count += 1;
3107 if expected_revert.actual_count < expected_revert.count {
3108 expected_revert.reverted_by = None;
3110 self.expected_revert = Some(expected_revert.clone());
3111 }
3112
3113 outcome.result.result = InstructionResult::Return;
3114 outcome.result.output = retdata;
3115 outcome.address = address;
3116 self.gas_metering.last_frame_gas = None;
3117 }
3118 Err(err) => {
3119 outcome.result.result = InstructionResult::Revert;
3120 outcome.result.output = err.abi_encode().into();
3121 }
3122 };
3123 }
3124 }
3125
3126 if curr_depth > 0 {
3127 self.gas_metering.last_frame_gas = Some(frame_gas(&outcome.result));
3130 self.gas_metering.last_frame_snapshot_gas_used =
3131 isolated_snapshot_gas_used.unwrap_or_else(|| outcome.result.gas.total_gas_spent());
3132 }
3133
3134 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack
3137 && let Some(mut last_depth) = recorded_account_diffs_stack.pop()
3138 {
3139 if outcome.result.is_revert() {
3142 for element in &mut *last_depth {
3143 element.reverted = true;
3144 for storage_access in &mut element.storageAccesses {
3145 storage_access.reverted = true;
3146 }
3147 }
3148 }
3149
3150 if let Some(create_access) = last_depth.first_mut() {
3151 if create_access.depth == curr_depth as u64 {
3153 debug_assert_eq!(
3154 create_access.kind as u8,
3155 crate::Vm::AccountAccessKind::Create as u8
3156 );
3157 if let Some(address) = outcome.address
3158 && let Ok(created_acc) = ecx.journal_mut().load_account(address)
3159 {
3160 create_access.newBalance = created_acc.data.info.balance;
3161 create_access.newNonce = created_acc.data.info.nonce;
3162 create_access.deployedCode =
3163 created_acc.data.info.code.clone().unwrap_or_default().original_bytes();
3164 }
3165 }
3166 }
3167 if let Some(last) = recorded_account_diffs_stack.last_mut() {
3172 last.append(&mut last_depth);
3173 } else {
3174 recorded_account_diffs_stack.push(last_depth);
3175 }
3176 }
3177
3178 if !self.expected_creates.is_empty()
3180 && let (Some(address), Some(call)) = (outcome.address, call)
3181 && let Ok(created_acc) = ecx.journal_mut().load_account(address)
3182 {
3183 let bytecode = created_acc.data.info.code.clone().unwrap_or_default().original_bytes();
3184 if let Some((index, _)) =
3185 self.expected_creates.iter().find_position(|expected_create| {
3186 expected_create.deployer == call.caller()
3187 && expected_create.create_scheme.eq(call.scheme().into())
3188 && expected_create.bytecode == bytecode
3189 })
3190 {
3191 self.expected_creates.swap_remove(index);
3192 }
3193 }
3194 }
3195}
3196
3197impl<FEN: FoundryEvmNetwork> InspectorExt for Cheatcodes<FEN> {
3198 fn should_use_create2_factory(&mut self, depth: usize, inputs: &CreateInputs) -> bool {
3199 let target_depth = if let Some(prank) = &self.get_prank(depth) {
3200 prank.depth
3201 } else if let Some(broadcast) = &self.broadcast {
3202 broadcast.depth
3203 } else {
3204 1
3205 };
3206
3207 if depth != target_depth {
3208 return false;
3209 }
3210
3211 match inputs.scheme() {
3212 CreateScheme::Create2 { .. } => {
3213 self.broadcast.is_some() || self.config.always_use_create_2_factory
3214 }
3215 CreateScheme::Create => self.config.batch_rewrite_creates && self.broadcast.is_some(),
3216 _ => false,
3217 }
3218 }
3219
3220 fn create2_deployer(&self) -> Address {
3221 self.config.evm_opts.create2_deployer
3222 }
3223}
3224
3225impl<FEN: FoundryEvmNetwork> Cheatcodes<FEN> {
3226 #[cold]
3227 fn meter_gas(&mut self, interpreter: &mut Interpreter) {
3228 if let Some(paused_gas) = self.gas_metering.paused_frames.last() {
3229 let memory = *interpreter.gas.memory();
3232 interpreter.gas = *paused_gas;
3233 interpreter.gas.memory_mut().words_num = memory.words_num;
3234 interpreter.gas.memory_mut().expansion_cost = memory.expansion_cost;
3235 } else {
3236 self.gas_metering.paused_frames.push(interpreter.gas);
3238 }
3239 }
3240
3241 #[cold]
3242 fn meter_gas_record(
3243 &mut self,
3244 interpreter: &mut Interpreter,
3245 ecx: &mut FoundryContextFor<'_, FEN>,
3246 ) {
3247 if interpreter.bytecode.action.as_ref().and_then(|i| i.instruction_result()).is_none() {
3248 let curr_depth = ecx.journal().depth();
3249 let isolated_refund = match self.gas_metering.pending_isolated_refund {
3250 Some((depth, refund)) if depth == curr_depth => {
3251 self.gas_metering.pending_isolated_refund = None;
3252 refund
3253 }
3254 _ => 0,
3255 };
3256 self.gas_metering.gas_records.iter_mut().for_each(|record| {
3257 if curr_depth == record.depth {
3258 if self.gas_metering.last_gas_used != 0 {
3261 let gas_diff = interpreter
3262 .gas
3263 .total_gas_spent()
3264 .saturating_sub(self.gas_metering.last_gas_used)
3265 .saturating_sub(isolated_refund);
3266 record.gas_used = record.gas_used.saturating_add(gas_diff);
3267 }
3268
3269 self.gas_metering.last_gas_used = interpreter.gas.total_gas_spent();
3272 }
3273 });
3274 }
3275 }
3276
3277 #[cold]
3278 fn meter_gas_end(&mut self, interpreter: &mut Interpreter) {
3279 if let Some(interpreter_action) = interpreter.bytecode.action.as_ref()
3281 && will_exit(interpreter_action)
3282 {
3283 self.gas_metering.paused_frames.pop();
3284 }
3285 }
3286
3287 #[cold]
3288 const fn meter_gas_reset(&mut self, interpreter: &mut Interpreter) {
3289 let mut gas = Gas::new(interpreter.gas.limit());
3290 gas.memory_mut().words_num = interpreter.gas.memory().words_num;
3291 gas.memory_mut().expansion_cost = interpreter.gas.memory().expansion_cost;
3292 interpreter.gas = gas;
3293 self.gas_metering.reset = false;
3294 }
3295
3296 #[cold]
3297 fn meter_gas_check(&mut self, interpreter: &mut Interpreter) {
3298 if let Some(interpreter_action) = interpreter.bytecode.action.as_ref()
3299 && will_exit(interpreter_action)
3300 {
3301 if interpreter.gas.total_gas_spent()
3305 < u64::try_from(interpreter.gas.refunded()).unwrap_or_default()
3306 {
3307 interpreter.gas = Gas::new(interpreter.gas.limit());
3308 }
3309 }
3310 }
3311
3312 #[cold]
3326 fn apply_env_overrides(&mut self, interpreter: &mut Interpreter, fork_id: Option<U256>) {
3327 let Some(env_overrides) = self.env_overrides.get_mut(&fork_id) else { return };
3328 let Some(opcode) = env_overrides.pending_opcode.take() else { return };
3329 match opcode {
3330 op::BASEFEE => {
3331 if let Some(basefee) = env_overrides.basefee {
3332 Self::replace_top_of_stack(interpreter, U256::from(basefee));
3334 }
3335 }
3336 op::GASPRICE => {
3337 if let Some(gas_price) = env_overrides.gas_price {
3338 Self::replace_top_of_stack(interpreter, U256::from(gas_price));
3340 }
3341 }
3342 op::BLOBHASH => {
3343 let blob_hashes = env_overrides.blob_hashes.clone();
3344 let blobhash_index = env_overrides.pending_blobhash_index.take();
3345 if let Some(ref blob_hashes) = blob_hashes
3346 && let Some(index) = blobhash_index
3347 {
3348 let hash = blob_hashes.get(index as usize).copied().unwrap_or_default();
3351 Self::replace_top_of_stack(interpreter, hash.into());
3352 }
3353 }
3354 _ => {}
3355 }
3356 }
3357
3358 fn replace_top_of_stack(interpreter: &mut Interpreter, value: U256) {
3366 if interpreter.stack.pop().is_err() {
3367 debug_assert!(false, "env override expected opcode result on stack");
3368 return;
3369 }
3370 let _ = interpreter.stack.push(value);
3371 }
3372
3373 #[cold]
3381 fn arbitrary_storage_end(
3382 &mut self,
3383 interpreter: &mut Interpreter,
3384 ecx: &mut FoundryContextFor<'_, FEN>,
3385 ) {
3386 let (key, target_address) = if interpreter.bytecode.opcode() == op::SLOAD {
3387 (try_or_return!(interpreter.stack.peek(0)), interpreter.input.target_address)
3388 } else {
3389 return;
3390 };
3391
3392 if self.is_arbitrary_storage_slot_explicit(target_address, key) {
3393 return;
3394 }
3395
3396 let Some(value) = ecx.sload(target_address, key) else {
3397 return;
3398 };
3399
3400 if (value.is_cold && value.data.is_zero())
3401 || self.should_overwrite_arbitrary_storage(&target_address, key)
3402 {
3403 if self.has_arbitrary_storage(&target_address) {
3404 let arbitrary_value = self
3405 .cached_arbitrary_storage_value(target_address, key)
3406 .unwrap_or_else(|| self.rng().random());
3407 self.arbitrary_storage.as_mut().unwrap().save(
3408 ecx,
3409 target_address,
3410 key,
3411 arbitrary_value,
3412 );
3413 } else if self.is_arbitrary_storage_copy(&target_address) {
3414 let arbitrary_value = self.rng().random();
3415 self.arbitrary_storage.as_mut().unwrap().copy(
3416 ecx,
3417 target_address,
3418 key,
3419 arbitrary_value,
3420 );
3421 }
3422 }
3423 }
3424
3425 #[inline]
3429 pub fn finish_storage_hook_callback(
3430 &mut self,
3431 interpreter: &mut Interpreter,
3432 ecx: &mut FoundryContextFor<'_, FEN>,
3433 ) -> bool {
3434 let Some(active) = self.active_storage_hook.as_ref() else { return false };
3435 let Some((result, output)) = active.outcome.clone() else { return false };
3436
3437 let active = self.active_storage_hook.take().expect("active storage hook exists");
3438 Self::restore_storage_hook_access(ecx, active.journal_start);
3439 self.restore_storage_hook_inspector_state(active.inspector_state);
3440 let _ = interpreter.stack.pop();
3441 if let Some(item) = active.saved_stack_item {
3442 let result = interpreter.stack.push(item);
3443 debug_assert!(result, "reserved storage-hook stack slot must be available");
3444 }
3445 interpreter.gas = active.saved_gas;
3446 interpreter.return_data.set_buffer(active.saved_return_data);
3447
3448 if result.is_ok() {
3449 false
3450 } else {
3451 interpreter.bytecode.set_action(InterpreterAction::new_return(
3452 InstructionResult::Revert,
3453 output,
3454 interpreter.gas,
3455 ));
3456 true
3457 }
3458 }
3459
3460 fn take_storage_hook_inspector_state(&mut self) -> StorageHookInspectorState {
3461 StorageHookInspectorState {
3462 accesses: std::mem::take(&mut self.accesses),
3463 recording_accesses: std::mem::replace(&mut self.recording_accesses, false),
3464 mapping_slots: self.mapping_slots.take(),
3465 recorded_logs: self.recorded_logs.take(),
3466 mocked_calls: std::mem::take(&mut self.mocked_calls),
3467 mocked_functions: std::mem::take(&mut self.mocked_functions),
3468 expected_revert: self.expected_revert.take(),
3469 assume_no_revert: self.assume_no_revert.take(),
3470 expected_calls: std::mem::take(&mut self.expected_calls),
3471 expected_emits: std::mem::take(&mut self.expected_emits),
3472 expected_creates: std::mem::take(&mut self.expected_creates),
3473 }
3474 }
3475
3476 fn restore_storage_hook_inspector_state(&mut self, state: StorageHookInspectorState) {
3477 self.accesses = state.accesses;
3478 self.recording_accesses = state.recording_accesses;
3479 self.mapping_slots = state.mapping_slots;
3480 self.recorded_logs = state.recorded_logs;
3481 self.mocked_calls = state.mocked_calls;
3482 self.mocked_functions = state.mocked_functions;
3483 self.expected_revert = state.expected_revert;
3484 self.assume_no_revert = state.assume_no_revert;
3485 self.expected_calls = state.expected_calls;
3486 self.expected_emits = state.expected_emits;
3487 self.expected_creates = state.expected_creates;
3488 }
3489
3490 fn restore_storage_hook_access(ecx: &mut FoundryContextFor<'_, FEN>, journal_start: usize) {
3491 let (_, journal) = ecx.db_journal_inner_mut();
3492 let entries =
3493 journal.journal.drain(journal_start.min(journal.journal.len())..).collect_vec();
3494 for entry in entries {
3495 match entry {
3496 JournalEntry::AccountWarmed { address } => {
3497 journal.state.get_mut(&address).expect("warmed account exists").mark_cold();
3498 }
3499 JournalEntry::StorageWarmed { address, key } => {
3500 journal
3504 .state
3505 .get_mut(&address)
3506 .expect("warmed account exists")
3507 .storage
3508 .get_mut(&key)
3509 .expect("warmed storage slot exists")
3510 .mark_cold();
3511 }
3512 entry => journal.journal.push(entry),
3513 }
3514 }
3515 }
3516
3517 fn capture_storage_hook(
3518 &mut self,
3519 interpreter: &Interpreter,
3520 ecx: &mut FoundryContextFor<'_, FEN>,
3521 ) {
3522 self.pending_storage_hook = None;
3523 if self.active_storage_hook.is_some() {
3524 return;
3525 }
3526 let account = interpreter.input.target_address;
3527 match interpreter.bytecode.opcode() {
3528 op::SLOAD => {
3529 let slot = try_or_return!(interpreter.stack.peek(0));
3530 let Some(hook) = self.storage_load_hooks.get(&account).copied() else { return };
3531 self.pending_storage_hook = Some(PendingStorageHook::Load { account, slot, hook });
3532 }
3533 op::SSTORE => {
3534 let slot = try_or_return!(interpreter.stack.peek(0));
3535 let (hook, mapping) = if let Some(hook) = self.storage_store_hooks.get(&account) {
3536 (*hook, None)
3537 } else {
3538 let Some(provenance) = self
3539 .storage_hook_mapping_slots
3540 .get(&account)
3541 .and_then(|slots| slots.resolve(slot.into()))
3542 else {
3543 return;
3544 };
3545 let Some(hook) = self
3546 .mapping_storage_store_hooks
3547 .get(&account)
3548 .and_then(|hooks| hooks.get(&provenance.root_slot))
3549 .copied()
3550 else {
3551 return;
3552 };
3553 (hook, Some((provenance.root_slot, provenance.keys)))
3554 };
3555 let checkpoint = ecx.journal_mut().checkpoint();
3556 let old_value =
3557 ecx.sload(account, slot).map(|value| value.data).unwrap_or_default();
3558 ecx.journal_mut().checkpoint_revert(checkpoint);
3559 self.pending_storage_hook =
3560 Some(PendingStorageHook::Store { account, slot, old_value, mapping, hook });
3561 }
3562 _ => {}
3563 }
3564 }
3565
3566 fn invoke_pending_storage_hook(
3567 &mut self,
3568 interpreter: &mut Interpreter,
3569 ecx: &mut FoundryContextFor<'_, FEN>,
3570 ) {
3571 let Some(pending) = self.pending_storage_hook.take() else { return };
3572 if interpreter
3573 .bytecode
3574 .action
3575 .as_ref()
3576 .and_then(InterpreterAction::instruction_result)
3577 .is_some()
3578 {
3579 return;
3580 }
3581
3582 let (hook, input, saved_stack_item) = match pending {
3583 PendingStorageHook::Load { account, slot, hook } => {
3584 let value = try_or_return!(interpreter.stack.peek(0));
3585 let mut input = Vec::with_capacity(4 + 32 * 3);
3586 input.extend_from_slice(&hook.callback_selector);
3587 input.extend_from_slice(account.into_word().as_slice());
3588 input.extend_from_slice(&slot.to_be_bytes::<32>());
3589 input.extend_from_slice(&value.to_be_bytes::<32>());
3590 (hook, Bytes::from(input), Some(value))
3591 }
3592 PendingStorageHook::Store { account, slot, old_value, mapping, hook } => {
3593 let new_value =
3594 ecx.sload(account, slot).map(|value| value.data).unwrap_or_default();
3595 let mut input = Vec::with_capacity(4 + 32 * 4);
3596 input.extend_from_slice(&hook.callback_selector);
3597 input.extend_from_slice(account.into_word().as_slice());
3598 input.extend_from_slice(&slot.to_be_bytes::<32>());
3599 if let Some((root, keys)) = mapping {
3600 input.extend_from_slice(root.as_slice());
3601 input.extend_from_slice(&U256::from(32 * 6).to_be_bytes::<32>());
3602 input.extend_from_slice(&old_value.to_be_bytes::<32>());
3603 input.extend_from_slice(&new_value.to_be_bytes::<32>());
3604 input.extend_from_slice(&U256::from(keys.len()).to_be_bytes::<32>());
3605 for key in keys {
3606 input.extend_from_slice(key.as_slice());
3607 }
3608 } else {
3609 input.extend_from_slice(&old_value.to_be_bytes::<32>());
3610 input.extend_from_slice(&new_value.to_be_bytes::<32>());
3611 }
3612 (hook, Bytes::from(input), None)
3613 }
3614 };
3615
3616 let journal_start = ecx.db_journal_inner_mut().1.journal.len();
3617 let account = match ecx.journal_mut().load_account_with_code(hook.callback_target) {
3618 Ok(account) => account,
3619 Err(err) => {
3620 interpreter.bytecode.set_action(InterpreterAction::new_return(
3621 InstructionResult::Revert,
3622 Error::encode(err),
3623 interpreter.gas,
3624 ));
3625 return;
3626 }
3627 };
3628 let known_bytecode =
3629 (account.info.code_hash, account.info.code.clone().unwrap_or_default());
3630 let saved_gas = interpreter.gas;
3631 let saved_return_data = Bytes::copy_from_slice(interpreter.return_data.buffer());
3632 let gas_limit = interpreter.gas.remaining();
3633 let parent_depth = ecx.journal().depth();
3634 if saved_stack_item.is_some() {
3635 let result = interpreter.stack.pop();
3636 debug_assert!(result.is_ok(), "captured SLOAD result must be on the stack");
3637 }
3638 let inspector_state = self.take_storage_hook_inspector_state();
3639
3640 self.active_storage_hook = Some(ActiveStorageHook {
3641 parent_depth,
3642 callback_target: hook.callback_target,
3643 callback_input: input.clone(),
3644 saved_gas,
3645 saved_return_data,
3646 saved_stack_item,
3647 journal_start,
3648 inspector_state,
3649 outcome: None,
3650 });
3651 interpreter.bytecode.set_action(InterpreterAction::NewFrame(FrameInput::Call(Box::new(
3652 CallInputs {
3653 input: CallInput::Bytes(input),
3654 return_memory_offset: 0..0,
3655 gas_limit,
3656 reservoir: 0,
3657 bytecode_address: hook.callback_target,
3658 known_bytecode,
3659 target_address: hook.callback_target,
3660 caller: CHEATCODE_ADDRESS,
3661 value: CallValue::Transfer(U256::ZERO),
3662 scheme: CallScheme::Call,
3663 is_static: false,
3664 charged_new_account_state_gas: false,
3665 },
3666 ))));
3667 }
3668
3669 #[cold]
3671 fn record_accesses(&mut self, interpreter: &mut Interpreter) {
3672 let access = &mut self.accesses;
3673 match interpreter.bytecode.opcode() {
3674 op::SLOAD => {
3675 let key = try_or_return!(interpreter.stack.peek(0));
3676 access.record_read(interpreter.input.target_address, key);
3677 }
3678 op::SSTORE => {
3679 let key = try_or_return!(interpreter.stack.peek(0));
3680 access.record_write(interpreter.input.target_address, key);
3681 }
3682 _ => {}
3683 }
3684 }
3685
3686 #[cold]
3687 fn record_state_diffs(
3688 &mut self,
3689 interpreter: &mut Interpreter,
3690 ecx: &mut FoundryContextFor<'_, FEN>,
3691 ) {
3692 let Some(account_accesses) = &mut self.recorded_account_diffs_stack else { return };
3693 match interpreter.bytecode.opcode() {
3694 op::SELFDESTRUCT => {
3695 let Some(last) = account_accesses.last_mut() else { return };
3697
3698 let target = try_or_return!(interpreter.stack.peek(0));
3700 let target = Address::from_word(B256::from(target));
3701 let (initialized, old_balance, old_nonce) = ecx
3702 .journal_mut()
3703 .load_account(target)
3704 .map(|account| {
3705 (
3706 account.data.info.exists(),
3707 account.data.info.balance,
3708 account.data.info.nonce,
3709 )
3710 })
3711 .unwrap_or_default();
3712
3713 let value = ecx
3715 .balance(interpreter.input.target_address)
3716 .map(|b| b.data)
3717 .unwrap_or(U256::ZERO);
3718
3719 last.push(crate::Vm::AccountAccess {
3721 chainInfo: crate::Vm::ChainInfo {
3722 forkId: ecx.db().active_fork_id().unwrap_or_default(),
3723 chainId: U256::from(ecx.cfg().chain_id()),
3724 },
3725 accessor: interpreter.input.target_address,
3726 account: target,
3727 kind: crate::Vm::AccountAccessKind::SelfDestruct,
3728 initialized,
3729 oldBalance: old_balance,
3730 newBalance: old_balance + value,
3731 oldNonce: old_nonce,
3732 newNonce: old_nonce, value,
3734 data: Bytes::new(),
3735 reverted: false,
3736 deployedCode: Bytes::new(),
3737 storageAccesses: vec![],
3738 depth: ecx
3739 .journal()
3740 .depth()
3741 .try_into()
3742 .expect("journaled state depth exceeds u64"),
3743 });
3744 }
3745
3746 op::SLOAD => {
3747 let Some(last) = account_accesses.last_mut() else { return };
3748
3749 let key = try_or_return!(interpreter.stack.peek(0));
3750 let address = interpreter.input.target_address;
3751
3752 let checkpoint = ecx.journal_mut().checkpoint();
3756 let present_value =
3757 ecx.sload(address, key).map(|previous| previous.data).unwrap_or_default();
3758 ecx.journal_mut().checkpoint_revert(checkpoint);
3759 let access = crate::Vm::StorageAccess {
3760 account: interpreter.input.target_address,
3761 slot: key.into(),
3762 isWrite: false,
3763 previousValue: present_value.into(),
3764 newValue: present_value.into(),
3765 reverted: false,
3766 };
3767 let curr_depth =
3768 ecx.journal().depth().try_into().expect("journaled state depth exceeds u64");
3769 append_storage_access(last, access, curr_depth);
3770 }
3771 op::SSTORE => {
3772 let Some(last) = account_accesses.last_mut() else { return };
3773
3774 let key = try_or_return!(interpreter.stack.peek(0));
3775 let value = try_or_return!(interpreter.stack.peek(1));
3776 let address = interpreter.input.target_address;
3777 let checkpoint = ecx.journal_mut().checkpoint();
3781 let previous_value =
3782 ecx.sload(address, key).map(|previous| previous.data).unwrap_or_default();
3783 ecx.journal_mut().checkpoint_revert(checkpoint);
3784
3785 let access = crate::Vm::StorageAccess {
3786 account: address,
3787 slot: key.into(),
3788 isWrite: true,
3789 previousValue: previous_value.into(),
3790 newValue: value.into(),
3791 reverted: false,
3792 };
3793 let curr_depth =
3794 ecx.journal().depth().try_into().expect("journaled state depth exceeds u64");
3795 append_storage_access(last, access, curr_depth);
3796 }
3797
3798 op::EXTCODECOPY | op::EXTCODESIZE | op::EXTCODEHASH | op::BALANCE => {
3800 let kind = match interpreter.bytecode.opcode() {
3801 op::EXTCODECOPY => crate::Vm::AccountAccessKind::Extcodecopy,
3802 op::EXTCODESIZE => crate::Vm::AccountAccessKind::Extcodesize,
3803 op::EXTCODEHASH => crate::Vm::AccountAccessKind::Extcodehash,
3804 op::BALANCE => crate::Vm::AccountAccessKind::Balance,
3805 _ => unreachable!(),
3806 };
3807 let address =
3808 Address::from_word(B256::from(try_or_return!(interpreter.stack.peek(0))));
3809 let checkpoint = ecx.journal_mut().checkpoint();
3810 let (initialized, balance, nonce) = ecx
3811 .journal_mut()
3812 .load_account(address)
3813 .map(|acc| (acc.data.info.exists(), acc.data.info.balance, acc.data.info.nonce))
3814 .unwrap_or_default();
3815 ecx.journal_mut().checkpoint_revert(checkpoint);
3816 let curr_depth =
3817 ecx.journal().depth().try_into().expect("journaled state depth exceeds u64");
3818 let account_access = crate::Vm::AccountAccess {
3819 chainInfo: crate::Vm::ChainInfo {
3820 forkId: ecx.db().active_fork_id().unwrap_or_default(),
3821 chainId: U256::from(ecx.cfg().chain_id()),
3822 },
3823 accessor: interpreter.input.target_address,
3824 account: address,
3825 kind,
3826 initialized,
3827 oldBalance: balance,
3828 newBalance: balance,
3829 oldNonce: nonce,
3830 newNonce: nonce, value: U256::ZERO,
3832 data: Bytes::new(),
3833 reverted: false,
3834 deployedCode: Bytes::new(),
3835 storageAccesses: vec![],
3836 depth: curr_depth,
3837 };
3838 if let Some(last) = account_accesses.last_mut() {
3841 last.push(account_access);
3842 } else {
3843 account_accesses.push(vec![account_access]);
3844 }
3845 }
3846 _ => {}
3847 }
3848 }
3849
3850 #[cold]
3855 fn check_mem_opcodes(&self, interpreter: &mut Interpreter, depth: u64) {
3856 let Some(ranges) = self.allowed_mem_writes.get(&depth) else {
3857 return;
3858 };
3859
3860 macro_rules! mem_opcode_match {
3869 ($(($opcode:ident, $offset_depth:expr, $size_depth:expr, $writes:expr)),* $(,)?) => {
3870 match interpreter.bytecode.opcode() {
3871 op::MSTORE => {
3876 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
3878
3879 if !ranges.iter().any(|range| {
3882 range.contains(&offset) && range.contains(&(offset + 31))
3883 }) {
3884 let value = try_or_return!(interpreter.stack.peek(1)).to_be_bytes::<32>();
3889 if value[..SELECTOR_LEN] == stopExpectSafeMemoryCall::SELECTOR {
3890 return
3891 }
3892
3893 disallowed_mem_write(offset, 32, interpreter, ranges);
3894 return
3895 }
3896 }
3897 op::MSTORE8 => {
3898 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
3900
3901 if !ranges.iter().any(|range| range.contains(&offset)) {
3904 disallowed_mem_write(offset, 1, interpreter, ranges);
3905 return
3906 }
3907 }
3908
3909 op::MLOAD => {
3914 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
3916
3917 if offset >= interpreter.memory.size() as u64 && !ranges.iter().any(|range| {
3921 range.contains(&offset) && range.contains(&(offset + 31))
3922 }) {
3923 disallowed_mem_write(offset, 32, interpreter, ranges);
3924 return
3925 }
3926 }
3927
3928 op::CALL => {
3933 let dest_offset = try_or_return!(interpreter.stack.peek(5)).saturating_to::<u64>();
3935
3936 let size = try_or_return!(interpreter.stack.peek(6)).saturating_to::<u64>();
3938
3939 let fail_cond = !ranges.iter().any(|range| {
3943 range.contains(&dest_offset) &&
3944 range.contains(&(dest_offset + size.saturating_sub(1)))
3945 });
3946
3947 if fail_cond {
3950 let to = Address::from_word(try_or_return!(interpreter.stack.peek(1)).to_be_bytes::<32>().into());
3954 if to == CHEATCODE_ADDRESS {
3955 let args_offset = try_or_return!(interpreter.stack.peek(3)).saturating_to::<usize>();
3956 let args_size = try_or_return!(interpreter.stack.peek(4)).saturating_to::<usize>();
3957 if args_size >= SELECTOR_LEN
3959 && args_offset.saturating_add(args_size) <= interpreter.memory.size()
3960 {
3961 let memory_word = interpreter.memory.slice_len(args_offset, args_size);
3962 if memory_word[..SELECTOR_LEN] == stopExpectSafeMemoryCall::SELECTOR {
3963 return
3964 }
3965 }
3966 }
3967
3968 disallowed_mem_write(dest_offset, size, interpreter, ranges);
3969 return
3970 }
3971 }
3972
3973 $(op::$opcode => {
3974 let dest_offset = try_or_return!(interpreter.stack.peek($offset_depth)).saturating_to::<u64>();
3976
3977 let size = try_or_return!(interpreter.stack.peek($size_depth)).saturating_to::<u64>();
3979
3980 let fail_cond = !ranges.iter().any(|range| {
3984 range.contains(&dest_offset) &&
3985 range.contains(&(dest_offset + size.saturating_sub(1)))
3986 }) && ($writes ||
3987 [dest_offset, (dest_offset + size).saturating_sub(1)].into_iter().any(|offset| {
3988 offset >= interpreter.memory.size() as u64
3989 })
3990 );
3991
3992 if fail_cond {
3995 disallowed_mem_write(dest_offset, size, interpreter, ranges);
3996 return
3997 }
3998 })*
3999
4000 _ => {}
4001 }
4002 }
4003 }
4004
4005 mem_opcode_match!(
4008 (CALLDATACOPY, 0, 2, true),
4009 (CODECOPY, 0, 2, true),
4010 (RETURNDATACOPY, 0, 2, true),
4011 (EXTCODECOPY, 1, 3, true),
4012 (CALLCODE, 5, 6, true),
4013 (STATICCALL, 4, 5, true),
4014 (DELEGATECALL, 4, 5, true),
4015 (KECCAK256, 0, 1, false),
4016 (LOG0, 0, 1, false),
4017 (LOG1, 0, 1, false),
4018 (LOG2, 0, 1, false),
4019 (LOG3, 0, 1, false),
4020 (LOG4, 0, 1, false),
4021 (CREATE, 1, 2, false),
4022 (CREATE2, 1, 2, false),
4023 (RETURN, 0, 1, false),
4024 (REVERT, 0, 1, false),
4025 );
4026 }
4027
4028 #[cold]
4029 fn set_gas_limit_type(&mut self, interpreter: &mut Interpreter) {
4030 match interpreter.bytecode.opcode() {
4031 op::CREATE2 => self.dynamic_gas_limit = true,
4032 op::CALL => {
4033 self.dynamic_gas_limit =
4036 try_or_return!(interpreter.stack.peek(0)) >= interpreter.gas.remaining() - 100
4037 }
4038 _ => self.dynamic_gas_limit = false,
4039 }
4040 }
4041}
4042
4043fn disallowed_mem_write(
4049 dest_offset: u64,
4050 size: u64,
4051 interpreter: &mut Interpreter,
4052 ranges: &[Range<u64>],
4053) {
4054 let revert_string = format!(
4055 "memory write at offset 0x{:02X} of size 0x{:02X} not allowed; safe range: {}",
4056 dest_offset,
4057 size,
4058 ranges.iter().map(|r| format!("[0x{:02X}, 0x{:02X})", r.start, r.end)).join(" U ")
4059 );
4060
4061 interpreter.bytecode.set_action(InterpreterAction::new_return(
4062 InstructionResult::Revert,
4063 Bytes::from(revert_string.into_bytes()),
4064 interpreter.gas,
4065 ));
4066}
4067
4068const fn access_is_call(kind: crate::Vm::AccountAccessKind) -> bool {
4070 matches!(
4071 kind,
4072 crate::Vm::AccountAccessKind::Call
4073 | crate::Vm::AccountAccessKind::StaticCall
4074 | crate::Vm::AccountAccessKind::CallCode
4075 | crate::Vm::AccountAccessKind::DelegateCall
4076 )
4077}
4078
4079fn record_logs(recorded_logs: &mut Option<Vec<Vm::Log>>, log: &Log) {
4081 if let Some(storage_recorded_logs) = recorded_logs {
4082 storage_recorded_logs.push(Vm::Log {
4083 topics: log.data.topics().to_vec(),
4084 data: log.data.data.clone(),
4085 emitter: log.address,
4086 });
4087 }
4088}
4089
4090fn append_storage_access(
4092 last: &mut Vec<AccountAccess>,
4093 storage_access: crate::Vm::StorageAccess,
4094 storage_depth: u64,
4095) {
4096 if !last.is_empty() && last.first().unwrap().depth < storage_depth {
4098 if last.len() == 1 {
4104 last.first_mut().unwrap().storageAccesses.push(storage_access);
4105 } else {
4106 let last_record = last.last_mut().unwrap();
4107 if last_record.kind as u8 == crate::Vm::AccountAccessKind::Resume as u8 {
4108 last_record.storageAccesses.push(storage_access);
4109 } else {
4110 let entry = last.first().unwrap();
4111 let resume_record = crate::Vm::AccountAccess {
4112 chainInfo: crate::Vm::ChainInfo {
4113 forkId: entry.chainInfo.forkId,
4114 chainId: entry.chainInfo.chainId,
4115 },
4116 accessor: entry.accessor,
4117 account: entry.account,
4118 kind: crate::Vm::AccountAccessKind::Resume,
4119 initialized: entry.initialized,
4120 storageAccesses: vec![storage_access],
4121 reverted: entry.reverted,
4122 oldBalance: U256::ZERO,
4124 newBalance: U256::ZERO,
4125 oldNonce: 0,
4126 newNonce: 0,
4127 value: U256::ZERO,
4128 data: Bytes::new(),
4129 deployedCode: Bytes::new(),
4130 depth: entry.depth,
4131 };
4132 last.push(resume_record);
4133 }
4134 }
4135 }
4136}
4137
4138const fn cheatcode_of<T: spec::CheatcodeDef>(_: &T) -> &'static spec::Cheatcode<'static> {
4140 T::CHEATCODE
4141}
4142
4143fn cheatcode_name(cheat: &spec::Cheatcode<'static>) -> &'static str {
4144 cheat.func.signature.split('(').next().unwrap()
4145}
4146
4147const fn cheatcode_id(cheat: &spec::Cheatcode<'static>) -> &'static str {
4148 cheat.func.id
4149}
4150
4151const fn cheatcode_signature(cheat: &spec::Cheatcode<'static>) -> &'static str {
4152 cheat.func.signature
4153}
4154
4155fn apply_dispatch<FEN: FoundryEvmNetwork>(
4157 calls: &Vm::VmCalls,
4158 ccx: &mut CheatsCtxt<'_, '_, FEN>,
4159 executor: &mut dyn CheatcodesExecutor<FEN>,
4160) -> Result {
4161 macro_rules! get_cheatcode {
4163 ($($variant:ident),*) => {
4164 match calls {
4165 $(Vm::VmCalls::$variant(cheat) => cheatcode_of(cheat),)*
4166 }
4167 };
4168 }
4169 let cheat = vm_calls!(get_cheatcode);
4170
4171 let _guard = debug_span!(target: "cheatcodes", "apply", id = %cheatcode_id(cheat)).entered();
4172 trace!(target: "cheatcodes", cheat = %cheatcode_signature(cheat), "applying");
4173
4174 if let spec::Status::Deprecated(replacement) = cheat.status {
4175 ccx.state.deprecated.insert(cheatcode_signature(cheat), replacement);
4176 }
4177
4178 macro_rules! dispatch {
4180 ($($variant:ident),*) => {
4181 match calls {
4182 $(Vm::VmCalls::$variant(cheat) => Cheatcode::apply_full(cheat, ccx, executor),)*
4183 }
4184 };
4185 }
4186 let mut result = if ccx.state.config.blocked_cheatcodes.contains(&cheat.func.selector_bytes) {
4187 Err(fmt_err!("disabled during restricted execution"))
4188 } else {
4189 vm_calls!(dispatch)
4190 };
4191
4192 if let Err(e) = &mut result
4194 && e.is_str()
4195 {
4196 let name = cheatcode_name(cheat);
4197 if !name.contains("assert") && name != "rpcUrl" {
4201 *e = fmt_err!("vm.{name}: {e}");
4202 }
4203 }
4204
4205 trace!(
4206 target: "cheatcodes",
4207 return = %match &result {
4208 Ok(b) => hex::encode(b),
4209 Err(e) => e.to_string(),
4210 }
4211 );
4212
4213 result
4214}
4215
4216const fn will_exit(action: &InterpreterAction) -> bool {
4218 match action {
4219 InterpreterAction::Return(result) => {
4220 result.result.is_ok_or_revert() || result.result.is_halt()
4221 }
4222 _ => false,
4223 }
4224}
4225
4226#[cfg(test)]
4227mod tests {
4228 use super::*;
4229
4230 fn cheats(flag: bool, broadcast: Option<Broadcast>) -> Cheatcodes {
4231 let config = CheatsConfig { batch_rewrite_creates: flag, ..Default::default() };
4232 let mut cheats = Cheatcodes::new(Arc::new(config));
4233 cheats.broadcast = broadcast;
4234 cheats
4235 }
4236
4237 fn create_inputs() -> CreateInputs {
4238 CreateInputs::new(Address::ZERO, CreateScheme::Create, U256::ZERO, Bytes::new(), 100_000, 0)
4239 }
4240
4241 fn broadcast_at(depth: usize) -> Broadcast {
4242 Broadcast { depth, ..Default::default() }
4243 }
4244
4245 #[test]
4246 fn flag_off_with_broadcast_returns_false() {
4247 let mut cheats = cheats(false, Some(broadcast_at(1)));
4248 assert!(!cheats.should_use_create2_factory(1, &create_inputs()));
4249 }
4250
4251 #[test]
4252 fn flag_on_without_broadcast_returns_false() {
4253 let mut cheats = cheats(true, None);
4254 assert!(!cheats.should_use_create2_factory(1, &create_inputs()));
4255 }
4256
4257 #[test]
4258 fn flag_on_with_broadcast_depth_mismatch_returns_false() {
4259 let mut cheats = cheats(true, Some(broadcast_at(2)));
4260 assert!(!cheats.should_use_create2_factory(1, &create_inputs()));
4261 }
4262
4263 #[test]
4264 fn flag_on_with_broadcast_depth_match_returns_true() {
4265 let mut cheats = cheats(true, Some(broadcast_at(1)));
4266 assert!(cheats.should_use_create2_factory(1, &create_inputs()));
4267 }
4268
4269 #[test]
4270 fn default_cheatcodes_have_no_opcode_hooks() {
4271 let cheats = Cheatcodes::<EthEvmNetwork>::new(Arc::default());
4272 assert!(!cheats.has_step_hooks());
4273 assert!(!cheats.has_step_end_hooks());
4274 assert!(!cheats.has_log_hooks());
4275 }
4276
4277 #[test]
4278 fn active_cheatcode_state_enables_opcode_hooks() {
4279 let mut cheats = Cheatcodes::<EthEvmNetwork>::new(Arc::default());
4280
4281 cheats.recording_accesses = true;
4282 assert!(cheats.has_step_hooks());
4283 assert!(!cheats.has_step_end_hooks());
4284 assert!(cheats.has_recording_accesses_only_step_hook());
4285
4286 cheats.recording_accesses = false;
4287 cheats.gas_metering.touched = true;
4288 assert!(!cheats.has_step_hooks());
4289 assert!(cheats.has_step_end_hooks());
4290 assert!(!cheats.has_recording_accesses_only_step_hook());
4291
4292 cheats.gas_metering.touched = false;
4293 cheats.register_storage_load_hook(Address::ZERO, Address::ZERO, [0; 4]);
4294 assert!(cheats.has_step_hooks());
4295 assert!(cheats.has_step_end_hooks());
4296 assert!(!cheats.has_recording_accesses_only_step_hook());
4297 }
4298
4299 #[test]
4300 fn mixed_step_hooks_disable_record_access_fast_path() {
4301 let mut cheats = Cheatcodes::<EthEvmNetwork>::new(Arc::default());
4302 cheats.recording_accesses = true;
4303
4304 cheats.gas_metering.reset = true;
4305 assert!(!cheats.has_recording_accesses_only_step_hook());
4306
4307 cheats.gas_metering.reset = false;
4308 cheats.env_overrides.insert(None, EnvOverrides { basefee: Some(1), ..Default::default() });
4309 assert!(!cheats.has_recording_accesses_only_step_hook());
4310 }
4311
4312 #[test]
4313 fn inactive_env_override_entries_do_not_enable_opcode_hooks() {
4314 let mut cheats = Cheatcodes::<EthEvmNetwork>::new(Arc::default());
4315 cheats.env_overrides.insert(None, EnvOverrides::default());
4316
4317 assert!(!cheats.has_step_hooks());
4318 assert!(!cheats.has_step_end_hooks());
4319
4320 cheats.env_overrides.get_mut(&None).unwrap().basefee = Some(1);
4321 assert!(cheats.has_step_hooks());
4322 assert!(cheats.has_step_end_hooks());
4323 }
4324
4325 #[test]
4326 fn active_log_state_enables_log_hooks() {
4327 let mut cheats = Cheatcodes::<EthEvmNetwork>::new(Arc::default());
4328
4329 cheats.recorded_logs = Some(Default::default());
4330 assert!(cheats.has_log_hooks());
4331
4332 cheats.recorded_logs = None;
4333 cheats.expected_emits.push_back((
4334 expect::ExpectedEmit {
4335 depth: 0,
4336 log: None,
4337 checks: [false; 5],
4338 address: None,
4339 anonymous: false,
4340 found: false,
4341 count: 1,
4342 mismatch_error: None,
4343 },
4344 Default::default(),
4345 ));
4346 assert!(cheats.has_log_hooks());
4347 }
4348
4349 #[test]
4350 fn frame_gas_reports_settled_components() {
4351 for mut gas in [Gas::new(100_000), Gas::new_with_regular_gas_and_reservoir(100_000, 50_000)]
4352 {
4353 assert!(gas.record_regular_cost(1_000));
4354 assert!(gas.record_state_cost(20_000));
4355
4356 let mut result = InterpreterResult::new(InstructionResult::Stop, Bytes::new(), gas);
4357 let reported = frame_gas(&result);
4358 assert_eq!(reported.gasTotalUsed, 1_000);
4359 assert_eq!(reported.gasStateUsed, 20_000);
4360
4361 result.result = InstructionResult::Revert;
4362 assert_eq!(frame_gas(&result).gasStateUsed, 0);
4363 }
4364
4365 let mut gas = Gas::new(100_000);
4366 gas.refill_reservoir(20_000);
4367 let result = InterpreterResult::new(InstructionResult::Stop, Bytes::new(), gas);
4368 assert_eq!(frame_gas(&result).gasStateUsed, -20_000);
4369
4370 let mut gas = Gas::new(100_000);
4371 assert!(gas.record_state_cost(20_000));
4372 gas.spend_all();
4373 let result = InterpreterResult::new(InstructionResult::OutOfGas, Bytes::new(), gas);
4374 let reported = frame_gas(&result);
4375 assert_eq!(reported.gasTotalUsed, 100_000);
4376 assert_eq!(reported.gasStateUsed, 0);
4377 }
4378
4379 #[test]
4380 fn arbitrary_storage_cache_value_routes_copied_targets_to_source() {
4381 let mut storage = ArbitraryStorage::default();
4382 let source = Address::repeat_byte(0x11);
4383 let copied = Address::repeat_byte(0x22);
4384 let slot = U256::from(7);
4385
4386 storage.mark_arbitrary(&source, false);
4387 storage.mark_copy(&source, &copied);
4388 storage.cache_value(copied, slot, U256::ZERO);
4389
4390 assert_eq!(storage.cached_value(source, slot), Some(U256::ZERO));
4391 }
4392}