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_primitives::{
25 Address, B256, Bytes, Log, TxKind, U256, hex,
26 map::{AddressHashMap, HashMap, HashSet},
27};
28use alloy_rpc_types::AccessList;
29use alloy_sol_types::{SolCall, SolInterface, SolValue};
30use foundry_common::{
31 SELECTOR_LEN,
32 mapping_slots::{MappingSlots, step as mapping_step},
33};
34use foundry_evm_core::{
35 Breakpoints, EthCheatCtx, EvmEnv, FoundryInspectorExt, FoundryTransaction,
36 abi::Vm::stopExpectSafeMemoryCall,
37 backend::{DatabaseError, DatabaseExt, RevertDiagnostic},
38 constants::{CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS, MAGIC_ASSUME},
39 env::FoundryContextExt,
40 evm::{NestedEvm, NestedEvmClosure, new_eth_evm_with_inspector, with_cloned_context},
41};
42use foundry_evm_traces::{
43 TracingInspector, TracingInspectorConfig, identifier::SignaturesIdentifier,
44};
45use foundry_wallets::wallet_multi::MultiWallet;
46use itertools::Itertools;
47use proptest::test_runner::{RngAlgorithm, TestRng, TestRunner};
48use rand::Rng;
49use revm::{
50 Inspector,
51 bytecode::opcode as op,
52 context::{
53 BlockEnv, Cfg, ContextTr, JournalTr, Transaction, TransactionType, result::EVMError,
54 },
55 context_interface::{CreateScheme, transaction::SignedAuthorization},
56 handler::FrameResult,
57 inspector::JournalExt,
58 interpreter::{
59 CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, FrameInput, Gas,
60 InstructionResult, Interpreter, InterpreterAction, InterpreterResult,
61 interpreter_types::{Jumps, LoopControl, MemoryTr},
62 },
63 primitives::hardfork::SpecId,
64};
65use serde_json::Value;
66use std::{
67 cmp::max,
68 collections::{BTreeMap, VecDeque},
69 fmt::Debug,
70 fs::File,
71 io::BufReader,
72 ops::Range,
73 path::PathBuf,
74 sync::{Arc, OnceLock},
75};
76
77mod utils;
78
79pub mod analysis;
80pub use analysis::CheatcodeAnalysis;
81
82pub trait CheatcodesExecutor<CTX: ContextTr> {
84 fn with_nested_evm(
87 &mut self,
88 cheats: &mut Cheatcodes,
89 ecx: &mut CTX,
90 f: NestedEvmClosure<'_, CTX::Block, CTX::Tx, <CTX::Cfg as Cfg>::Spec>,
91 ) -> Result<(), EVMError<DatabaseError>>;
92
93 fn transact_on_db(
95 &mut self,
96 cheats: &mut Cheatcodes,
97 ecx: &mut CTX,
98 fork_id: Option<U256>,
99 transaction: B256,
100 ) -> eyre::Result<()>;
101
102 fn transact_from_tx_on_db(
104 &mut self,
105 cheats: &mut Cheatcodes,
106 ecx: &mut CTX,
107 tx: &CTX::Tx,
108 ) -> eyre::Result<()>;
109
110 fn with_fresh_nested_evm(
114 &mut self,
115 cheats: &mut Cheatcodes,
116 db: &mut dyn DatabaseExt<CTX::Block, CTX::Tx, <CTX::Cfg as Cfg>::Spec>,
117 evm_env: EvmEnv<<CTX::Cfg as Cfg>::Spec, CTX::Block>,
118 tx_env: CTX::Tx,
119 f: NestedEvmClosure<'_, CTX::Block, CTX::Tx, <CTX::Cfg as Cfg>::Spec>,
120 ) -> Result<(), EVMError<DatabaseError>>;
121
122 fn console_log(&mut self, cheats: &mut Cheatcodes, msg: &str);
124
125 fn tracing_inspector(&mut self) -> Option<&mut TracingInspector> {
127 None
128 }
129
130 fn set_in_inner_context(&mut self, _enabled: bool, _original_origin: Option<Address>) {}
134}
135
136pub(crate) fn exec_create<CTX: EthCheatCtx>(
138 executor: &mut dyn CheatcodesExecutor<CTX>,
139 inputs: CreateInputs,
140 ccx: &mut CheatsCtxt<'_, CTX>,
141) -> std::result::Result<CreateOutcome, EVMError<DatabaseError>> {
142 let mut inputs = Some(inputs);
143 let mut outcome = None;
144 executor.with_nested_evm(ccx.state, ccx.ecx, &mut |evm| {
145 let inputs = inputs.take().unwrap();
146 evm.journal_inner_mut().depth += 1;
147
148 let frame = FrameInput::Create(Box::new(inputs));
149
150 let result = match evm.run_execution(frame)? {
151 FrameResult::Call(_) => unreachable!(),
152 FrameResult::Create(create) => create,
153 };
154
155 evm.journal_inner_mut().depth -= 1;
156
157 outcome = Some(result);
158 Ok(())
159 })?;
160 Ok(outcome.unwrap())
161}
162
163#[derive(Debug, Default, Clone, Copy)]
166struct TransparentCheatcodesExecutor;
167
168impl<CTX: EthCheatCtx> CheatcodesExecutor<CTX> for TransparentCheatcodesExecutor {
169 fn with_nested_evm(
170 &mut self,
171 cheats: &mut Cheatcodes,
172 ecx: &mut CTX,
173 f: NestedEvmClosure<'_, CTX::Block, CTX::Tx, <CTX::Cfg as Cfg>::Spec>,
174 ) -> Result<(), EVMError<DatabaseError>> {
175 with_cloned_context(ecx, |db, evm_env, tx_env, journal_inner| {
176 let mut evm = new_eth_evm_with_inspector(db, evm_env, tx_env, cheats);
177 *evm.journal_inner_mut() = journal_inner;
178 f(&mut evm)?;
179 let sub_evm_env = evm.to_evm_env();
180 let sub_inner = evm.journaled_state.inner.clone();
181 Ok((sub_evm_env, sub_inner))
182 })
183 }
184
185 fn with_fresh_nested_evm(
186 &mut self,
187 cheats: &mut Cheatcodes,
188 db: &mut dyn DatabaseExt<CTX::Block, CTX::Tx, <CTX::Cfg as Cfg>::Spec>,
189 evm_env: EvmEnv<<CTX::Cfg as Cfg>::Spec, CTX::Block>,
190 tx_env: CTX::Tx,
191 f: NestedEvmClosure<'_, CTX::Block, CTX::Tx, <CTX::Cfg as Cfg>::Spec>,
192 ) -> Result<(), EVMError<DatabaseError>> {
193 let mut evm = new_eth_evm_with_inspector(db, evm_env, tx_env, cheats);
194 f(&mut evm)
195 }
196
197 fn transact_on_db(
198 &mut self,
199 cheats: &mut Cheatcodes,
200 ecx: &mut CTX,
201 fork_id: Option<U256>,
202 transaction: B256,
203 ) -> eyre::Result<()> {
204 let evm_env = ecx.evm_clone();
205 let tx_env = ecx.tx_clone();
206 let (db, inner) = ecx.db_journal_inner_mut();
207 db.transact(fork_id, transaction, evm_env, tx_env, inner, cheats)
208 }
209
210 fn transact_from_tx_on_db(
211 &mut self,
212 cheats: &mut Cheatcodes,
213 ecx: &mut CTX,
214 tx: &CTX::Tx,
215 ) -> eyre::Result<()> {
216 let evm_env = ecx.evm_clone();
217 let (db, inner) = ecx.db_journal_inner_mut();
218 db.transact_from_tx(tx, evm_env, inner, cheats)
219 }
220
221 fn console_log(&mut self, _cheats: &mut Cheatcodes, _msg: &str) {}
222}
223
224macro_rules! try_or_return {
225 ($e:expr) => {
226 match $e {
227 Ok(v) => v,
228 Err(_) => return,
229 }
230 };
231}
232
233#[derive(Debug, Default)]
235pub struct TestContext {
236 pub opened_read_files: HashMap<PathBuf, BufReader<File>>,
238}
239
240impl Clone for TestContext {
242 fn clone(&self) -> Self {
243 Default::default()
244 }
245}
246
247impl TestContext {
248 pub fn clear(&mut self) {
250 self.opened_read_files.clear();
251 }
252}
253
254#[derive(Clone, Debug)]
260pub struct BroadcastableTransaction {
261 pub rpc: Option<String>,
263 pub from: Address,
265 pub to: Option<TxKind>,
267 pub value: U256,
269 pub input: Bytes,
271 pub nonce: u64,
273 pub gas: Option<u64>,
275 pub kind: BroadcastKind,
277}
278
279#[derive(Clone, Debug)]
282pub enum BroadcastKind {
283 Unsigned {
285 chain_id: Option<u64>,
286 blob_sidecar: Option<BlobTransactionSidecarVariant>,
287 authorization_list: Option<Vec<SignedAuthorization>>,
288 },
289 Signed(Bytes),
291}
292
293impl BroadcastKind {
294 pub fn unsigned() -> Self {
296 Self::Unsigned { chain_id: None, blob_sidecar: None, authorization_list: None }
297 }
298}
299
300#[derive(Clone, Debug, Copy)]
301pub struct RecordDebugStepInfo {
302 pub start_node_idx: usize,
304 pub original_tracer_config: TracingInspectorConfig,
306}
307
308#[derive(Clone, Debug, Default)]
310pub struct GasMetering {
311 pub paused: bool,
313 pub touched: bool,
316 pub reset: bool,
318 pub paused_frames: Vec<Gas>,
320
321 pub active_gas_snapshot: Option<(String, String)>,
323
324 pub last_call_gas: Option<crate::Vm::Gas>,
327
328 pub recording: bool,
330 pub last_gas_used: u64,
332 pub gas_records: Vec<GasRecord>,
334}
335
336impl GasMetering {
337 pub fn start(&mut self) {
339 self.recording = true;
340 }
341
342 pub fn stop(&mut self) {
344 self.recording = false;
345 }
346
347 pub fn resume(&mut self) {
349 if self.paused {
350 self.paused = false;
351 self.touched = true;
352 }
353 self.paused_frames.clear();
354 }
355
356 pub fn reset(&mut self) {
358 self.paused = false;
359 self.touched = true;
360 self.reset = true;
361 self.paused_frames.clear();
362 }
363}
364
365#[derive(Clone, Debug, Default)]
367pub struct ArbitraryStorage {
368 pub values: HashMap<Address, HashMap<U256, U256>>,
372 pub copies: HashMap<Address, Address>,
374 pub overwrites: HashSet<Address>,
376}
377
378impl ArbitraryStorage {
379 pub fn mark_arbitrary(&mut self, address: &Address, overwrite: bool) {
381 self.values.insert(*address, HashMap::default());
382 if overwrite {
383 self.overwrites.insert(*address);
384 } else {
385 self.overwrites.remove(address);
386 }
387 }
388
389 pub fn mark_copy(&mut self, from: &Address, to: &Address) {
391 if self.values.contains_key(from) {
392 self.copies.insert(*to, *from);
393 }
394 }
395
396 pub fn save<CTX: ContextTr>(
400 &mut self,
401 ecx: &mut CTX,
402 address: Address,
403 slot: U256,
404 data: U256,
405 ) {
406 self.values.get_mut(&address).expect("missing arbitrary address entry").insert(slot, data);
407 if ecx.journal_mut().load_account(address).is_ok() {
408 ecx.journal_mut()
409 .sstore(address, slot, data)
410 .expect("could not set arbitrary storage value");
411 }
412 }
413
414 pub fn copy<CTX: ContextTr>(
420 &mut self,
421 ecx: &mut CTX,
422 target: Address,
423 slot: U256,
424 new_value: U256,
425 ) -> U256 {
426 let source = self.copies.get(&target).expect("missing arbitrary copy target entry");
427 let storage_cache = self.values.get_mut(source).expect("missing arbitrary source storage");
428 let value = match storage_cache.get(&slot) {
429 Some(value) => *value,
430 None => {
431 storage_cache.insert(slot, new_value);
432 if ecx.journal_mut().load_account(*source).is_ok() {
434 ecx.journal_mut()
435 .sstore(*source, slot, new_value)
436 .expect("could not copy arbitrary storage value");
437 }
438 new_value
439 }
440 };
441 if ecx.journal_mut().load_account(target).is_ok() {
443 ecx.journal_mut().sstore(target, slot, value).expect("could not set storage");
444 }
445 value
446 }
447}
448
449pub type BroadcastableTransactions = VecDeque<BroadcastableTransaction>;
451
452#[derive(Clone, Debug)]
470pub struct Cheatcodes {
471 pub analysis: Option<CheatcodeAnalysis>,
473
474 pub block: Option<BlockEnv>,
479
480 pub active_delegations: Vec<SignedAuthorization>,
484
485 pub active_blob_sidecar: Option<BlobTransactionSidecarVariant>,
487
488 pub gas_price: Option<u128>,
493
494 pub labels: AddressHashMap<String>,
496
497 pub pranks: BTreeMap<usize, Prank>,
499
500 pub expected_revert: Option<ExpectedRevert>,
502
503 pub assume_no_revert: Option<AssumeNoRevert>,
505
506 pub fork_revert_diagnostic: Option<RevertDiagnostic>,
508
509 pub accesses: RecordAccess,
511
512 pub recording_accesses: bool,
514
515 pub recorded_account_diffs_stack: Option<Vec<Vec<AccountAccess>>>,
521
522 pub record_debug_steps_info: Option<RecordDebugStepInfo>,
524
525 pub recorded_logs: Option<Vec<crate::Vm::Log>>,
527
528 pub mocked_calls: HashMap<Address, BTreeMap<MockCallDataContext, VecDeque<MockCallReturnData>>>,
531
532 pub mocked_functions: HashMap<Address, HashMap<Bytes, Address>>,
534
535 pub expected_calls: ExpectedCallTracker,
537 pub expected_emits: ExpectedEmitTracker,
539 pub expected_creates: Vec<ExpectedCreate>,
541
542 pub allowed_mem_writes: HashMap<u64, Vec<Range<u64>>>,
544
545 pub broadcast: Option<Broadcast>,
547
548 pub broadcastable_transactions: BroadcastableTransactions,
550
551 pub access_list: Option<AccessList>,
553
554 pub config: Arc<CheatsConfig>,
556
557 pub test_context: TestContext,
559
560 pub fs_commit: bool,
563
564 pub serialized_jsons: BTreeMap<String, BTreeMap<String, Value>>,
567
568 pub eth_deals: Vec<DealRecord>,
570
571 pub gas_metering: GasMetering,
573
574 pub gas_snapshots: BTreeMap<String, BTreeMap<String, String>>,
577
578 pub mapping_slots: Option<AddressHashMap<MappingSlots>>,
580
581 pub pc: usize,
583 pub breakpoints: Breakpoints,
586
587 pub intercept_next_create_call: bool,
589
590 test_runner: Option<TestRunner>,
593
594 pub ignored_traces: IgnoredTraces,
596
597 pub arbitrary_storage: Option<ArbitraryStorage>,
599
600 pub deprecated: HashMap<&'static str, Option<&'static str>>,
602 pub wallets: Option<Wallets>,
604 signatures_identifier: OnceLock<Option<SignaturesIdentifier>>,
606 pub dynamic_gas_limit: bool,
608 pub execution_evm_version: Option<SpecId>,
610}
611
612impl Default for Cheatcodes {
616 fn default() -> Self {
617 Self::new(Arc::default())
618 }
619}
620
621impl Cheatcodes {
622 pub fn new(config: Arc<CheatsConfig>) -> Self {
624 Self {
625 analysis: None,
626 fs_commit: true,
627 labels: config.labels.clone(),
628 config,
629 block: Default::default(),
630 active_delegations: Default::default(),
631 active_blob_sidecar: Default::default(),
632 gas_price: Default::default(),
633 pranks: Default::default(),
634 expected_revert: Default::default(),
635 assume_no_revert: Default::default(),
636 fork_revert_diagnostic: Default::default(),
637 accesses: Default::default(),
638 recording_accesses: Default::default(),
639 recorded_account_diffs_stack: Default::default(),
640 recorded_logs: Default::default(),
641 record_debug_steps_info: Default::default(),
642 mocked_calls: Default::default(),
643 mocked_functions: Default::default(),
644 expected_calls: Default::default(),
645 expected_emits: Default::default(),
646 expected_creates: Default::default(),
647 allowed_mem_writes: Default::default(),
648 broadcast: Default::default(),
649 broadcastable_transactions: Default::default(),
650 access_list: Default::default(),
651 test_context: Default::default(),
652 serialized_jsons: Default::default(),
653 eth_deals: Default::default(),
654 gas_metering: Default::default(),
655 gas_snapshots: Default::default(),
656 mapping_slots: Default::default(),
657 pc: Default::default(),
658 breakpoints: Default::default(),
659 intercept_next_create_call: Default::default(),
660 test_runner: Default::default(),
661 ignored_traces: Default::default(),
662 arbitrary_storage: Default::default(),
663 deprecated: Default::default(),
664 wallets: Default::default(),
665 signatures_identifier: Default::default(),
666 dynamic_gas_limit: Default::default(),
667 execution_evm_version: None,
668 }
669 }
670
671 pub fn set_analysis(&mut self, analysis: CheatcodeAnalysis) {
673 self.analysis = Some(analysis);
674 }
675
676 pub fn get_prank(&self, depth: usize) -> Option<&Prank> {
680 self.pranks.range(..=depth).last().map(|(_, prank)| prank)
681 }
682
683 pub fn wallets(&mut self) -> &Wallets {
685 self.wallets.get_or_insert_with(|| Wallets::new(MultiWallet::default(), None))
686 }
687
688 pub fn set_wallets(&mut self, wallets: Wallets) {
690 self.wallets = Some(wallets);
691 }
692
693 pub fn add_delegation(&mut self, authorization: SignedAuthorization) {
695 self.active_delegations.push(authorization);
696 }
697
698 pub fn signatures_identifier(&self) -> Option<&SignaturesIdentifier> {
700 self.signatures_identifier.get_or_init(|| SignaturesIdentifier::new(true).ok()).as_ref()
701 }
702
703 fn apply_cheatcode<CTX: EthCheatCtx>(
705 &mut self,
706 ecx: &mut CTX,
707 call: &CallInputs,
708 executor: &mut dyn CheatcodesExecutor<CTX>,
709 ) -> Result {
710 let decoded = Vm::VmCalls::abi_decode(&call.input.bytes(ecx)).map_err(|e| {
712 if let alloy_sol_types::Error::UnknownSelector { name: _, selector } = e {
713 let msg = format!(
714 "unknown cheatcode with selector {selector}; \
715 you may have a mismatch between the `Vm` interface (likely in `forge-std`) \
716 and the `forge` version"
717 );
718 return alloy_sol_types::Error::Other(std::borrow::Cow::Owned(msg));
719 }
720 e
721 })?;
722
723 let caller = call.caller;
724
725 ecx.db_mut().ensure_cheatcode_access_forking_mode(&caller)?;
728
729 apply_dispatch(
730 &decoded,
731 &mut CheatsCtxt { state: self, ecx, gas_limit: call.gas_limit, caller },
732 executor,
733 )
734 }
735
736 fn allow_cheatcodes_on_create<CTX: ContextTr<Db: DatabaseExt>>(
742 &self,
743 ecx: &mut CTX,
744 caller: Address,
745 created_address: Address,
746 ) {
747 if ecx.journal().depth() <= 1 || ecx.db().has_cheatcode_access(&caller) {
748 ecx.db_mut().allow_cheatcode_access(created_address);
749 }
750 }
751
752 fn apply_accesslist<CTX: FoundryContextExt>(&mut self, ecx: &mut CTX) {
758 if let Some(access_list) = &self.access_list {
759 ecx.tx_mut().set_access_list(access_list.clone());
760
761 if ecx.tx().tx_type() == TransactionType::Legacy as u8 {
762 ecx.tx_mut().set_tx_type(TransactionType::Eip2930 as u8);
763 }
764 }
765 }
766
767 pub fn on_revert<CTX: ContextTr<Journal: JournalExt>>(&mut self, ecx: &mut CTX) {
772 trace!(deals=?self.eth_deals.len(), "rolling back deals");
773
774 if self.expected_revert.is_some() {
776 return;
777 }
778
779 if ecx.journal().depth() > 0 {
781 return;
782 }
783
784 while let Some(record) = self.eth_deals.pop() {
788 if let Some(acc) = ecx.journal_mut().evm_state_mut().get_mut(&record.address) {
789 acc.info.balance = record.old_balance;
790 }
791 }
792 }
793
794 pub fn call_with_executor<CTX: EthCheatCtx>(
795 &mut self,
796 ecx: &mut CTX,
797 call: &mut CallInputs,
798 executor: &mut dyn CheatcodesExecutor<CTX>,
799 ) -> Option<CallOutcome> {
800 if let Some(spec_id) = self.execution_evm_version {
802 ecx.cfg_mut().set_spec(spec_id);
803 }
804
805 let gas = Gas::new(call.gas_limit);
806 let curr_depth = ecx.journal().depth();
807
808 if curr_depth == 0 {
812 let sender = ecx.tx().caller();
813 let account = match super::evm::journaled_account(ecx, sender) {
814 Ok(account) => account,
815 Err(err) => {
816 return Some(CallOutcome {
817 result: InterpreterResult {
818 result: InstructionResult::Revert,
819 output: err.abi_encode().into(),
820 gas,
821 },
822 memory_offset: call.return_memory_offset.clone(),
823 was_precompile_called: false,
824 precompile_call_logs: vec![],
825 });
826 }
827 };
828 let prev = account.info.nonce;
829 account.info.nonce = prev.saturating_sub(1);
830
831 trace!(target: "cheatcodes", %sender, nonce=account.info.nonce, prev, "corrected nonce");
832 }
833
834 if call.target_address == CHEATCODE_ADDRESS {
835 return match self.apply_cheatcode(ecx, call, executor) {
836 Ok(retdata) => Some(CallOutcome {
837 result: InterpreterResult {
838 result: InstructionResult::Return,
839 output: retdata.into(),
840 gas,
841 },
842 memory_offset: call.return_memory_offset.clone(),
843 was_precompile_called: true,
844 precompile_call_logs: vec![],
845 }),
846 Err(err) => Some(CallOutcome {
847 result: InterpreterResult {
848 result: InstructionResult::Revert,
849 output: err.abi_encode().into(),
850 gas,
851 },
852 memory_offset: call.return_memory_offset.clone(),
853 was_precompile_called: false,
854 precompile_call_logs: vec![],
855 }),
856 };
857 }
858
859 if call.target_address == HARDHAT_CONSOLE_ADDRESS {
860 return None;
861 }
862
863 if let Some(expected) = &mut self.expected_revert {
867 expected.max_depth = max(curr_depth + 1, expected.max_depth);
868 }
869
870 if let Some(expected_calls_for_target) = self.expected_calls.get_mut(&call.bytecode_address)
874 {
875 for (calldata, (expected, actual_count)) in expected_calls_for_target {
877 if calldata.len() <= call.input.len() &&
880 *calldata == call.input.bytes(ecx)[..calldata.len()] &&
882 expected
884 .value.is_none_or(|value| Some(value) == call.transfer_value()) &&
885 expected.gas.is_none_or(|gas| gas == call.gas_limit) &&
887 expected.min_gas.is_none_or(|min_gas| min_gas <= call.gas_limit)
889 {
890 *actual_count += 1;
891 }
892 }
893 }
894
895 if let Some(mocks) = self.mocked_calls.get_mut(&call.bytecode_address) {
897 let ctx = MockCallDataContext {
898 calldata: call.input.bytes(ecx),
899 value: call.transfer_value(),
900 };
901
902 if let Some(return_data_queue) = match mocks.get_mut(&ctx) {
903 Some(queue) => Some(queue),
904 None => mocks
905 .iter_mut()
906 .find(|(mock, _)| {
907 call.input.bytes(ecx).get(..mock.calldata.len()) == Some(&mock.calldata[..])
908 && mock.value.is_none_or(|value| Some(value) == call.transfer_value())
909 })
910 .map(|(_, v)| v),
911 } && let Some(return_data) = if return_data_queue.len() == 1 {
912 return_data_queue.front().map(|x| x.to_owned())
914 } else {
915 return_data_queue.pop_front()
917 } {
918 return Some(CallOutcome {
919 result: InterpreterResult {
920 result: return_data.ret_type,
921 output: return_data.data,
922 gas,
923 },
924 memory_offset: call.return_memory_offset.clone(),
925 was_precompile_called: true,
926 precompile_call_logs: vec![],
927 });
928 }
929 }
930
931 if let Some(prank) = &self.get_prank(curr_depth) {
933 if prank.delegate_call
935 && curr_depth == prank.depth
936 && let CallScheme::DelegateCall = call.scheme
937 {
938 call.target_address = prank.new_caller;
939 call.caller = prank.new_caller;
940 if let Some(new_origin) = prank.new_origin {
941 ecx.tx_mut().set_caller(new_origin);
942 }
943 }
944
945 if curr_depth >= prank.depth && call.caller == prank.prank_caller {
946 let mut prank_applied = false;
947
948 if curr_depth == prank.depth {
950 let _ = journaled_account(ecx, prank.new_caller);
952 call.caller = prank.new_caller;
953 prank_applied = true;
954 }
955
956 if let Some(new_origin) = prank.new_origin {
958 ecx.tx_mut().set_caller(new_origin);
959 prank_applied = true;
960 }
961
962 if prank_applied && let Some(applied_prank) = prank.first_time_applied() {
964 self.pranks.insert(curr_depth, applied_prank);
965 }
966 }
967 }
968
969 self.apply_accesslist(ecx);
971
972 if let Some(broadcast) = &self.broadcast {
974 let is_fixed_gas_limit = call.gas_limit >= 21_000 && !self.dynamic_gas_limit;
977 self.dynamic_gas_limit = false;
978
979 if curr_depth == broadcast.depth && call.caller == broadcast.original_caller {
984 ecx.tx_mut().set_caller(broadcast.new_origin);
988
989 call.caller = broadcast.new_origin;
990 if !call.is_static {
995 if let Err(err) = ecx.journal_mut().load_account(broadcast.new_origin) {
996 return Some(CallOutcome {
997 result: InterpreterResult {
998 result: InstructionResult::Revert,
999 output: Error::encode(err),
1000 gas,
1001 },
1002 memory_offset: call.return_memory_offset.clone(),
1003 was_precompile_called: false,
1004 precompile_call_logs: vec![],
1005 });
1006 }
1007
1008 let input = call.input.bytes(ecx);
1009 let chain_id = ecx.cfg().chain_id();
1010 let rpc = ecx.db().active_fork_url();
1011 let account =
1012 ecx.journal_mut().evm_state_mut().get_mut(&broadcast.new_origin).unwrap();
1013
1014 let blob_sidecar = self.active_blob_sidecar.take();
1015 let active_delegations = std::mem::take(&mut self.active_delegations);
1016
1017 if blob_sidecar.is_some() && !active_delegations.is_empty() {
1019 let msg = "both delegation and blob are active; `attachBlob` and `attachDelegation` are not compatible";
1020 return Some(CallOutcome {
1021 result: InterpreterResult {
1022 result: InstructionResult::Revert,
1023 output: Error::encode(msg),
1024 gas,
1025 },
1026 memory_offset: call.return_memory_offset.clone(),
1027 was_precompile_called: false,
1028 precompile_call_logs: vec![],
1029 });
1030 }
1031
1032 let nonce = account.info.nonce;
1035
1036 let authorization_list = if !active_delegations.is_empty() {
1038 for auth in &active_delegations {
1039 let Ok(authority) = auth.recover_authority() else {
1040 continue;
1041 };
1042 if authority == broadcast.new_origin {
1043 account.info.nonce += 1;
1046 }
1047 }
1048 Some(active_delegations)
1049 } else {
1050 None
1051 };
1052
1053 self.broadcastable_transactions.push_back(BroadcastableTransaction {
1054 rpc,
1055 from: broadcast.new_origin,
1056 to: Some(TxKind::from(Some(call.target_address))),
1057 value: call.transfer_value().unwrap_or_default(),
1058 input,
1059 nonce,
1060 gas: if is_fixed_gas_limit { Some(call.gas_limit) } else { None },
1061 kind: BroadcastKind::Unsigned {
1062 chain_id: Some(chain_id),
1063 blob_sidecar,
1064 authorization_list,
1065 },
1066 });
1067 debug!(target: "cheatcodes", tx=?self.broadcastable_transactions.back().unwrap(), "broadcastable call");
1068
1069 if !self.config.evm_opts.isolate {
1071 let prev = account.info.nonce;
1072 account.info.nonce += 1;
1073 debug!(target: "cheatcodes", address=%broadcast.new_origin, nonce=prev+1, prev, "incremented nonce");
1074 }
1075 } else if broadcast.single_call {
1076 let msg = "`staticcall`s are not allowed after `broadcast`; use `startBroadcast` instead";
1077 return Some(CallOutcome {
1078 result: InterpreterResult {
1079 result: InstructionResult::Revert,
1080 output: Error::encode(msg),
1081 gas,
1082 },
1083 memory_offset: call.return_memory_offset.clone(),
1084 was_precompile_called: false,
1085 precompile_call_logs: vec![],
1086 });
1087 }
1088 }
1089 }
1090
1091 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1093 let initialized;
1096 let old_balance;
1097 let old_nonce;
1098
1099 if let Ok(acc) = ecx.journal_mut().load_account(call.target_address) {
1100 initialized = acc.data.info.exists();
1101 old_balance = acc.data.info.balance;
1102 old_nonce = acc.data.info.nonce;
1103 } else {
1104 initialized = false;
1105 old_balance = U256::ZERO;
1106 old_nonce = 0;
1107 }
1108
1109 let kind = match call.scheme {
1110 CallScheme::Call => crate::Vm::AccountAccessKind::Call,
1111 CallScheme::CallCode => crate::Vm::AccountAccessKind::CallCode,
1112 CallScheme::DelegateCall => crate::Vm::AccountAccessKind::DelegateCall,
1113 CallScheme::StaticCall => crate::Vm::AccountAccessKind::StaticCall,
1114 };
1115
1116 recorded_account_diffs_stack.push(vec![AccountAccess {
1122 chainInfo: crate::Vm::ChainInfo {
1123 forkId: ecx.db().active_fork_id().unwrap_or_default(),
1124 chainId: U256::from(ecx.cfg().chain_id()),
1125 },
1126 accessor: call.caller,
1127 account: call.bytecode_address,
1128 kind,
1129 initialized,
1130 oldBalance: old_balance,
1131 newBalance: U256::ZERO, oldNonce: old_nonce,
1133 newNonce: 0, value: call.call_value(),
1135 data: call.input.bytes(ecx),
1136 reverted: false,
1137 deployedCode: Bytes::new(),
1138 storageAccesses: vec![], depth: ecx.journal().depth().try_into().expect("journaled state depth exceeds u64"),
1140 }]);
1141 }
1142
1143 None
1144 }
1145
1146 pub fn rng(&mut self) -> &mut impl Rng {
1147 self.test_runner().rng()
1148 }
1149
1150 pub fn test_runner(&mut self) -> &mut TestRunner {
1151 self.test_runner.get_or_insert_with(|| match self.config.seed {
1152 Some(seed) => TestRunner::new_with_rng(
1153 proptest::test_runner::Config::default(),
1154 TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>()),
1155 ),
1156 None => TestRunner::new(proptest::test_runner::Config::default()),
1157 })
1158 }
1159
1160 pub fn set_seed(&mut self, seed: U256) {
1161 self.test_runner = Some(TestRunner::new_with_rng(
1162 proptest::test_runner::Config::default(),
1163 TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>()),
1164 ));
1165 }
1166
1167 pub fn arbitrary_storage(&mut self) -> &mut ArbitraryStorage {
1170 self.arbitrary_storage.get_or_insert_with(ArbitraryStorage::default)
1171 }
1172
1173 pub fn has_arbitrary_storage(&self, address: &Address) -> bool {
1175 match &self.arbitrary_storage {
1176 Some(storage) => storage.values.contains_key(address),
1177 None => false,
1178 }
1179 }
1180
1181 pub fn should_overwrite_arbitrary_storage(
1185 &self,
1186 address: &Address,
1187 storage_slot: U256,
1188 ) -> bool {
1189 match &self.arbitrary_storage {
1190 Some(storage) => {
1191 storage.overwrites.contains(address)
1192 && storage
1193 .values
1194 .get(address)
1195 .and_then(|arbitrary_values| arbitrary_values.get(&storage_slot))
1196 .is_none()
1197 }
1198 None => false,
1199 }
1200 }
1201
1202 pub fn is_arbitrary_storage_copy(&self, address: &Address) -> bool {
1204 match &self.arbitrary_storage {
1205 Some(storage) => storage.copies.contains_key(address),
1206 None => false,
1207 }
1208 }
1209
1210 pub fn struct_defs(&self) -> Option<&foundry_common::fmt::StructDefinitions> {
1212 self.analysis.as_ref().and_then(|analysis| analysis.struct_defs().ok())
1213 }
1214}
1215
1216impl<CTX: EthCheatCtx> Inspector<CTX> for Cheatcodes {
1217 fn initialize_interp(&mut self, interpreter: &mut Interpreter, ecx: &mut CTX) {
1218 if let Some(block) = self.block.take() {
1221 ecx.set_block(block);
1222 }
1223 if let Some(gas_price) = self.gas_price.take() {
1224 ecx.tx_mut().set_gas_price(gas_price);
1225 }
1226
1227 if self.gas_metering.paused {
1229 self.gas_metering.paused_frames.push(interpreter.gas);
1230 }
1231
1232 if let Some(expected) = &mut self.expected_revert {
1234 expected.max_depth = max(ecx.journal().depth(), expected.max_depth);
1235 }
1236 }
1237
1238 fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut CTX) {
1239 self.pc = interpreter.bytecode.pc();
1240
1241 if self.broadcast.is_some() {
1242 self.set_gas_limit_type(interpreter);
1243 }
1244
1245 if self.gas_metering.paused {
1247 self.meter_gas(interpreter);
1248 }
1249
1250 if self.gas_metering.reset {
1252 self.meter_gas_reset(interpreter);
1253 }
1254
1255 if self.recording_accesses {
1257 self.record_accesses(interpreter);
1258 }
1259
1260 if self.recorded_account_diffs_stack.is_some() {
1262 self.record_state_diffs(interpreter, ecx);
1263 }
1264
1265 if !self.allowed_mem_writes.is_empty() {
1267 self.check_mem_opcodes(
1268 interpreter,
1269 ecx.journal().depth().try_into().expect("journaled state depth exceeds u64"),
1270 );
1271 }
1272
1273 if let Some(mapping_slots) = &mut self.mapping_slots {
1275 mapping_step(mapping_slots, interpreter);
1276 }
1277
1278 if self.gas_metering.recording {
1280 self.meter_gas_record(interpreter, ecx);
1281 }
1282 }
1283
1284 fn step_end(&mut self, interpreter: &mut Interpreter, ecx: &mut CTX) {
1285 if self.gas_metering.paused {
1286 self.meter_gas_end(interpreter);
1287 }
1288
1289 if self.gas_metering.touched {
1290 self.meter_gas_check(interpreter);
1291 }
1292
1293 if self.arbitrary_storage.is_some() {
1295 self.arbitrary_storage_end(interpreter, ecx);
1296 }
1297 }
1298
1299 fn log(&mut self, _ecx: &mut CTX, log: Log) {
1300 if !self.expected_emits.is_empty()
1301 && let Some(err) = expect::handle_expect_emit(self, &log, None)
1302 {
1303 let _ = sh_err!("{err:?}");
1307 }
1308
1309 record_logs(&mut self.recorded_logs, &log);
1311 }
1312
1313 fn log_full(&mut self, interpreter: &mut Interpreter, _ecx: &mut CTX, log: Log) {
1314 if !self.expected_emits.is_empty() {
1315 expect::handle_expect_emit(self, &log, Some(interpreter));
1316 }
1317
1318 record_logs(&mut self.recorded_logs, &log);
1320 }
1321
1322 fn call(&mut self, ecx: &mut CTX, inputs: &mut CallInputs) -> Option<CallOutcome> {
1323 Self::call_with_executor(self, ecx, inputs, &mut TransparentCheatcodesExecutor)
1324 }
1325
1326 fn call_end(&mut self, ecx: &mut CTX, call: &CallInputs, outcome: &mut CallOutcome) {
1327 let cheatcode_call = call.target_address == CHEATCODE_ADDRESS
1328 || call.target_address == HARDHAT_CONSOLE_ADDRESS;
1329
1330 if !cheatcode_call {
1334 let curr_depth = ecx.journal().depth();
1336 if let Some(prank) = &self.get_prank(curr_depth)
1337 && curr_depth == prank.depth
1338 {
1339 ecx.tx_mut().set_caller(prank.prank_origin);
1340
1341 if prank.single_call {
1343 self.pranks.remove(&curr_depth);
1344 }
1345 }
1346
1347 if let Some(broadcast) = &self.broadcast
1349 && curr_depth == broadcast.depth
1350 {
1351 ecx.tx_mut().set_caller(broadcast.original_origin);
1352
1353 if broadcast.single_call {
1355 let _ = self.broadcast.take();
1356 }
1357 }
1358 }
1359
1360 if let Some(assume_no_revert) = &mut self.assume_no_revert {
1362 if outcome.result.is_revert() && assume_no_revert.reverted_by.is_none() {
1365 assume_no_revert.reverted_by = Some(call.target_address);
1366 }
1367
1368 let curr_depth = ecx.journal().depth();
1370 if curr_depth <= assume_no_revert.depth && !cheatcode_call {
1371 if outcome.result.is_revert() {
1374 let assume_no_revert = std::mem::take(&mut self.assume_no_revert).unwrap();
1375 return match revert_handlers::handle_assume_no_revert(
1376 &assume_no_revert,
1377 outcome.result.result,
1378 &outcome.result.output,
1379 &self.config.available_artifacts,
1380 ) {
1381 Ok(_) => {
1384 outcome.result.output = Error::from(MAGIC_ASSUME).abi_encode().into();
1385 }
1386 Err(error) => {
1389 trace!(expected=?assume_no_revert, ?error, status=?outcome.result.result, "Expected revert mismatch");
1390 outcome.result.result = InstructionResult::Revert;
1391 outcome.result.output = error.abi_encode().into();
1392 }
1393 };
1394 } else {
1395 self.assume_no_revert = None;
1397 }
1398 }
1399 }
1400
1401 if let Some(expected_revert) = &mut self.expected_revert {
1403 if outcome.result.is_revert() {
1406 if expected_revert.reverter.is_some()
1410 && (expected_revert.reverted_by.is_none() || expected_revert.count > 1)
1411 {
1412 expected_revert.reverted_by = Some(call.target_address);
1413 }
1414 }
1415
1416 let curr_depth = ecx.journal().depth();
1417 if curr_depth <= expected_revert.depth {
1418 let needs_processing = match expected_revert.kind {
1419 ExpectedRevertKind::Default => !cheatcode_call,
1420 ExpectedRevertKind::Cheatcode { pending_processing } => {
1423 cheatcode_call && !pending_processing
1424 }
1425 };
1426
1427 if needs_processing {
1428 let mut expected_revert = std::mem::take(&mut self.expected_revert).unwrap();
1429 return match revert_handlers::handle_expect_revert(
1430 cheatcode_call,
1431 false,
1432 self.config.internal_expect_revert,
1433 &expected_revert,
1434 outcome.result.result,
1435 outcome.result.output.clone(),
1436 &self.config.available_artifacts,
1437 ) {
1438 Err(error) => {
1439 trace!(expected=?expected_revert, ?error, status=?outcome.result.result, "Expected revert mismatch");
1440 outcome.result.result = InstructionResult::Revert;
1441 outcome.result.output = error.abi_encode().into();
1442 }
1443 Ok((_, retdata)) => {
1444 expected_revert.actual_count += 1;
1445 if expected_revert.actual_count < expected_revert.count {
1446 self.expected_revert = Some(expected_revert);
1447 }
1448 outcome.result.result = InstructionResult::Return;
1449 outcome.result.output = retdata;
1450 }
1451 };
1452 }
1453
1454 if let ExpectedRevertKind::Cheatcode { pending_processing } =
1457 &mut self.expected_revert.as_mut().unwrap().kind
1458 {
1459 *pending_processing = false;
1460 }
1461 }
1462 }
1463
1464 if cheatcode_call {
1467 return;
1468 }
1469
1470 let gas = outcome.result.gas;
1473 self.gas_metering.last_call_gas = Some(crate::Vm::Gas {
1474 gasLimit: gas.limit(),
1475 gasTotalUsed: gas.spent(),
1476 gasMemoryUsed: 0,
1477 gasRefunded: gas.refunded(),
1478 gasRemaining: gas.remaining(),
1479 });
1480
1481 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1484 if ecx.journal().depth() > 0
1486 && let Some(mut last_recorded_depth) = recorded_account_diffs_stack.pop()
1487 {
1488 if outcome.result.is_revert() {
1491 last_recorded_depth.iter_mut().for_each(|element| {
1492 element.reverted = true;
1493 element
1494 .storageAccesses
1495 .iter_mut()
1496 .for_each(|storage_access| storage_access.reverted = true);
1497 })
1498 }
1499
1500 if let Some(call_access) = last_recorded_depth.first_mut() {
1501 let curr_depth = ecx.journal().depth();
1506 if call_access.depth == curr_depth as u64
1507 && let Ok(acc) = ecx.journal_mut().load_account(call.target_address)
1508 {
1509 debug_assert!(access_is_call(call_access.kind));
1510 call_access.newBalance = acc.data.info.balance;
1511 call_access.newNonce = acc.data.info.nonce;
1512 }
1513 if let Some(last) = recorded_account_diffs_stack.last_mut() {
1518 last.extend(last_recorded_depth);
1519 } else {
1520 recorded_account_diffs_stack.push(last_recorded_depth);
1521 }
1522 }
1523 }
1524 }
1525
1526 let should_check_emits = self
1538 .expected_emits
1539 .iter()
1540 .any(|(expected, _)| {
1541 let curr_depth = ecx.journal().depth();
1542 expected.depth == curr_depth
1543 }) &&
1544 !call.is_static;
1546 if should_check_emits {
1547 let expected_counts = self
1548 .expected_emits
1549 .iter()
1550 .filter_map(|(expected, count_map)| {
1551 let count = match expected.address {
1552 Some(emitter) => match count_map.get(&emitter) {
1553 Some(log_count) => expected
1554 .log
1555 .as_ref()
1556 .map(|l| log_count.count(l))
1557 .unwrap_or_else(|| log_count.count_unchecked()),
1558 None => 0,
1559 },
1560 None => match &expected.log {
1561 Some(log) => count_map.values().map(|logs| logs.count(log)).sum(),
1562 None => count_map.values().map(|logs| logs.count_unchecked()).sum(),
1563 },
1564 };
1565
1566 if count != expected.count { Some((expected, count)) } else { None }
1567 })
1568 .collect::<Vec<_>>();
1569
1570 if let Some((expected, _)) = self
1572 .expected_emits
1573 .iter()
1574 .find(|(expected, _)| !expected.found && expected.count > 0)
1575 {
1576 outcome.result.result = InstructionResult::Revert;
1577 let error_msg = expected.mismatch_error.as_deref().unwrap_or("log != expected log");
1578 outcome.result.output = error_msg.abi_encode().into();
1579 return;
1580 }
1581
1582 if !expected_counts.is_empty() {
1583 let msg = if outcome.result.is_ok() {
1584 let (expected, count) = expected_counts.first().unwrap();
1585 format!("log emitted {count} times, expected {}", expected.count)
1586 } else {
1587 "expected an emit, but the call reverted instead. \
1588 ensure you're testing the happy path when using `expectEmit`"
1589 .to_string()
1590 };
1591
1592 outcome.result.result = InstructionResult::Revert;
1593 outcome.result.output = Error::encode(msg);
1594 return;
1595 }
1596
1597 self.expected_emits.clear()
1601 }
1602
1603 let diag = self.fork_revert_diagnostic.take();
1606
1607 if outcome.result.is_revert()
1610 && let Some(err) = diag
1611 {
1612 outcome.result.output = Error::encode(err.to_error_msg(&self.labels));
1613 return;
1614 }
1615
1616 if let TxKind::Call(test_contract) = ecx.tx().kind() {
1619 if ecx.db().is_forked_mode()
1622 && outcome.result.result == InstructionResult::Stop
1623 && call.target_address != test_contract
1624 {
1625 self.fork_revert_diagnostic =
1626 ecx.db().diagnose_revert(call.target_address, ecx.journal().evm_state());
1627 }
1628 }
1629
1630 if ecx.journal().depth() == 0 {
1632 if outcome.result.is_revert() {
1636 return;
1637 }
1638
1639 for (address, calldatas) in &self.expected_calls {
1644 for (calldata, (expected, actual_count)) in calldatas {
1646 let ExpectedCallData { gas, min_gas, value, count, call_type } = expected;
1648
1649 let failed = match call_type {
1650 ExpectedCallType::Count => *count != *actual_count,
1654 ExpectedCallType::NonCount => *count > *actual_count,
1659 };
1660 if failed {
1661 let expected_values = [
1662 Some(format!("data {}", hex::encode_prefixed(calldata))),
1663 value.as_ref().map(|v| format!("value {v}")),
1664 gas.map(|g| format!("gas {g}")),
1665 min_gas.map(|g| format!("minimum gas {g}")),
1666 ]
1667 .into_iter()
1668 .flatten()
1669 .join(", ");
1670 let but = if outcome.result.is_ok() {
1671 let s = if *actual_count == 1 { "" } else { "s" };
1672 format!("was called {actual_count} time{s}")
1673 } else {
1674 "the call reverted instead; \
1675 ensure you're testing the happy path when using `expectCall`"
1676 .to_string()
1677 };
1678 let s = if *count == 1 { "" } else { "s" };
1679 let msg = format!(
1680 "expected call to {address} with {expected_values} \
1681 to be called {count} time{s}, but {but}"
1682 );
1683 outcome.result.result = InstructionResult::Revert;
1684 outcome.result.output = Error::encode(msg);
1685
1686 return;
1687 }
1688 }
1689 }
1690
1691 for (expected, _) in &mut self.expected_emits {
1695 if expected.count == 0 && !expected.found {
1696 expected.found = true;
1697 }
1698 }
1699 self.expected_emits.retain(|(expected, _)| !expected.found);
1700 if !self.expected_emits.is_empty() {
1702 let msg = if outcome.result.is_ok() {
1703 "expected an emit, but no logs were emitted afterwards. \
1704 you might have mismatched events or not enough events were emitted"
1705 } else {
1706 "expected an emit, but the call reverted instead. \
1707 ensure you're testing the happy path when using `expectEmit`"
1708 };
1709 outcome.result.result = InstructionResult::Revert;
1710 outcome.result.output = Error::encode(msg);
1711 return;
1712 }
1713
1714 if let Some(expected_create) = self.expected_creates.first() {
1716 let msg = format!(
1717 "expected {} call by address {} for bytecode {} but not found",
1718 expected_create.create_scheme,
1719 hex::encode_prefixed(expected_create.deployer),
1720 hex::encode_prefixed(&expected_create.bytecode),
1721 );
1722 outcome.result.result = InstructionResult::Revert;
1723 outcome.result.output = Error::encode(msg);
1724 }
1725 }
1726 }
1727
1728 fn create(&mut self, ecx: &mut CTX, mut input: &mut CreateInputs) -> Option<CreateOutcome> {
1729 if let Some(spec_id) = self.execution_evm_version {
1731 ecx.cfg_mut().set_spec(spec_id);
1732 }
1733
1734 let gas = Gas::new(input.gas_limit());
1735 if self.intercept_next_create_call {
1737 self.intercept_next_create_call = false;
1739
1740 let output = input.init_code();
1742
1743 return Some(CreateOutcome {
1745 result: InterpreterResult { result: InstructionResult::Revert, output, gas },
1746 address: None,
1747 });
1748 }
1749
1750 let curr_depth = ecx.journal().depth();
1751
1752 if let Some(prank) = &self.get_prank(curr_depth)
1754 && curr_depth >= prank.depth
1755 && input.caller() == prank.prank_caller
1756 {
1757 let mut prank_applied = false;
1758
1759 if curr_depth == prank.depth {
1761 let _ = journaled_account(ecx, prank.new_caller);
1763 input.set_caller(prank.new_caller);
1764 prank_applied = true;
1765 }
1766
1767 if let Some(new_origin) = prank.new_origin {
1769 ecx.tx_mut().set_caller(new_origin);
1770 prank_applied = true;
1771 }
1772
1773 if prank_applied && let Some(applied_prank) = prank.first_time_applied() {
1775 self.pranks.insert(curr_depth, applied_prank);
1776 }
1777 }
1778
1779 self.apply_accesslist(ecx);
1781
1782 if let Some(broadcast) = &mut self.broadcast
1784 && curr_depth >= broadcast.depth
1785 && input.caller() == broadcast.original_caller
1786 {
1787 if let Err(err) = ecx.journal_mut().load_account(broadcast.new_origin) {
1788 return Some(CreateOutcome {
1789 result: InterpreterResult {
1790 result: InstructionResult::Revert,
1791 output: Error::encode(err),
1792 gas,
1793 },
1794 address: None,
1795 });
1796 }
1797
1798 ecx.tx_mut().set_caller(broadcast.new_origin);
1799
1800 if curr_depth == broadcast.depth || broadcast.deploy_from_code {
1801 broadcast.deploy_from_code = false;
1803
1804 input.set_caller(broadcast.new_origin);
1805
1806 let rpc = ecx.db().active_fork_url();
1807 let account = &ecx.journal().evm_state()[&broadcast.new_origin];
1808 self.broadcastable_transactions.push_back(BroadcastableTransaction {
1809 rpc,
1810 from: broadcast.new_origin,
1811 to: None,
1812 value: input.value(),
1813 input: input.init_code(),
1814 nonce: account.info.nonce,
1815 gas: None,
1816 kind: BroadcastKind::unsigned(),
1817 });
1818
1819 input.log_debug(self, &input.scheme().unwrap_or(CreateScheme::Create));
1820 }
1821 }
1822
1823 let address = input.allow_cheatcodes(self, ecx);
1825
1826 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1828 recorded_account_diffs_stack.push(vec![AccountAccess {
1829 chainInfo: crate::Vm::ChainInfo {
1830 forkId: ecx.db().active_fork_id().unwrap_or_default(),
1831 chainId: U256::from(ecx.cfg().chain_id()),
1832 },
1833 accessor: input.caller(),
1834 account: address,
1835 kind: crate::Vm::AccountAccessKind::Create,
1836 initialized: true,
1837 oldBalance: U256::ZERO, newBalance: U256::ZERO, oldNonce: 0, newNonce: 1, value: input.value(),
1842 data: input.init_code(),
1843 reverted: false,
1844 deployedCode: Bytes::new(), storageAccesses: vec![], depth: curr_depth as u64,
1847 }]);
1848 }
1849
1850 None
1851 }
1852
1853 fn create_end(&mut self, ecx: &mut CTX, call: &CreateInputs, outcome: &mut CreateOutcome) {
1854 let call = Some(call);
1855 let curr_depth = ecx.journal().depth();
1856
1857 if let Some(prank) = &self.get_prank(curr_depth)
1859 && curr_depth == prank.depth
1860 {
1861 ecx.tx_mut().set_caller(prank.prank_origin);
1862
1863 if prank.single_call {
1865 std::mem::take(&mut self.pranks);
1866 }
1867 }
1868
1869 if let Some(broadcast) = &self.broadcast
1871 && curr_depth == broadcast.depth
1872 {
1873 ecx.tx_mut().set_caller(broadcast.original_origin);
1874
1875 if broadcast.single_call {
1877 std::mem::take(&mut self.broadcast);
1878 }
1879 }
1880
1881 if let Some(expected_revert) = &self.expected_revert
1883 && curr_depth <= expected_revert.depth
1884 && matches!(expected_revert.kind, ExpectedRevertKind::Default)
1885 {
1886 let mut expected_revert = std::mem::take(&mut self.expected_revert).unwrap();
1887 return match revert_handlers::handle_expect_revert(
1888 false,
1889 true,
1890 self.config.internal_expect_revert,
1891 &expected_revert,
1892 outcome.result.result,
1893 outcome.result.output.clone(),
1894 &self.config.available_artifacts,
1895 ) {
1896 Ok((address, retdata)) => {
1897 expected_revert.actual_count += 1;
1898 if expected_revert.actual_count < expected_revert.count {
1899 self.expected_revert = Some(expected_revert.clone());
1900 }
1901
1902 outcome.result.result = InstructionResult::Return;
1903 outcome.result.output = retdata;
1904 outcome.address = address;
1905 }
1906 Err(err) => {
1907 outcome.result.result = InstructionResult::Revert;
1908 outcome.result.output = err.abi_encode().into();
1909 }
1910 };
1911 }
1912
1913 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1916 if curr_depth > 0
1918 && let Some(last_depth) = &mut recorded_account_diffs_stack.pop()
1919 {
1920 if outcome.result.is_revert() {
1923 last_depth.iter_mut().for_each(|element| {
1924 element.reverted = true;
1925 element
1926 .storageAccesses
1927 .iter_mut()
1928 .for_each(|storage_access| storage_access.reverted = true);
1929 })
1930 }
1931
1932 if let Some(create_access) = last_depth.first_mut() {
1933 let depth = ecx.journal().depth();
1938 if create_access.depth == depth as u64 {
1939 debug_assert_eq!(
1940 create_access.kind as u8,
1941 crate::Vm::AccountAccessKind::Create as u8
1942 );
1943 if let Some(address) = outcome.address
1944 && let Ok(created_acc) = ecx.journal_mut().load_account(address)
1945 {
1946 create_access.newBalance = created_acc.data.info.balance;
1947 create_access.newNonce = created_acc.data.info.nonce;
1948 create_access.deployedCode = created_acc
1949 .data
1950 .info
1951 .code
1952 .clone()
1953 .unwrap_or_default()
1954 .original_bytes();
1955 }
1956 }
1957 if let Some(last) = recorded_account_diffs_stack.last_mut() {
1962 last.append(last_depth);
1963 } else {
1964 recorded_account_diffs_stack.push(last_depth.clone());
1965 }
1966 }
1967 }
1968 }
1969
1970 if !self.expected_creates.is_empty()
1972 && let (Some(address), Some(call)) = (outcome.address, call)
1973 && let Ok(created_acc) = ecx.journal_mut().load_account(address)
1974 {
1975 let bytecode = created_acc.data.info.code.clone().unwrap_or_default().original_bytes();
1976 if let Some((index, _)) =
1977 self.expected_creates.iter().find_position(|expected_create| {
1978 expected_create.deployer == call.caller()
1979 && expected_create.create_scheme.eq(call.scheme().into())
1980 && expected_create.bytecode == bytecode
1981 })
1982 {
1983 self.expected_creates.swap_remove(index);
1984 }
1985 }
1986 }
1987}
1988
1989impl FoundryInspectorExt for Cheatcodes {
1990 fn should_use_create2_factory(&mut self, depth: usize, inputs: &CreateInputs) -> bool {
1991 if let CreateScheme::Create2 { .. } = inputs.scheme() {
1992 let target_depth = if let Some(prank) = &self.get_prank(depth) {
1993 prank.depth
1994 } else if let Some(broadcast) = &self.broadcast {
1995 broadcast.depth
1996 } else {
1997 1
1998 };
1999
2000 depth == target_depth
2001 && (self.broadcast.is_some() || self.config.always_use_create_2_factory)
2002 } else {
2003 false
2004 }
2005 }
2006
2007 fn create2_deployer(&self) -> Address {
2008 self.config.evm_opts.create2_deployer
2009 }
2010}
2011
2012impl Cheatcodes {
2013 #[cold]
2014 fn meter_gas(&mut self, interpreter: &mut Interpreter) {
2015 if let Some(paused_gas) = self.gas_metering.paused_frames.last() {
2016 let memory = *interpreter.gas.memory();
2019 interpreter.gas = *paused_gas;
2020 interpreter.gas.memory_mut().words_num = memory.words_num;
2021 interpreter.gas.memory_mut().expansion_cost = memory.expansion_cost;
2022 } else {
2023 self.gas_metering.paused_frames.push(interpreter.gas);
2025 }
2026 }
2027
2028 #[cold]
2029 fn meter_gas_record<CTX: ContextTr>(&mut self, interpreter: &mut Interpreter, ecx: &mut CTX) {
2030 if interpreter.bytecode.action.as_ref().and_then(|i| i.instruction_result()).is_none() {
2031 self.gas_metering.gas_records.iter_mut().for_each(|record| {
2032 let curr_depth = ecx.journal().depth();
2033 if curr_depth == record.depth {
2034 if self.gas_metering.last_gas_used != 0 {
2037 let gas_diff =
2038 interpreter.gas.spent().saturating_sub(self.gas_metering.last_gas_used);
2039 record.gas_used = record.gas_used.saturating_add(gas_diff);
2040 }
2041
2042 self.gas_metering.last_gas_used = interpreter.gas.spent();
2045 }
2046 });
2047 }
2048 }
2049
2050 #[cold]
2051 fn meter_gas_end(&mut self, interpreter: &mut Interpreter) {
2052 if let Some(interpreter_action) = interpreter.bytecode.action.as_ref()
2054 && will_exit(interpreter_action)
2055 {
2056 self.gas_metering.paused_frames.pop();
2057 }
2058 }
2059
2060 #[cold]
2061 fn meter_gas_reset(&mut self, interpreter: &mut Interpreter) {
2062 let mut gas = Gas::new(interpreter.gas.limit());
2063 gas.memory_mut().words_num = interpreter.gas.memory().words_num;
2064 gas.memory_mut().expansion_cost = interpreter.gas.memory().expansion_cost;
2065 interpreter.gas = gas;
2066 self.gas_metering.reset = false;
2067 }
2068
2069 #[cold]
2070 fn meter_gas_check(&mut self, interpreter: &mut Interpreter) {
2071 if let Some(interpreter_action) = interpreter.bytecode.action.as_ref()
2072 && will_exit(interpreter_action)
2073 {
2074 if interpreter.gas.spent()
2078 < u64::try_from(interpreter.gas.refunded()).unwrap_or_default()
2079 {
2080 interpreter.gas = Gas::new(interpreter.gas.limit());
2081 }
2082 }
2083 }
2084
2085 #[cold]
2093 fn arbitrary_storage_end<CTX: ContextTr>(
2094 &mut self,
2095 interpreter: &mut Interpreter,
2096 ecx: &mut CTX,
2097 ) {
2098 let (key, target_address) = if interpreter.bytecode.opcode() == op::SLOAD {
2099 (try_or_return!(interpreter.stack.peek(0)), interpreter.input.target_address)
2100 } else {
2101 return;
2102 };
2103
2104 let Some(value) = ecx.sload(target_address, key) else {
2105 return;
2106 };
2107
2108 if (value.is_cold && value.data.is_zero())
2109 || self.should_overwrite_arbitrary_storage(&target_address, key)
2110 {
2111 if self.has_arbitrary_storage(&target_address) {
2112 let arbitrary_value = self.rng().random();
2113 self.arbitrary_storage.as_mut().unwrap().save(
2114 ecx,
2115 target_address,
2116 key,
2117 arbitrary_value,
2118 );
2119 } else if self.is_arbitrary_storage_copy(&target_address) {
2120 let arbitrary_value = self.rng().random();
2121 self.arbitrary_storage.as_mut().unwrap().copy(
2122 ecx,
2123 target_address,
2124 key,
2125 arbitrary_value,
2126 );
2127 }
2128 }
2129 }
2130
2131 #[cold]
2133 fn record_accesses(&mut self, interpreter: &mut Interpreter) {
2134 let access = &mut self.accesses;
2135 match interpreter.bytecode.opcode() {
2136 op::SLOAD => {
2137 let key = try_or_return!(interpreter.stack.peek(0));
2138 access.record_read(interpreter.input.target_address, key);
2139 }
2140 op::SSTORE => {
2141 let key = try_or_return!(interpreter.stack.peek(0));
2142 access.record_write(interpreter.input.target_address, key);
2143 }
2144 _ => {}
2145 }
2146 }
2147
2148 #[cold]
2149 fn record_state_diffs<CTX: ContextTr<Db: DatabaseExt>>(
2150 &mut self,
2151 interpreter: &mut Interpreter,
2152 ecx: &mut CTX,
2153 ) {
2154 let Some(account_accesses) = &mut self.recorded_account_diffs_stack else { return };
2155 match interpreter.bytecode.opcode() {
2156 op::SELFDESTRUCT => {
2157 let Some(last) = account_accesses.last_mut() else { return };
2159
2160 let target = try_or_return!(interpreter.stack.peek(0));
2162 let target = Address::from_word(B256::from(target));
2163 let (initialized, old_balance, old_nonce) = ecx
2164 .journal_mut()
2165 .load_account(target)
2166 .map(|account| {
2167 (
2168 account.data.info.exists(),
2169 account.data.info.balance,
2170 account.data.info.nonce,
2171 )
2172 })
2173 .unwrap_or_default();
2174
2175 let value = ecx
2177 .balance(interpreter.input.target_address)
2178 .map(|b| b.data)
2179 .unwrap_or(U256::ZERO);
2180
2181 last.push(crate::Vm::AccountAccess {
2183 chainInfo: crate::Vm::ChainInfo {
2184 forkId: ecx.db().active_fork_id().unwrap_or_default(),
2185 chainId: U256::from(ecx.cfg().chain_id()),
2186 },
2187 accessor: interpreter.input.target_address,
2188 account: target,
2189 kind: crate::Vm::AccountAccessKind::SelfDestruct,
2190 initialized,
2191 oldBalance: old_balance,
2192 newBalance: old_balance + value,
2193 oldNonce: old_nonce,
2194 newNonce: old_nonce, value,
2196 data: Bytes::new(),
2197 reverted: false,
2198 deployedCode: Bytes::new(),
2199 storageAccesses: vec![],
2200 depth: ecx
2201 .journal()
2202 .depth()
2203 .try_into()
2204 .expect("journaled state depth exceeds u64"),
2205 });
2206 }
2207
2208 op::SLOAD => {
2209 let Some(last) = account_accesses.last_mut() else { return };
2210
2211 let key = try_or_return!(interpreter.stack.peek(0));
2212 let address = interpreter.input.target_address;
2213
2214 let mut present_value = U256::ZERO;
2217 if ecx.journal_mut().load_account(address).is_ok()
2219 && let Some(previous) = ecx.sload(address, key)
2220 {
2221 present_value = previous.data;
2222 }
2223 let access = crate::Vm::StorageAccess {
2224 account: interpreter.input.target_address,
2225 slot: key.into(),
2226 isWrite: false,
2227 previousValue: present_value.into(),
2228 newValue: present_value.into(),
2229 reverted: false,
2230 };
2231 let curr_depth =
2232 ecx.journal().depth().try_into().expect("journaled state depth exceeds u64");
2233 append_storage_access(last, access, curr_depth);
2234 }
2235 op::SSTORE => {
2236 let Some(last) = account_accesses.last_mut() else { return };
2237
2238 let key = try_or_return!(interpreter.stack.peek(0));
2239 let value = try_or_return!(interpreter.stack.peek(1));
2240 let address = interpreter.input.target_address;
2241 let mut previous_value = U256::ZERO;
2244 if ecx.journal_mut().load_account(address).is_ok()
2245 && let Some(previous) = ecx.sload(address, key)
2246 {
2247 previous_value = previous.data;
2248 }
2249
2250 let access = crate::Vm::StorageAccess {
2251 account: address,
2252 slot: key.into(),
2253 isWrite: true,
2254 previousValue: previous_value.into(),
2255 newValue: value.into(),
2256 reverted: false,
2257 };
2258 let curr_depth =
2259 ecx.journal().depth().try_into().expect("journaled state depth exceeds u64");
2260 append_storage_access(last, access, curr_depth);
2261 }
2262
2263 op::EXTCODECOPY | op::EXTCODESIZE | op::EXTCODEHASH | op::BALANCE => {
2265 let kind = match interpreter.bytecode.opcode() {
2266 op::EXTCODECOPY => crate::Vm::AccountAccessKind::Extcodecopy,
2267 op::EXTCODESIZE => crate::Vm::AccountAccessKind::Extcodesize,
2268 op::EXTCODEHASH => crate::Vm::AccountAccessKind::Extcodehash,
2269 op::BALANCE => crate::Vm::AccountAccessKind::Balance,
2270 _ => unreachable!(),
2271 };
2272 let address =
2273 Address::from_word(B256::from(try_or_return!(interpreter.stack.peek(0))));
2274 let initialized;
2275 let balance;
2276 let nonce;
2277 if let Ok(acc) = ecx.journal_mut().load_account(address) {
2278 initialized = acc.data.info.exists();
2279 balance = acc.data.info.balance;
2280 nonce = acc.data.info.nonce;
2281 } else {
2282 initialized = false;
2283 balance = U256::ZERO;
2284 nonce = 0;
2285 }
2286 let curr_depth =
2287 ecx.journal().depth().try_into().expect("journaled state depth exceeds u64");
2288 let account_access = crate::Vm::AccountAccess {
2289 chainInfo: crate::Vm::ChainInfo {
2290 forkId: ecx.db().active_fork_id().unwrap_or_default(),
2291 chainId: U256::from(ecx.cfg().chain_id()),
2292 },
2293 accessor: interpreter.input.target_address,
2294 account: address,
2295 kind,
2296 initialized,
2297 oldBalance: balance,
2298 newBalance: balance,
2299 oldNonce: nonce,
2300 newNonce: nonce, value: U256::ZERO,
2302 data: Bytes::new(),
2303 reverted: false,
2304 deployedCode: Bytes::new(),
2305 storageAccesses: vec![],
2306 depth: curr_depth,
2307 };
2308 if let Some(last) = account_accesses.last_mut() {
2311 last.push(account_access);
2312 } else {
2313 account_accesses.push(vec![account_access]);
2314 }
2315 }
2316 _ => {}
2317 }
2318 }
2319
2320 #[cold]
2325 fn check_mem_opcodes(&self, interpreter: &mut Interpreter, depth: u64) {
2326 let Some(ranges) = self.allowed_mem_writes.get(&depth) else {
2327 return;
2328 };
2329
2330 macro_rules! mem_opcode_match {
2339 ($(($opcode:ident, $offset_depth:expr, $size_depth:expr, $writes:expr)),* $(,)?) => {
2340 match interpreter.bytecode.opcode() {
2341 op::MSTORE => {
2346 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2348
2349 if !ranges.iter().any(|range| {
2352 range.contains(&offset) && range.contains(&(offset + 31))
2353 }) {
2354 let value = try_or_return!(interpreter.stack.peek(1)).to_be_bytes::<32>();
2359 if value[..SELECTOR_LEN] == stopExpectSafeMemoryCall::SELECTOR {
2360 return
2361 }
2362
2363 disallowed_mem_write(offset, 32, interpreter, ranges);
2364 return
2365 }
2366 }
2367 op::MSTORE8 => {
2368 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2370
2371 if !ranges.iter().any(|range| range.contains(&offset)) {
2374 disallowed_mem_write(offset, 1, interpreter, ranges);
2375 return
2376 }
2377 }
2378
2379 op::MLOAD => {
2384 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2386
2387 if offset >= interpreter.memory.size() as u64 && !ranges.iter().any(|range| {
2391 range.contains(&offset) && range.contains(&(offset + 31))
2392 }) {
2393 disallowed_mem_write(offset, 32, interpreter, ranges);
2394 return
2395 }
2396 }
2397
2398 op::CALL => {
2403 let dest_offset = try_or_return!(interpreter.stack.peek(5)).saturating_to::<u64>();
2405
2406 let size = try_or_return!(interpreter.stack.peek(6)).saturating_to::<u64>();
2408
2409 let fail_cond = !ranges.iter().any(|range| {
2413 range.contains(&dest_offset) &&
2414 range.contains(&(dest_offset + size.saturating_sub(1)))
2415 });
2416
2417 if fail_cond {
2420 let to = Address::from_word(try_or_return!(interpreter.stack.peek(1)).to_be_bytes::<32>().into());
2424 if to == CHEATCODE_ADDRESS {
2425 let args_offset = try_or_return!(interpreter.stack.peek(3)).saturating_to::<usize>();
2426 let args_size = try_or_return!(interpreter.stack.peek(4)).saturating_to::<usize>();
2427 let memory_word = interpreter.memory.slice_len(args_offset, args_size);
2428 if memory_word[..SELECTOR_LEN] == stopExpectSafeMemoryCall::SELECTOR {
2429 return
2430 }
2431 }
2432
2433 disallowed_mem_write(dest_offset, size, interpreter, ranges);
2434 return
2435 }
2436 }
2437
2438 $(op::$opcode => {
2439 let dest_offset = try_or_return!(interpreter.stack.peek($offset_depth)).saturating_to::<u64>();
2441
2442 let size = try_or_return!(interpreter.stack.peek($size_depth)).saturating_to::<u64>();
2444
2445 let fail_cond = !ranges.iter().any(|range| {
2449 range.contains(&dest_offset) &&
2450 range.contains(&(dest_offset + size.saturating_sub(1)))
2451 }) && ($writes ||
2452 [dest_offset, (dest_offset + size).saturating_sub(1)].into_iter().any(|offset| {
2453 offset >= interpreter.memory.size() as u64
2454 })
2455 );
2456
2457 if fail_cond {
2460 disallowed_mem_write(dest_offset, size, interpreter, ranges);
2461 return
2462 }
2463 })*
2464
2465 _ => {}
2466 }
2467 }
2468 }
2469
2470 mem_opcode_match!(
2473 (CALLDATACOPY, 0, 2, true),
2474 (CODECOPY, 0, 2, true),
2475 (RETURNDATACOPY, 0, 2, true),
2476 (EXTCODECOPY, 1, 3, true),
2477 (CALLCODE, 5, 6, true),
2478 (STATICCALL, 4, 5, true),
2479 (DELEGATECALL, 4, 5, true),
2480 (KECCAK256, 0, 1, false),
2481 (LOG0, 0, 1, false),
2482 (LOG1, 0, 1, false),
2483 (LOG2, 0, 1, false),
2484 (LOG3, 0, 1, false),
2485 (LOG4, 0, 1, false),
2486 (CREATE, 1, 2, false),
2487 (CREATE2, 1, 2, false),
2488 (RETURN, 0, 1, false),
2489 (REVERT, 0, 1, false),
2490 );
2491 }
2492
2493 #[cold]
2494 fn set_gas_limit_type(&mut self, interpreter: &mut Interpreter) {
2495 match interpreter.bytecode.opcode() {
2496 op::CREATE2 => self.dynamic_gas_limit = true,
2497 op::CALL => {
2498 self.dynamic_gas_limit =
2501 try_or_return!(interpreter.stack.peek(0)) >= interpreter.gas.remaining() - 100
2502 }
2503 _ => self.dynamic_gas_limit = false,
2504 }
2505 }
2506}
2507
2508fn disallowed_mem_write(
2514 dest_offset: u64,
2515 size: u64,
2516 interpreter: &mut Interpreter,
2517 ranges: &[Range<u64>],
2518) {
2519 let revert_string = format!(
2520 "memory write at offset 0x{:02X} of size 0x{:02X} not allowed; safe range: {}",
2521 dest_offset,
2522 size,
2523 ranges.iter().map(|r| format!("(0x{:02X}, 0x{:02X}]", r.start, r.end)).join(" U ")
2524 );
2525
2526 interpreter.bytecode.set_action(InterpreterAction::new_return(
2527 InstructionResult::Revert,
2528 Bytes::from(revert_string.into_bytes()),
2529 interpreter.gas,
2530 ));
2531}
2532
2533fn access_is_call(kind: crate::Vm::AccountAccessKind) -> bool {
2535 matches!(
2536 kind,
2537 crate::Vm::AccountAccessKind::Call
2538 | crate::Vm::AccountAccessKind::StaticCall
2539 | crate::Vm::AccountAccessKind::CallCode
2540 | crate::Vm::AccountAccessKind::DelegateCall
2541 )
2542}
2543
2544fn record_logs(recorded_logs: &mut Option<Vec<Vm::Log>>, log: &Log) {
2546 if let Some(storage_recorded_logs) = recorded_logs {
2547 storage_recorded_logs.push(Vm::Log {
2548 topics: log.data.topics().to_vec(),
2549 data: log.data.data.clone(),
2550 emitter: log.address,
2551 });
2552 }
2553}
2554
2555fn append_storage_access(
2557 last: &mut Vec<AccountAccess>,
2558 storage_access: crate::Vm::StorageAccess,
2559 storage_depth: u64,
2560) {
2561 if !last.is_empty() && last.first().unwrap().depth < storage_depth {
2563 if last.len() == 1 {
2569 last.first_mut().unwrap().storageAccesses.push(storage_access);
2570 } else {
2571 let last_record = last.last_mut().unwrap();
2572 if last_record.kind as u8 == crate::Vm::AccountAccessKind::Resume as u8 {
2573 last_record.storageAccesses.push(storage_access);
2574 } else {
2575 let entry = last.first().unwrap();
2576 let resume_record = crate::Vm::AccountAccess {
2577 chainInfo: crate::Vm::ChainInfo {
2578 forkId: entry.chainInfo.forkId,
2579 chainId: entry.chainInfo.chainId,
2580 },
2581 accessor: entry.accessor,
2582 account: entry.account,
2583 kind: crate::Vm::AccountAccessKind::Resume,
2584 initialized: entry.initialized,
2585 storageAccesses: vec![storage_access],
2586 reverted: entry.reverted,
2587 oldBalance: U256::ZERO,
2589 newBalance: U256::ZERO,
2590 oldNonce: 0,
2591 newNonce: 0,
2592 value: U256::ZERO,
2593 data: Bytes::new(),
2594 deployedCode: Bytes::new(),
2595 depth: entry.depth,
2596 };
2597 last.push(resume_record);
2598 }
2599 }
2600 }
2601}
2602
2603fn cheatcode_of<T: spec::CheatcodeDef>(_: &T) -> &'static spec::Cheatcode<'static> {
2605 T::CHEATCODE
2606}
2607
2608fn cheatcode_name(cheat: &spec::Cheatcode<'static>) -> &'static str {
2609 cheat.func.signature.split('(').next().unwrap()
2610}
2611
2612fn cheatcode_id(cheat: &spec::Cheatcode<'static>) -> &'static str {
2613 cheat.func.id
2614}
2615
2616fn cheatcode_signature(cheat: &spec::Cheatcode<'static>) -> &'static str {
2617 cheat.func.signature
2618}
2619
2620fn apply_dispatch<CTX: EthCheatCtx>(
2622 calls: &Vm::VmCalls,
2623 ccx: &mut CheatsCtxt<'_, CTX>,
2624 executor: &mut dyn CheatcodesExecutor<CTX>,
2625) -> Result {
2626 macro_rules! get_cheatcode {
2628 ($($variant:ident),*) => {
2629 match calls {
2630 $(Vm::VmCalls::$variant(cheat) => cheatcode_of(cheat),)*
2631 }
2632 };
2633 }
2634 let cheat = vm_calls!(get_cheatcode);
2635
2636 let _guard = debug_span!(target: "cheatcodes", "apply", id = %cheatcode_id(cheat)).entered();
2637 trace!(target: "cheatcodes", cheat = %cheatcode_signature(cheat), "applying");
2638
2639 if let spec::Status::Deprecated(replacement) = cheat.status {
2640 ccx.state.deprecated.insert(cheatcode_signature(cheat), replacement);
2641 }
2642
2643 macro_rules! dispatch {
2645 ($($variant:ident),*) => {
2646 match calls {
2647 $(Vm::VmCalls::$variant(cheat) => Cheatcode::apply_full(cheat, ccx, executor),)*
2648 }
2649 };
2650 }
2651 let mut result = vm_calls!(dispatch);
2652
2653 if let Err(e) = &mut result
2655 && e.is_str()
2656 {
2657 let name = cheatcode_name(cheat);
2658 if !name.contains("assert") && name != "rpcUrl" {
2662 *e = fmt_err!("vm.{name}: {e}");
2663 }
2664 }
2665
2666 trace!(
2667 target: "cheatcodes",
2668 return = %match &result {
2669 Ok(b) => hex::encode(b),
2670 Err(e) => e.to_string(),
2671 }
2672 );
2673
2674 result
2675}
2676
2677fn will_exit(action: &InterpreterAction) -> bool {
2679 match action {
2680 InterpreterAction::Return(result) => {
2681 result.result.is_ok_or_revert() || result.result.is_error()
2682 }
2683 _ => false,
2684 }
2685}