1use crate::{
4 Cheatcode, CheatsConfig, CheatsCtxt, Error, Result,
5 Vm::{self, AccountAccess},
6 evm::{
7 DealRecord, GasRecord, RecordAccess, journaled_account,
8 mock::{MockCallDataContext, MockCallReturnData},
9 prank::Prank,
10 },
11 inspector::utils::CommonCreateInput,
12 script::{Broadcast, Wallets},
13 test::{
14 assume::AssumeNoRevert,
15 expect::{
16 self, ExpectedCallData, ExpectedCallTracker, ExpectedCallType, ExpectedCreate,
17 ExpectedEmitTracker, ExpectedRevert, ExpectedRevertKind,
18 },
19 revert_handlers,
20 },
21 utils::IgnoredTraces,
22};
23use alloy_consensus::BlobTransactionSidecarVariant;
24use alloy_network::{Ethereum, Network, TransactionBuilder};
25use alloy_primitives::{
26 Address, B256, Bytes, Log, TxKind, U256, hex,
27 map::{AddressHashMap, HashMap, HashSet},
28};
29use alloy_rpc_types::AccessList;
30use alloy_sol_types::{SolCall, SolInterface, SolValue};
31use foundry_common::{
32 FoundryTransactionBuilder, SELECTOR_LEN, TransactionMaybeSigned,
33 mapping_slots::{MappingSlots, step as mapping_step},
34};
35use foundry_evm_core::{
36 Breakpoints, EvmEnv, FoundryTransaction, InspectorExt,
37 abi::Vm::stopExpectSafeMemoryCall,
38 backend::{DatabaseError, DatabaseExt, RevertDiagnostic},
39 constants::{CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS, MAGIC_ASSUME},
40 env::FoundryContextExt,
41 evm::{
42 BlockEnvFor, EthEvmNetwork, FoundryContextFor, FoundryEvmFactory, FoundryEvmNetwork,
43 NestedEvmClosure, SpecFor, TransactionRequestFor, TxEnvFor, with_cloned_context,
44 },
45};
46use foundry_evm_traces::{
47 TracingInspector, TracingInspectorConfig, identifier::SignaturesIdentifier,
48};
49use foundry_wallets::wallet_multi::MultiWallet;
50use itertools::Itertools;
51use proptest::test_runner::{RngAlgorithm, TestRng, TestRunner};
52use rand::Rng;
53use revm::{
54 Inspector,
55 bytecode::opcode as op,
56 context::{Cfg, ContextTr, Host, JournalTr, Transaction, TransactionType, result::EVMError},
57 context_interface::{CreateScheme, transaction::SignedAuthorization},
58 handler::FrameResult,
59 inspector::JournalExt,
60 interpreter::{
61 CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, FrameInput, Gas,
62 InstructionResult, Interpreter, InterpreterAction, InterpreterResult,
63 interpreter_types::{Jumps, LoopControl, MemoryTr},
64 },
65};
66use serde_json::Value;
67use std::{
68 cmp::max,
69 collections::{BTreeMap, VecDeque},
70 fmt::Debug,
71 fs::File,
72 io::BufReader,
73 ops::Range,
74 path::PathBuf,
75 sync::{Arc, OnceLock},
76};
77
78mod utils;
79
80pub mod analysis;
81pub use analysis::CheatcodeAnalysis;
82
83pub trait CheatcodesExecutor<FEN: FoundryEvmNetwork> {
85 fn with_nested_evm(
88 &mut self,
89 cheats: &mut Cheatcodes<FEN>,
90 ecx: &mut FoundryContextFor<'_, FEN>,
91 f: NestedEvmClosure<'_, SpecFor<FEN>, BlockEnvFor<FEN>, TxEnvFor<FEN>>,
92 ) -> Result<(), EVMError<DatabaseError>>;
93
94 fn transact_on_db(
96 &mut self,
97 cheats: &mut Cheatcodes<FEN>,
98 ecx: &mut FoundryContextFor<'_, FEN>,
99 fork_id: Option<U256>,
100 transaction: B256,
101 ) -> eyre::Result<()>;
102
103 fn transact_from_tx_on_db(
105 &mut self,
106 cheats: &mut Cheatcodes<FEN>,
107 ecx: &mut FoundryContextFor<'_, FEN>,
108 tx: TxEnvFor<FEN>,
109 ) -> eyre::Result<()>;
110
111 #[allow(clippy::type_complexity)]
116 fn with_fresh_nested_evm(
117 &mut self,
118 cheats: &mut Cheatcodes<FEN>,
119 db: &mut <FoundryContextFor<'_, FEN> as ContextTr>::Db,
120 evm_env: EvmEnv<SpecFor<FEN>, BlockEnvFor<FEN>>,
121 f: NestedEvmClosure<'_, SpecFor<FEN>, BlockEnvFor<FEN>, TxEnvFor<FEN>>,
122 ) -> Result<EvmEnv<SpecFor<FEN>, BlockEnvFor<FEN>>, EVMError<DatabaseError>>;
123
124 fn console_log(&mut self, msg: &str);
126
127 fn tracing_inspector(&mut self) -> Option<&mut TracingInspector> {
129 None
130 }
131
132 fn set_in_inner_context(&mut self, _enabled: bool, _original_origin: Option<Address>) {}
136}
137
138pub(crate) fn exec_create<FEN: FoundryEvmNetwork>(
140 executor: &mut dyn CheatcodesExecutor<FEN>,
141 inputs: CreateInputs,
142 ccx: &mut CheatsCtxt<'_, '_, FEN>,
143) -> std::result::Result<CreateOutcome, EVMError<DatabaseError>> {
144 let mut inputs = Some(inputs);
145 let mut outcome = None;
146 executor.with_nested_evm(ccx.state, ccx.ecx, &mut |evm| {
147 let inputs = inputs.take().unwrap();
148 evm.journal_inner_mut().depth += 1;
149
150 let frame = FrameInput::Create(Box::new(inputs));
151
152 let result = match evm.run_execution(frame)? {
153 FrameResult::Call(_) => unreachable!(),
154 FrameResult::Create(create) => create,
155 };
156
157 evm.journal_inner_mut().depth -= 1;
158
159 outcome = Some(result);
160 Ok(())
161 })?;
162 Ok(outcome.unwrap())
163}
164
165#[derive(Debug, Default, Clone, Copy)]
168struct TransparentCheatcodesExecutor;
169
170impl<FEN: FoundryEvmNetwork> CheatcodesExecutor<FEN> for TransparentCheatcodesExecutor {
171 fn with_nested_evm(
172 &mut self,
173 cheats: &mut Cheatcodes<FEN>,
174 ecx: &mut FoundryContextFor<'_, FEN>,
175 f: NestedEvmClosure<'_, SpecFor<FEN>, BlockEnvFor<FEN>, TxEnvFor<FEN>>,
176 ) -> Result<(), EVMError<DatabaseError>> {
177 with_cloned_context(ecx, |db, evm_env, journal_inner| {
178 let mut evm = FEN::EvmFactory::default().create_foundry_nested_evm(db, evm_env, cheats);
179 *evm.journal_inner_mut() = journal_inner;
180 f(&mut *evm)?;
181 let sub_inner = evm.journal_inner_mut().clone();
182 let sub_evm_env = evm.to_evm_env();
183 Ok((sub_evm_env, sub_inner))
184 })
185 }
186
187 fn with_fresh_nested_evm(
188 &mut self,
189 cheats: &mut Cheatcodes<FEN>,
190 db: &mut <FoundryContextFor<'_, FEN> as ContextTr>::Db,
191 evm_env: EvmEnv<SpecFor<FEN>, BlockEnvFor<FEN>>,
192 f: NestedEvmClosure<'_, SpecFor<FEN>, BlockEnvFor<FEN>, TxEnvFor<FEN>>,
193 ) -> Result<EvmEnv<SpecFor<FEN>, BlockEnvFor<FEN>>, EVMError<DatabaseError>> {
194 let mut evm = FEN::EvmFactory::default().create_foundry_nested_evm(db, evm_env, cheats);
195 f(&mut *evm)?;
196 Ok(evm.to_evm_env())
197 }
198
199 fn transact_on_db(
200 &mut self,
201 cheats: &mut Cheatcodes<FEN>,
202 ecx: &mut FoundryContextFor<'_, FEN>,
203 fork_id: Option<U256>,
204 transaction: B256,
205 ) -> eyre::Result<()> {
206 let evm_env = ecx.evm_clone();
207 let (db, inner) = ecx.db_journal_inner_mut();
208 db.transact(fork_id, transaction, evm_env, inner, cheats)
209 }
210
211 fn transact_from_tx_on_db(
212 &mut self,
213 cheats: &mut Cheatcodes<FEN>,
214 ecx: &mut FoundryContextFor<'_, FEN>,
215 tx: TxEnvFor<FEN>,
216 ) -> eyre::Result<()> {
217 let evm_env = ecx.evm_clone();
218 let (db, inner) = ecx.db_journal_inner_mut();
219 db.transact_from_tx(tx, evm_env, inner, cheats)
220 }
221
222 fn console_log(&mut self, _msg: &str) {}
223}
224
225macro_rules! try_or_return {
226 ($e:expr) => {
227 match $e {
228 Ok(v) => v,
229 Err(_) => return,
230 }
231 };
232}
233
234#[derive(Debug, Default)]
236pub struct TestContext {
237 pub opened_read_files: HashMap<PathBuf, BufReader<File>>,
239}
240
241impl Clone for TestContext {
243 fn clone(&self) -> Self {
244 Default::default()
245 }
246}
247
248impl TestContext {
249 pub fn clear(&mut self) {
251 self.opened_read_files.clear();
252 }
253}
254
255#[derive(Clone, Debug)]
257pub struct BroadcastableTransaction<N: Network = Ethereum> {
258 pub rpc: Option<String>,
260 pub transaction: TransactionMaybeSigned<N>,
262}
263
264#[derive(Clone, Debug, Copy)]
265pub struct RecordDebugStepInfo {
266 pub start_node_idx: usize,
268 pub original_tracer_config: TracingInspectorConfig,
270}
271
272#[derive(Clone, Debug, Default)]
274pub struct GasMetering {
275 pub paused: bool,
277 pub touched: bool,
280 pub reset: bool,
282 pub paused_frames: Vec<Gas>,
284
285 pub active_gas_snapshot: Option<(String, String)>,
287
288 pub last_call_gas: Option<crate::Vm::Gas>,
291
292 pub recording: bool,
294 pub last_gas_used: u64,
296 pub gas_records: Vec<GasRecord>,
298}
299
300impl GasMetering {
301 pub const fn start(&mut self) {
303 self.recording = true;
304 }
305
306 pub const fn stop(&mut self) {
308 self.recording = false;
309 }
310
311 pub fn resume(&mut self) {
313 if self.paused {
314 self.paused = false;
315 self.touched = true;
316 }
317 self.paused_frames.clear();
318 }
319
320 pub fn reset(&mut self) {
322 self.paused = false;
323 self.touched = true;
324 self.reset = true;
325 self.paused_frames.clear();
326 }
327}
328
329#[derive(Clone, Debug, Default)]
331pub struct ArbitraryStorage {
332 pub values: HashMap<Address, HashMap<U256, U256>>,
336 pub copies: HashMap<Address, Address>,
338 pub overwrites: HashSet<Address>,
340}
341
342impl ArbitraryStorage {
343 pub fn mark_arbitrary(&mut self, address: &Address, overwrite: bool) {
345 self.values.insert(*address, HashMap::default());
346 if overwrite {
347 self.overwrites.insert(*address);
348 } else {
349 self.overwrites.remove(address);
350 }
351 }
352
353 pub fn mark_copy(&mut self, from: &Address, to: &Address) {
355 if self.values.contains_key(from) {
356 self.copies.insert(*to, *from);
357 }
358 }
359
360 pub fn save<CTX: ContextTr>(
364 &mut self,
365 ecx: &mut CTX,
366 address: Address,
367 slot: U256,
368 data: U256,
369 ) {
370 self.values.get_mut(&address).expect("missing arbitrary address entry").insert(slot, data);
371 if ecx.journal_mut().load_account(address).is_ok() {
372 ecx.journal_mut()
373 .sstore(address, slot, data)
374 .expect("could not set arbitrary storage value");
375 }
376 }
377
378 pub fn copy<CTX: ContextTr>(
384 &mut self,
385 ecx: &mut CTX,
386 target: Address,
387 slot: U256,
388 new_value: U256,
389 ) -> U256 {
390 let source = self.copies.get(&target).expect("missing arbitrary copy target entry");
391 let storage_cache = self.values.get_mut(source).expect("missing arbitrary source storage");
392 let value = match storage_cache.get(&slot) {
393 Some(value) => *value,
394 None => {
395 storage_cache.insert(slot, new_value);
396 if ecx.journal_mut().load_account(*source).is_ok() {
398 ecx.journal_mut()
399 .sstore(*source, slot, new_value)
400 .expect("could not copy arbitrary storage value");
401 }
402 new_value
403 }
404 };
405 if ecx.journal_mut().load_account(target).is_ok() {
407 ecx.journal_mut().sstore(target, slot, value).expect("could not set storage");
408 }
409 value
410 }
411}
412
413pub type BroadcastableTransactions<N> = VecDeque<BroadcastableTransaction<N>>;
415
416#[derive(Clone, Debug)]
434pub struct Cheatcodes<FEN: FoundryEvmNetwork = EthEvmNetwork> {
435 pub analysis: Option<CheatcodeAnalysis>,
437
438 pub block: Option<BlockEnvFor<FEN>>,
443
444 pub active_delegations: Vec<SignedAuthorization>,
448
449 pub active_blob_sidecar: Option<BlobTransactionSidecarVariant>,
451
452 pub gas_price: Option<u128>,
457
458 pub labels: AddressHashMap<String>,
460
461 pub pranks: BTreeMap<usize, Prank>,
463
464 pub expected_revert: Option<ExpectedRevert>,
466
467 pub assume_no_revert: Option<AssumeNoRevert>,
469
470 pub fork_revert_diagnostic: Option<RevertDiagnostic>,
472
473 pub accesses: RecordAccess,
475
476 pub recording_accesses: bool,
478
479 pub recorded_account_diffs_stack: Option<Vec<Vec<AccountAccess>>>,
485
486 pub record_debug_steps_info: Option<RecordDebugStepInfo>,
488
489 pub recorded_logs: Option<Vec<crate::Vm::Log>>,
491
492 pub mocked_calls: HashMap<Address, BTreeMap<MockCallDataContext, VecDeque<MockCallReturnData>>>,
495
496 pub mocked_functions: HashMap<Address, HashMap<Bytes, Address>>,
498
499 pub expected_calls: ExpectedCallTracker,
501 pub expected_emits: ExpectedEmitTracker,
503 pub expected_creates: Vec<ExpectedCreate>,
505
506 pub allowed_mem_writes: HashMap<u64, Vec<Range<u64>>>,
508
509 pub broadcast: Option<Broadcast>,
511
512 pub broadcastable_transactions: BroadcastableTransactions<FEN::Network>,
514
515 pub access_list: Option<AccessList>,
517
518 pub config: Arc<CheatsConfig>,
520
521 pub test_context: TestContext,
523
524 pub fs_commit: bool,
527
528 pub serialized_jsons: BTreeMap<String, BTreeMap<String, Value>>,
531
532 pub eth_deals: Vec<DealRecord>,
534
535 pub gas_metering: GasMetering,
537
538 pub gas_snapshots: BTreeMap<String, BTreeMap<String, String>>,
541
542 pub mapping_slots: Option<AddressHashMap<MappingSlots>>,
544
545 pub pc: usize,
547 pub breakpoints: Breakpoints,
550
551 pub intercept_next_create_call: bool,
553
554 test_runner: Option<TestRunner>,
557
558 pub ignored_traces: IgnoredTraces,
560
561 pub arbitrary_storage: Option<ArbitraryStorage>,
563
564 pub deprecated: HashMap<&'static str, Option<&'static str>>,
566 pub wallets: Option<Wallets>,
568 signatures_identifier: OnceLock<Option<SignaturesIdentifier>>,
570 pub dynamic_gas_limit: bool,
572 pub execution_evm_version: Option<SpecFor<FEN>>,
574}
575
576impl Default for Cheatcodes {
580 fn default() -> Self {
581 Self::new(Arc::default())
582 }
583}
584
585impl<FEN: FoundryEvmNetwork> Cheatcodes<FEN> {
586 pub fn new(config: Arc<CheatsConfig>) -> Self {
588 Self {
589 analysis: None,
590 fs_commit: true,
591 labels: config.labels.clone(),
592 config,
593 block: Default::default(),
594 active_delegations: Default::default(),
595 active_blob_sidecar: Default::default(),
596 gas_price: Default::default(),
597 pranks: Default::default(),
598 expected_revert: Default::default(),
599 assume_no_revert: Default::default(),
600 fork_revert_diagnostic: Default::default(),
601 accesses: Default::default(),
602 recording_accesses: Default::default(),
603 recorded_account_diffs_stack: Default::default(),
604 recorded_logs: Default::default(),
605 record_debug_steps_info: Default::default(),
606 mocked_calls: Default::default(),
607 mocked_functions: Default::default(),
608 expected_calls: Default::default(),
609 expected_emits: Default::default(),
610 expected_creates: Default::default(),
611 allowed_mem_writes: Default::default(),
612 broadcast: Default::default(),
613 broadcastable_transactions: Default::default(),
614 access_list: Default::default(),
615 test_context: Default::default(),
616 serialized_jsons: Default::default(),
617 eth_deals: Default::default(),
618 gas_metering: Default::default(),
619 gas_snapshots: Default::default(),
620 mapping_slots: Default::default(),
621 pc: Default::default(),
622 breakpoints: Default::default(),
623 intercept_next_create_call: Default::default(),
624 test_runner: Default::default(),
625 ignored_traces: Default::default(),
626 arbitrary_storage: Default::default(),
627 deprecated: Default::default(),
628 wallets: Default::default(),
629 signatures_identifier: Default::default(),
630 dynamic_gas_limit: Default::default(),
631 execution_evm_version: None,
632 }
633 }
634
635 pub fn set_analysis(&mut self, analysis: CheatcodeAnalysis) {
637 self.analysis = Some(analysis);
638 }
639
640 pub fn get_prank(&self, depth: usize) -> Option<&Prank> {
644 self.pranks.range(..=depth).last().map(|(_, prank)| prank)
645 }
646
647 pub fn wallets(&mut self) -> &Wallets {
649 self.wallets.get_or_insert_with(|| Wallets::new(MultiWallet::default(), None))
650 }
651
652 pub fn set_wallets(&mut self, wallets: Wallets) {
654 self.wallets = Some(wallets);
655 }
656
657 pub fn add_delegation(&mut self, authorization: SignedAuthorization) {
659 self.active_delegations.push(authorization);
660 }
661
662 pub fn signatures_identifier(&self) -> Option<&SignaturesIdentifier> {
664 self.signatures_identifier.get_or_init(|| SignaturesIdentifier::new(true).ok()).as_ref()
665 }
666
667 fn apply_cheatcode(
669 &mut self,
670 ecx: &mut FoundryContextFor<'_, FEN>,
671 call: &CallInputs,
672 executor: &mut dyn CheatcodesExecutor<FEN>,
673 ) -> Result {
674 let decoded = Vm::VmCalls::abi_decode(&call.input.bytes(ecx)).map_err(|e| {
676 if let alloy_sol_types::Error::UnknownSelector { name: _, selector } = e {
677 let msg = format!(
678 "unknown cheatcode with selector {selector}; \
679 you may have a mismatch between the `Vm` interface (likely in `forge-std`) \
680 and the `forge` version"
681 );
682 return alloy_sol_types::Error::Other(std::borrow::Cow::Owned(msg));
683 }
684 e
685 })?;
686
687 let caller = call.caller;
688
689 ecx.db_mut().ensure_cheatcode_access_forking_mode(&caller)?;
692
693 apply_dispatch(
694 &decoded,
695 &mut CheatsCtxt { state: self, ecx, gas_limit: call.gas_limit, caller },
696 executor,
697 )
698 }
699
700 fn allow_cheatcodes_on_create(
706 &self,
707 ecx: &mut FoundryContextFor<FEN>,
708 caller: Address,
709 created_address: Address,
710 ) {
711 if ecx.journal().depth() <= 1 || ecx.db().has_cheatcode_access(&caller) {
712 ecx.db_mut().allow_cheatcode_access(created_address);
713 }
714 }
715
716 fn apply_accesslist(&mut self, ecx: &mut FoundryContextFor<FEN>) {
722 if let Some(access_list) = &self.access_list {
723 ecx.tx_mut().set_access_list(access_list.clone());
724
725 if ecx.tx().tx_type() == TransactionType::Legacy as u8 {
726 ecx.tx_mut().set_tx_type(TransactionType::Eip2930 as u8);
727 }
728 }
729 }
730
731 pub fn on_revert(&mut self, ecx: &mut FoundryContextFor<FEN>) {
736 trace!(deals=?self.eth_deals.len(), "rolling back deals");
737
738 if self.expected_revert.is_some() {
740 return;
741 }
742
743 if ecx.journal().depth() > 0 {
745 return;
746 }
747
748 while let Some(record) = self.eth_deals.pop() {
752 if let Some(acc) = ecx.journal_mut().evm_state_mut().get_mut(&record.address) {
753 acc.info.balance = record.old_balance;
754 }
755 }
756 }
757
758 pub fn call_with_executor(
759 &mut self,
760 ecx: &mut FoundryContextFor<'_, FEN>,
761 call: &mut CallInputs,
762 executor: &mut dyn CheatcodesExecutor<FEN>,
763 ) -> Option<CallOutcome> {
764 if let Some(spec_id) = self.execution_evm_version {
766 ecx.cfg_mut().set_spec_and_mainnet_gas_params(spec_id);
767 }
768
769 let gas = Gas::new(call.gas_limit);
770 let curr_depth = ecx.journal().depth();
771
772 if curr_depth == 0 {
776 let sender = ecx.tx().caller();
777 let account = match super::evm::journaled_account(ecx, sender) {
778 Ok(account) => account,
779 Err(err) => {
780 return Some(CallOutcome {
781 result: InterpreterResult {
782 result: InstructionResult::Revert,
783 output: err.abi_encode().into(),
784 gas,
785 },
786 memory_offset: call.return_memory_offset.clone(),
787 was_precompile_called: false,
788 precompile_call_logs: vec![],
789 });
790 }
791 };
792 let prev = account.info.nonce;
793 account.info.nonce = prev.saturating_sub(1);
794
795 trace!(target: "cheatcodes", %sender, nonce=account.info.nonce, prev, "corrected nonce");
796 }
797
798 if call.target_address == CHEATCODE_ADDRESS {
799 return match self.apply_cheatcode(ecx, call, executor) {
800 Ok(retdata) => Some(CallOutcome {
801 result: InterpreterResult {
802 result: InstructionResult::Return,
803 output: retdata.into(),
804 gas,
805 },
806 memory_offset: call.return_memory_offset.clone(),
807 was_precompile_called: true,
808 precompile_call_logs: vec![],
809 }),
810 Err(err) => Some(CallOutcome {
811 result: InterpreterResult {
812 result: InstructionResult::Revert,
813 output: err.abi_encode().into(),
814 gas,
815 },
816 memory_offset: call.return_memory_offset.clone(),
817 was_precompile_called: false,
818 precompile_call_logs: vec![],
819 }),
820 };
821 }
822
823 if call.target_address == HARDHAT_CONSOLE_ADDRESS {
824 return None;
825 }
826
827 if let Some(expected) = &mut self.expected_revert {
831 expected.max_depth = max(curr_depth + 1, expected.max_depth);
832 }
833
834 if let Some(expected_calls_for_target) = self.expected_calls.get_mut(&call.bytecode_address)
838 {
839 for (calldata, (expected, actual_count)) in expected_calls_for_target {
841 if calldata.len() <= call.input.len() &&
844 *calldata == call.input.bytes(ecx)[..calldata.len()] &&
846 expected
848 .value.is_none_or(|value| Some(value) == call.transfer_value()) &&
849 expected.gas.is_none_or(|gas| gas == call.gas_limit) &&
851 expected.min_gas.is_none_or(|min_gas| min_gas <= call.gas_limit)
853 {
854 *actual_count += 1;
855 }
856 }
857 }
858
859 if let Some(mocks) = self.mocked_calls.get_mut(&call.bytecode_address) {
861 let ctx = MockCallDataContext {
862 calldata: call.input.bytes(ecx),
863 value: call.transfer_value(),
864 };
865
866 if let Some(return_data_queue) = match mocks.get_mut(&ctx) {
867 Some(queue) => Some(queue),
868 None => mocks
869 .iter_mut()
870 .find(|(mock, _)| {
871 call.input.bytes(ecx).get(..mock.calldata.len()) == Some(&mock.calldata[..])
872 && mock.value.is_none_or(|value| Some(value) == call.transfer_value())
873 })
874 .map(|(_, v)| v),
875 } && let Some(return_data) = if return_data_queue.len() == 1 {
876 return_data_queue.front().map(|x| x.to_owned())
878 } else {
879 return_data_queue.pop_front()
881 } {
882 return Some(CallOutcome {
883 result: InterpreterResult {
884 result: return_data.ret_type,
885 output: return_data.data,
886 gas,
887 },
888 memory_offset: call.return_memory_offset.clone(),
889 was_precompile_called: true,
890 precompile_call_logs: vec![],
891 });
892 }
893 }
894
895 if let Some(prank) = &self.get_prank(curr_depth) {
897 if prank.delegate_call
899 && curr_depth == prank.depth
900 && call.scheme == CallScheme::DelegateCall
901 {
902 call.target_address = prank.new_caller;
903 call.caller = prank.new_caller;
904 if let Some(new_origin) = prank.new_origin {
905 ecx.tx_mut().set_caller(new_origin);
906 }
907 }
908
909 if curr_depth >= prank.depth && call.caller == prank.prank_caller {
910 let prank_applied = if curr_depth == prank.depth {
912 let _ = journaled_account(ecx, prank.new_caller);
914 call.caller = prank.new_caller;
915 true
916 } else {
917 false
918 };
919
920 let prank_applied = if let Some(new_origin) = prank.new_origin {
922 ecx.tx_mut().set_caller(new_origin);
923 true
924 } else {
925 prank_applied
926 };
927
928 if prank_applied && let Some(applied_prank) = prank.first_time_applied() {
930 self.pranks.insert(curr_depth, applied_prank);
931 }
932 }
933 }
934
935 self.apply_accesslist(ecx);
937
938 if let Some(broadcast) = &self.broadcast {
940 let is_fixed_gas_limit = call.gas_limit >= 21_000 && !self.dynamic_gas_limit;
943 self.dynamic_gas_limit = false;
944
945 if curr_depth == broadcast.depth && call.caller == broadcast.original_caller {
950 ecx.tx_mut().set_caller(broadcast.new_origin);
954
955 call.caller = broadcast.new_origin;
956 if !call.is_static {
961 if let Err(err) = ecx.journal_mut().load_account(broadcast.new_origin) {
962 return Some(CallOutcome {
963 result: InterpreterResult {
964 result: InstructionResult::Revert,
965 output: Error::encode(err),
966 gas,
967 },
968 memory_offset: call.return_memory_offset.clone(),
969 was_precompile_called: false,
970 precompile_call_logs: vec![],
971 });
972 }
973
974 let input = call.input.bytes(ecx);
975 let chain_id = ecx.cfg().chain_id();
976 let rpc = ecx.db().active_fork_url();
977 let account =
978 ecx.journal_mut().evm_state_mut().get_mut(&broadcast.new_origin).unwrap();
979
980 let mut tx_req = TransactionRequestFor::<FEN>::default()
981 .with_from(broadcast.new_origin)
982 .with_to(call.target_address)
983 .with_value(call.transfer_value().unwrap_or_default())
984 .with_input(input)
985 .with_nonce(account.info.nonce)
986 .with_chain_id(chain_id);
987 if is_fixed_gas_limit {
988 tx_req.set_gas_limit(call.gas_limit)
989 }
990
991 let active_delegations = std::mem::take(&mut self.active_delegations);
992 if let Some(blob_sidecar) = self.active_blob_sidecar.take() {
994 if !active_delegations.is_empty() {
996 let msg = "both delegation and blob are active; `attachBlob` and `attachDelegation` are not compatible";
997 return Some(CallOutcome {
998 result: InterpreterResult {
999 result: InstructionResult::Revert,
1000 output: Error::encode(msg),
1001 gas,
1002 },
1003 memory_offset: call.return_memory_offset.clone(),
1004 was_precompile_called: false,
1005 precompile_call_logs: vec![],
1006 });
1007 }
1008 tx_req.set_blob_sidecar(blob_sidecar);
1009 }
1010
1011 if !active_delegations.is_empty() {
1013 for auth in &active_delegations {
1014 let Ok(authority) = auth.recover_authority() else {
1015 continue;
1016 };
1017 if authority == broadcast.new_origin {
1018 account.info.nonce += 1;
1021 }
1022 }
1023 tx_req.set_authorization_list(active_delegations);
1024 }
1025 if let Some(fee_token) = self.config.fee_token {
1026 tx_req.set_fee_token(fee_token);
1027 }
1028 self.broadcastable_transactions.push_back(BroadcastableTransaction {
1029 rpc,
1030 transaction: TransactionMaybeSigned::new(tx_req),
1031 });
1032 debug!(target: "cheatcodes", tx=?self.broadcastable_transactions.back().unwrap(), "broadcastable call");
1033
1034 if !self.config.evm_opts.isolate {
1036 let prev = account.info.nonce;
1037 account.info.nonce += 1;
1038 debug!(target: "cheatcodes", address=%broadcast.new_origin, nonce=prev+1, prev, "incremented nonce");
1039 }
1040 } else if broadcast.single_call {
1041 let msg = "`staticcall`s are not allowed after `broadcast`; use `startBroadcast` instead";
1042 return Some(CallOutcome {
1043 result: InterpreterResult {
1044 result: InstructionResult::Revert,
1045 output: Error::encode(msg),
1046 gas,
1047 },
1048 memory_offset: call.return_memory_offset.clone(),
1049 was_precompile_called: false,
1050 precompile_call_logs: vec![],
1051 });
1052 }
1053 }
1054 }
1055
1056 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1058 let (initialized, old_balance, old_nonce) =
1061 if let Ok(acc) = ecx.journal_mut().load_account(call.target_address) {
1062 (acc.data.info.exists(), acc.data.info.balance, acc.data.info.nonce)
1063 } else {
1064 (false, U256::ZERO, 0)
1065 };
1066
1067 let kind = match call.scheme {
1068 CallScheme::Call => crate::Vm::AccountAccessKind::Call,
1069 CallScheme::CallCode => crate::Vm::AccountAccessKind::CallCode,
1070 CallScheme::DelegateCall => crate::Vm::AccountAccessKind::DelegateCall,
1071 CallScheme::StaticCall => crate::Vm::AccountAccessKind::StaticCall,
1072 };
1073
1074 recorded_account_diffs_stack.push(vec![AccountAccess {
1080 chainInfo: crate::Vm::ChainInfo {
1081 forkId: ecx.db().active_fork_id().unwrap_or_default(),
1082 chainId: U256::from(ecx.cfg().chain_id()),
1083 },
1084 accessor: call.caller,
1085 account: call.bytecode_address,
1086 kind,
1087 initialized,
1088 oldBalance: old_balance,
1089 newBalance: U256::ZERO, oldNonce: old_nonce,
1091 newNonce: 0, value: call.call_value(),
1093 data: call.input.bytes(ecx),
1094 reverted: false,
1095 deployedCode: Bytes::new(),
1096 storageAccesses: vec![], depth: ecx.journal().depth().try_into().expect("journaled state depth exceeds u64"),
1098 }]);
1099 }
1100
1101 None
1102 }
1103
1104 pub fn rng(&mut self) -> &mut impl Rng {
1105 self.test_runner().rng()
1106 }
1107
1108 pub fn test_runner(&mut self) -> &mut TestRunner {
1109 self.test_runner.get_or_insert_with(|| match self.config.seed {
1110 Some(seed) => TestRunner::new_with_rng(
1111 proptest::test_runner::Config::default(),
1112 TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>()),
1113 ),
1114 None => TestRunner::new(proptest::test_runner::Config::default()),
1115 })
1116 }
1117
1118 pub fn set_seed(&mut self, seed: U256) {
1119 self.test_runner = Some(TestRunner::new_with_rng(
1120 proptest::test_runner::Config::default(),
1121 TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>()),
1122 ));
1123 }
1124
1125 pub fn arbitrary_storage(&mut self) -> &mut ArbitraryStorage {
1128 self.arbitrary_storage.get_or_insert_with(ArbitraryStorage::default)
1129 }
1130
1131 pub fn has_arbitrary_storage(&self, address: &Address) -> bool {
1133 match &self.arbitrary_storage {
1134 Some(storage) => storage.values.contains_key(address),
1135 None => false,
1136 }
1137 }
1138
1139 pub fn should_overwrite_arbitrary_storage(
1143 &self,
1144 address: &Address,
1145 storage_slot: U256,
1146 ) -> bool {
1147 match &self.arbitrary_storage {
1148 Some(storage) => {
1149 storage.overwrites.contains(address)
1150 && storage
1151 .values
1152 .get(address)
1153 .and_then(|arbitrary_values| arbitrary_values.get(&storage_slot))
1154 .is_none()
1155 }
1156 None => false,
1157 }
1158 }
1159
1160 pub fn is_arbitrary_storage_copy(&self, address: &Address) -> bool {
1162 match &self.arbitrary_storage {
1163 Some(storage) => storage.copies.contains_key(address),
1164 None => false,
1165 }
1166 }
1167
1168 pub fn struct_defs(&self) -> Option<&foundry_common::fmt::StructDefinitions> {
1170 self.analysis.as_ref().and_then(|analysis| analysis.struct_defs().ok())
1171 }
1172}
1173
1174impl<FEN: FoundryEvmNetwork> Inspector<FoundryContextFor<'_, FEN>> for Cheatcodes<FEN> {
1175 fn initialize_interp(
1176 &mut self,
1177 interpreter: &mut Interpreter,
1178 ecx: &mut FoundryContextFor<'_, FEN>,
1179 ) {
1180 if let Some(block) = self.block.take() {
1183 ecx.set_block(block);
1184 }
1185 if let Some(gas_price) = self.gas_price.take() {
1186 ecx.tx_mut().set_gas_price(gas_price);
1187 }
1188
1189 if self.gas_metering.paused {
1191 self.gas_metering.paused_frames.push(interpreter.gas);
1192 }
1193
1194 if let Some(expected) = &mut self.expected_revert {
1196 expected.max_depth = max(ecx.journal().depth(), expected.max_depth);
1197 }
1198 }
1199
1200 fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut FoundryContextFor<'_, FEN>) {
1201 self.pc = interpreter.bytecode.pc();
1202
1203 if self.broadcast.is_some() {
1204 self.set_gas_limit_type(interpreter);
1205 }
1206
1207 if self.gas_metering.paused {
1209 self.meter_gas(interpreter);
1210 }
1211
1212 if self.gas_metering.reset {
1214 self.meter_gas_reset(interpreter);
1215 }
1216
1217 if self.recording_accesses {
1219 self.record_accesses(interpreter);
1220 }
1221
1222 if self.recorded_account_diffs_stack.is_some() {
1224 self.record_state_diffs(interpreter, ecx);
1225 }
1226
1227 if !self.allowed_mem_writes.is_empty() {
1229 self.check_mem_opcodes(
1230 interpreter,
1231 ecx.journal().depth().try_into().expect("journaled state depth exceeds u64"),
1232 );
1233 }
1234
1235 if let Some(mapping_slots) = &mut self.mapping_slots {
1237 mapping_step(mapping_slots, interpreter);
1238 }
1239
1240 if self.gas_metering.recording {
1242 self.meter_gas_record(interpreter, ecx);
1243 }
1244 }
1245
1246 fn step_end(&mut self, interpreter: &mut Interpreter, ecx: &mut FoundryContextFor<'_, FEN>) {
1247 if self.gas_metering.paused {
1248 self.meter_gas_end(interpreter);
1249 }
1250
1251 if self.gas_metering.touched {
1252 self.meter_gas_check(interpreter);
1253 }
1254
1255 if self.arbitrary_storage.is_some() {
1257 self.arbitrary_storage_end(interpreter, ecx);
1258 }
1259 }
1260
1261 fn log(&mut self, _ecx: &mut FoundryContextFor<'_, FEN>, log: Log) {
1262 if !self.expected_emits.is_empty()
1263 && let Some(err) = expect::handle_expect_emit(self, &log, None)
1264 {
1265 let _ = sh_err!("{err:?}");
1269 }
1270
1271 record_logs(&mut self.recorded_logs, &log);
1273 }
1274
1275 fn log_full(
1276 &mut self,
1277 interpreter: &mut Interpreter,
1278 _ecx: &mut FoundryContextFor<'_, FEN>,
1279 log: Log,
1280 ) {
1281 if !self.expected_emits.is_empty() {
1282 expect::handle_expect_emit(self, &log, Some(interpreter));
1283 }
1284
1285 record_logs(&mut self.recorded_logs, &log);
1287 }
1288
1289 fn call(
1290 &mut self,
1291 ecx: &mut FoundryContextFor<'_, FEN>,
1292 inputs: &mut CallInputs,
1293 ) -> Option<CallOutcome> {
1294 Self::call_with_executor(self, ecx, inputs, &mut TransparentCheatcodesExecutor)
1295 }
1296
1297 fn call_end(
1298 &mut self,
1299 ecx: &mut FoundryContextFor<'_, FEN>,
1300 call: &CallInputs,
1301 outcome: &mut CallOutcome,
1302 ) {
1303 let cheatcode_call = call.target_address == CHEATCODE_ADDRESS
1304 || call.target_address == HARDHAT_CONSOLE_ADDRESS;
1305
1306 if !cheatcode_call {
1310 let curr_depth = ecx.journal().depth();
1312 if let Some(prank) = &self.get_prank(curr_depth)
1313 && curr_depth == prank.depth
1314 {
1315 ecx.tx_mut().set_caller(prank.prank_origin);
1316
1317 if prank.single_call {
1319 self.pranks.remove(&curr_depth);
1320 }
1321 }
1322
1323 if let Some(broadcast) = &self.broadcast
1325 && curr_depth == broadcast.depth
1326 {
1327 ecx.tx_mut().set_caller(broadcast.original_origin);
1328
1329 if broadcast.single_call {
1331 let _ = self.broadcast.take();
1332 }
1333 }
1334 }
1335
1336 if let Some(assume_no_revert) = &mut self.assume_no_revert {
1338 if outcome.result.is_revert() && assume_no_revert.reverted_by.is_none() {
1341 assume_no_revert.reverted_by = Some(call.target_address);
1342 }
1343
1344 let curr_depth = ecx.journal().depth();
1346 if curr_depth <= assume_no_revert.depth && !cheatcode_call {
1347 if outcome.result.is_revert() {
1350 let assume_no_revert = std::mem::take(&mut self.assume_no_revert).unwrap();
1351 return match revert_handlers::handle_assume_no_revert(
1352 &assume_no_revert,
1353 outcome.result.result,
1354 &outcome.result.output,
1355 &self.config.available_artifacts,
1356 ) {
1357 Ok(_) => {
1360 outcome.result.output = Error::from(MAGIC_ASSUME).abi_encode().into();
1361 }
1362 Err(error) => {
1365 trace!(expected=?assume_no_revert, ?error, status=?outcome.result.result, "Expected revert mismatch");
1366 outcome.result.result = InstructionResult::Revert;
1367 outcome.result.output = error.abi_encode().into();
1368 }
1369 };
1370 }
1371 self.assume_no_revert = None;
1373 }
1374 }
1375
1376 if let Some(expected_revert) = &mut self.expected_revert {
1378 if outcome.result.is_revert() {
1381 if expected_revert.reverter.is_some()
1385 && (expected_revert.reverted_by.is_none() || expected_revert.count > 1)
1386 {
1387 expected_revert.reverted_by = Some(call.target_address);
1388 }
1389 }
1390
1391 let curr_depth = ecx.journal().depth();
1392 if curr_depth <= expected_revert.depth {
1393 let needs_processing = match expected_revert.kind {
1394 ExpectedRevertKind::Default => !cheatcode_call,
1395 ExpectedRevertKind::Cheatcode { pending_processing } => {
1398 cheatcode_call && !pending_processing
1399 }
1400 };
1401
1402 if needs_processing {
1403 let mut expected_revert = std::mem::take(&mut self.expected_revert).unwrap();
1404 return match revert_handlers::handle_expect_revert(
1405 cheatcode_call,
1406 false,
1407 self.config.internal_expect_revert,
1408 &expected_revert,
1409 outcome.result.result,
1410 outcome.result.output.clone(),
1411 &self.config.available_artifacts,
1412 ) {
1413 Err(error) => {
1414 trace!(expected=?expected_revert, ?error, status=?outcome.result.result, "Expected revert mismatch");
1415 outcome.result.result = InstructionResult::Revert;
1416 outcome.result.output = error.abi_encode().into();
1417 }
1418 Ok((_, retdata)) => {
1419 expected_revert.actual_count += 1;
1420 if expected_revert.actual_count < expected_revert.count {
1421 self.expected_revert = Some(expected_revert);
1422 }
1423 outcome.result.result = InstructionResult::Return;
1424 outcome.result.output = retdata;
1425 }
1426 };
1427 }
1428
1429 if let ExpectedRevertKind::Cheatcode { pending_processing } =
1432 &mut self.expected_revert.as_mut().unwrap().kind
1433 {
1434 *pending_processing = false;
1435 }
1436 }
1437 }
1438
1439 if cheatcode_call {
1442 return;
1443 }
1444
1445 let gas = outcome.result.gas;
1448 self.gas_metering.last_call_gas = Some(crate::Vm::Gas {
1449 gasLimit: gas.limit(),
1450 gasTotalUsed: gas.total_gas_spent(),
1451 gasMemoryUsed: 0,
1452 gasRefunded: gas.refunded(),
1453 gasRemaining: gas.remaining(),
1454 });
1455
1456 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1459 if ecx.journal().depth() > 0
1461 && let Some(mut last_recorded_depth) = recorded_account_diffs_stack.pop()
1462 {
1463 if outcome.result.is_revert() {
1466 for element in &mut *last_recorded_depth {
1467 element.reverted = true;
1468 for storage_access in &mut element.storageAccesses {
1469 storage_access.reverted = true;
1470 }
1471 }
1472 }
1473
1474 if let Some(call_access) = last_recorded_depth.first_mut() {
1475 let curr_depth = ecx.journal().depth();
1480 if call_access.depth == curr_depth as u64
1481 && let Ok(acc) = ecx.journal_mut().load_account(call.target_address)
1482 {
1483 debug_assert!(access_is_call(call_access.kind));
1484 call_access.newBalance = acc.data.info.balance;
1485 call_access.newNonce = acc.data.info.nonce;
1486 }
1487 if let Some(last) = recorded_account_diffs_stack.last_mut() {
1492 last.extend(last_recorded_depth);
1493 } else {
1494 recorded_account_diffs_stack.push(last_recorded_depth);
1495 }
1496 }
1497 }
1498 }
1499
1500 let should_check_emits = self
1512 .expected_emits
1513 .iter()
1514 .any(|(expected, _)| {
1515 let curr_depth = ecx.journal().depth();
1516 expected.depth == curr_depth
1517 }) &&
1518 !call.is_static;
1520 if should_check_emits {
1521 let expected_counts = self
1522 .expected_emits
1523 .iter()
1524 .filter_map(|(expected, count_map)| {
1525 let count = match expected.address {
1526 Some(emitter) => match count_map.get(&emitter) {
1527 Some(log_count) => expected
1528 .log
1529 .as_ref()
1530 .map(|l| log_count.count(l))
1531 .unwrap_or_else(|| log_count.count_unchecked()),
1532 None => 0,
1533 },
1534 None => match &expected.log {
1535 Some(log) => count_map.values().map(|logs| logs.count(log)).sum(),
1536 None => count_map.values().map(|logs| logs.count_unchecked()).sum(),
1537 },
1538 };
1539
1540 (count != expected.count).then_some((expected, count))
1541 })
1542 .collect::<Vec<_>>();
1543
1544 if let Some((expected, _)) = self
1546 .expected_emits
1547 .iter()
1548 .find(|(expected, _)| !expected.found && expected.count > 0)
1549 {
1550 outcome.result.result = InstructionResult::Revert;
1551 let error_msg = expected.mismatch_error.as_deref().unwrap_or("log != expected log");
1552 outcome.result.output = error_msg.abi_encode().into();
1553 return;
1554 }
1555
1556 if !expected_counts.is_empty() {
1557 let msg = if outcome.result.is_ok() {
1558 let (expected, count) = expected_counts.first().unwrap();
1559 format!("log emitted {count} times, expected {}", expected.count)
1560 } else {
1561 "expected an emit, but the call reverted instead. \
1562 ensure you're testing the happy path when using `expectEmit`"
1563 .to_string()
1564 };
1565
1566 outcome.result.result = InstructionResult::Revert;
1567 outcome.result.output = Error::encode(msg);
1568 return;
1569 }
1570
1571 self.expected_emits.clear()
1575 }
1576
1577 let diag = self.fork_revert_diagnostic.take();
1580
1581 if outcome.result.is_revert()
1584 && let Some(err) = diag
1585 {
1586 outcome.result.output = Error::encode(err.to_error_msg(&self.labels));
1587 return;
1588 }
1589
1590 if let TxKind::Call(test_contract) = ecx.tx().kind() {
1593 if ecx.db().is_forked_mode()
1596 && outcome.result.result == InstructionResult::Stop
1597 && call.target_address != test_contract
1598 {
1599 self.fork_revert_diagnostic =
1600 ecx.db().diagnose_revert(call.target_address, ecx.journal().evm_state());
1601 }
1602 }
1603
1604 if ecx.journal().depth() == 0 {
1606 if outcome.result.is_revert() {
1610 return;
1611 }
1612
1613 for (address, calldatas) in &self.expected_calls {
1618 for (calldata, (expected, actual_count)) in calldatas {
1620 let ExpectedCallData { gas, min_gas, value, count, call_type } = expected;
1622
1623 let failed = match call_type {
1624 ExpectedCallType::Count => *count != *actual_count,
1628 ExpectedCallType::NonCount => *count > *actual_count,
1633 };
1634 if failed {
1635 let expected_values = [
1636 Some(format!("data {}", hex::encode_prefixed(calldata))),
1637 value.as_ref().map(|v| format!("value {v}")),
1638 gas.map(|g| format!("gas {g}")),
1639 min_gas.map(|g| format!("minimum gas {g}")),
1640 ]
1641 .into_iter()
1642 .flatten()
1643 .join(", ");
1644 let but = if outcome.result.is_ok() {
1645 let s = if *actual_count == 1 { "" } else { "s" };
1646 format!("was called {actual_count} time{s}")
1647 } else {
1648 "the call reverted instead; \
1649 ensure you're testing the happy path when using `expectCall`"
1650 .to_string()
1651 };
1652 let s = if *count == 1 { "" } else { "s" };
1653 let msg = format!(
1654 "expected call to {address} with {expected_values} \
1655 to be called {count} time{s}, but {but}"
1656 );
1657 outcome.result.result = InstructionResult::Revert;
1658 outcome.result.output = Error::encode(msg);
1659
1660 return;
1661 }
1662 }
1663 }
1664
1665 for (expected, _) in &mut self.expected_emits {
1669 if expected.count == 0 && !expected.found {
1670 expected.found = true;
1671 }
1672 }
1673 self.expected_emits.retain(|(expected, _)| !expected.found);
1674 if !self.expected_emits.is_empty() {
1676 let msg = if outcome.result.is_ok() {
1677 "expected an emit, but no logs were emitted afterwards. \
1678 you might have mismatched events or not enough events were emitted"
1679 } else {
1680 "expected an emit, but the call reverted instead. \
1681 ensure you're testing the happy path when using `expectEmit`"
1682 };
1683 outcome.result.result = InstructionResult::Revert;
1684 outcome.result.output = Error::encode(msg);
1685 return;
1686 }
1687
1688 if let Some(expected_create) = self.expected_creates.first() {
1690 let msg = format!(
1691 "expected {} call by address {} for bytecode {} but not found",
1692 expected_create.create_scheme,
1693 hex::encode_prefixed(expected_create.deployer),
1694 hex::encode_prefixed(&expected_create.bytecode),
1695 );
1696 outcome.result.result = InstructionResult::Revert;
1697 outcome.result.output = Error::encode(msg);
1698 }
1699 }
1700 }
1701
1702 fn create(
1703 &mut self,
1704 ecx: &mut FoundryContextFor<'_, FEN>,
1705 mut input: &mut CreateInputs,
1706 ) -> Option<CreateOutcome> {
1707 if let Some(spec_id) = self.execution_evm_version {
1709 ecx.cfg_mut().set_spec_and_mainnet_gas_params(spec_id);
1710 }
1711
1712 let gas = Gas::new(input.gas_limit());
1713 if self.intercept_next_create_call {
1715 self.intercept_next_create_call = false;
1717
1718 let output = input.init_code();
1720
1721 return Some(CreateOutcome {
1723 result: InterpreterResult { result: InstructionResult::Revert, output, gas },
1724 address: None,
1725 });
1726 }
1727
1728 let curr_depth = ecx.journal().depth();
1729
1730 if let Some(prank) = &self.get_prank(curr_depth)
1732 && curr_depth >= prank.depth
1733 && input.caller() == prank.prank_caller
1734 {
1735 let prank_applied = if curr_depth == prank.depth {
1737 let _ = journaled_account(ecx, prank.new_caller);
1739 input.set_caller(prank.new_caller);
1740 true
1741 } else {
1742 false
1743 };
1744
1745 let prank_applied = if let Some(new_origin) = prank.new_origin {
1747 ecx.tx_mut().set_caller(new_origin);
1748 true
1749 } else {
1750 prank_applied
1751 };
1752
1753 if prank_applied && let Some(applied_prank) = prank.first_time_applied() {
1755 self.pranks.insert(curr_depth, applied_prank);
1756 }
1757 }
1758
1759 self.apply_accesslist(ecx);
1761
1762 if let Some(broadcast) = &mut self.broadcast
1764 && curr_depth >= broadcast.depth
1765 && input.caller() == broadcast.original_caller
1766 {
1767 if let Err(err) = ecx.journal_mut().load_account(broadcast.new_origin) {
1768 return Some(CreateOutcome {
1769 result: InterpreterResult {
1770 result: InstructionResult::Revert,
1771 output: Error::encode(err),
1772 gas,
1773 },
1774 address: None,
1775 });
1776 }
1777
1778 ecx.tx_mut().set_caller(broadcast.new_origin);
1779
1780 if curr_depth == broadcast.depth || broadcast.deploy_from_code {
1781 broadcast.deploy_from_code = false;
1783
1784 input.set_caller(broadcast.new_origin);
1785
1786 let rpc = ecx.db().active_fork_url();
1787 let account = &ecx.journal().evm_state()[&broadcast.new_origin];
1788 let mut tx_req = TransactionRequestFor::<FEN>::default()
1789 .with_from(broadcast.new_origin)
1790 .with_kind(TxKind::Create)
1791 .with_value(input.value())
1792 .with_input(input.init_code())
1793 .with_nonce(account.info.nonce);
1794 if let Some(fee_token) = self.config.fee_token {
1795 tx_req.set_fee_token(fee_token);
1796 }
1797 self.broadcastable_transactions.push_back(BroadcastableTransaction {
1798 rpc,
1799 transaction: TransactionMaybeSigned::new(tx_req),
1800 });
1801
1802 input.log_debug(self, &input.scheme().unwrap_or(CreateScheme::Create));
1803 }
1804 }
1805
1806 let address = input.allow_cheatcodes(self, ecx);
1808
1809 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1811 recorded_account_diffs_stack.push(vec![AccountAccess {
1812 chainInfo: crate::Vm::ChainInfo {
1813 forkId: ecx.db().active_fork_id().unwrap_or_default(),
1814 chainId: U256::from(ecx.cfg().chain_id()),
1815 },
1816 accessor: input.caller(),
1817 account: address,
1818 kind: crate::Vm::AccountAccessKind::Create,
1819 initialized: true,
1820 oldBalance: U256::ZERO, newBalance: U256::ZERO, oldNonce: 0, newNonce: 1, value: input.value(),
1825 data: input.init_code(),
1826 reverted: false,
1827 deployedCode: Bytes::new(), storageAccesses: vec![], depth: curr_depth as u64,
1830 }]);
1831 }
1832
1833 None
1834 }
1835
1836 fn create_end(
1837 &mut self,
1838 ecx: &mut FoundryContextFor<'_, FEN>,
1839 call: &CreateInputs,
1840 outcome: &mut CreateOutcome,
1841 ) {
1842 let call = Some(call);
1843 let curr_depth = ecx.journal().depth();
1844
1845 if let Some(prank) = &self.get_prank(curr_depth)
1847 && curr_depth == prank.depth
1848 {
1849 ecx.tx_mut().set_caller(prank.prank_origin);
1850
1851 if prank.single_call {
1853 std::mem::take(&mut self.pranks);
1854 }
1855 }
1856
1857 if let Some(broadcast) = &self.broadcast
1859 && curr_depth == broadcast.depth
1860 {
1861 ecx.tx_mut().set_caller(broadcast.original_origin);
1862
1863 if broadcast.single_call {
1865 std::mem::take(&mut self.broadcast);
1866 }
1867 }
1868
1869 if let Some(expected_revert) = &self.expected_revert
1871 && curr_depth <= expected_revert.depth
1872 && matches!(expected_revert.kind, ExpectedRevertKind::Default)
1873 {
1874 let mut expected_revert = std::mem::take(&mut self.expected_revert).unwrap();
1875 return match revert_handlers::handle_expect_revert(
1876 false,
1877 true,
1878 self.config.internal_expect_revert,
1879 &expected_revert,
1880 outcome.result.result,
1881 outcome.result.output.clone(),
1882 &self.config.available_artifacts,
1883 ) {
1884 Ok((address, retdata)) => {
1885 expected_revert.actual_count += 1;
1886 if expected_revert.actual_count < expected_revert.count {
1887 self.expected_revert = Some(expected_revert.clone());
1888 }
1889
1890 outcome.result.result = InstructionResult::Return;
1891 outcome.result.output = retdata;
1892 outcome.address = address;
1893 }
1894 Err(err) => {
1895 outcome.result.result = InstructionResult::Revert;
1896 outcome.result.output = err.abi_encode().into();
1897 }
1898 };
1899 }
1900
1901 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1904 if curr_depth > 0
1906 && let Some(last_depth) = &mut recorded_account_diffs_stack.pop()
1907 {
1908 if outcome.result.is_revert() {
1911 for element in &mut *last_depth {
1912 element.reverted = true;
1913 for storage_access in &mut element.storageAccesses {
1914 storage_access.reverted = true;
1915 }
1916 }
1917 }
1918
1919 if let Some(create_access) = last_depth.first_mut() {
1920 let depth = ecx.journal().depth();
1925 if create_access.depth == depth as u64 {
1926 debug_assert_eq!(
1927 create_access.kind as u8,
1928 crate::Vm::AccountAccessKind::Create as u8
1929 );
1930 if let Some(address) = outcome.address
1931 && let Ok(created_acc) = ecx.journal_mut().load_account(address)
1932 {
1933 create_access.newBalance = created_acc.data.info.balance;
1934 create_access.newNonce = created_acc.data.info.nonce;
1935 create_access.deployedCode = created_acc
1936 .data
1937 .info
1938 .code
1939 .clone()
1940 .unwrap_or_default()
1941 .original_bytes();
1942 }
1943 }
1944 if let Some(last) = recorded_account_diffs_stack.last_mut() {
1949 last.append(last_depth);
1950 } else {
1951 recorded_account_diffs_stack.push(last_depth.clone());
1952 }
1953 }
1954 }
1955 }
1956
1957 if !self.expected_creates.is_empty()
1959 && let (Some(address), Some(call)) = (outcome.address, call)
1960 && let Ok(created_acc) = ecx.journal_mut().load_account(address)
1961 {
1962 let bytecode = created_acc.data.info.code.clone().unwrap_or_default().original_bytes();
1963 if let Some((index, _)) =
1964 self.expected_creates.iter().find_position(|expected_create| {
1965 expected_create.deployer == call.caller()
1966 && expected_create.create_scheme.eq(call.scheme().into())
1967 && expected_create.bytecode == bytecode
1968 })
1969 {
1970 self.expected_creates.swap_remove(index);
1971 }
1972 }
1973 }
1974}
1975
1976impl<FEN: FoundryEvmNetwork> InspectorExt for Cheatcodes<FEN> {
1977 fn should_use_create2_factory(&mut self, depth: usize, inputs: &CreateInputs) -> bool {
1978 if let CreateScheme::Create2 { .. } = inputs.scheme() {
1979 let target_depth = if let Some(prank) = &self.get_prank(depth) {
1980 prank.depth
1981 } else if let Some(broadcast) = &self.broadcast {
1982 broadcast.depth
1983 } else {
1984 1
1985 };
1986
1987 depth == target_depth
1988 && (self.broadcast.is_some() || self.config.always_use_create_2_factory)
1989 } else {
1990 false
1991 }
1992 }
1993
1994 fn create2_deployer(&self) -> Address {
1995 self.config.evm_opts.create2_deployer
1996 }
1997}
1998
1999impl<FEN: FoundryEvmNetwork> Cheatcodes<FEN> {
2000 #[cold]
2001 fn meter_gas(&mut self, interpreter: &mut Interpreter) {
2002 if let Some(paused_gas) = self.gas_metering.paused_frames.last() {
2003 let memory = *interpreter.gas.memory();
2006 interpreter.gas = *paused_gas;
2007 interpreter.gas.memory_mut().words_num = memory.words_num;
2008 interpreter.gas.memory_mut().expansion_cost = memory.expansion_cost;
2009 } else {
2010 self.gas_metering.paused_frames.push(interpreter.gas);
2012 }
2013 }
2014
2015 #[cold]
2016 fn meter_gas_record(
2017 &mut self,
2018 interpreter: &mut Interpreter,
2019 ecx: &mut FoundryContextFor<'_, FEN>,
2020 ) {
2021 if interpreter.bytecode.action.as_ref().and_then(|i| i.instruction_result()).is_none() {
2022 self.gas_metering.gas_records.iter_mut().for_each(|record| {
2023 let curr_depth = ecx.journal().depth();
2024 if curr_depth == record.depth {
2025 if self.gas_metering.last_gas_used != 0 {
2028 let gas_diff = interpreter
2029 .gas
2030 .total_gas_spent()
2031 .saturating_sub(self.gas_metering.last_gas_used);
2032 record.gas_used = record.gas_used.saturating_add(gas_diff);
2033 }
2034
2035 self.gas_metering.last_gas_used = interpreter.gas.total_gas_spent();
2038 }
2039 });
2040 }
2041 }
2042
2043 #[cold]
2044 fn meter_gas_end(&mut self, interpreter: &mut Interpreter) {
2045 if let Some(interpreter_action) = interpreter.bytecode.action.as_ref()
2047 && will_exit(interpreter_action)
2048 {
2049 self.gas_metering.paused_frames.pop();
2050 }
2051 }
2052
2053 #[cold]
2054 fn meter_gas_reset(&mut self, interpreter: &mut Interpreter) {
2055 let mut gas = Gas::new(interpreter.gas.limit());
2056 gas.memory_mut().words_num = interpreter.gas.memory().words_num;
2057 gas.memory_mut().expansion_cost = interpreter.gas.memory().expansion_cost;
2058 interpreter.gas = gas;
2059 self.gas_metering.reset = false;
2060 }
2061
2062 #[cold]
2063 fn meter_gas_check(&mut self, interpreter: &mut Interpreter) {
2064 if let Some(interpreter_action) = interpreter.bytecode.action.as_ref()
2065 && will_exit(interpreter_action)
2066 {
2067 if interpreter.gas.total_gas_spent()
2071 < u64::try_from(interpreter.gas.refunded()).unwrap_or_default()
2072 {
2073 interpreter.gas = Gas::new(interpreter.gas.limit());
2074 }
2075 }
2076 }
2077
2078 #[cold]
2086 fn arbitrary_storage_end(
2087 &mut self,
2088 interpreter: &mut Interpreter,
2089 ecx: &mut FoundryContextFor<'_, FEN>,
2090 ) {
2091 let (key, target_address) = if interpreter.bytecode.opcode() == op::SLOAD {
2092 (try_or_return!(interpreter.stack.peek(0)), interpreter.input.target_address)
2093 } else {
2094 return;
2095 };
2096
2097 let Some(value) = ecx.sload(target_address, key) else {
2098 return;
2099 };
2100
2101 if (value.is_cold && value.data.is_zero())
2102 || self.should_overwrite_arbitrary_storage(&target_address, key)
2103 {
2104 if self.has_arbitrary_storage(&target_address) {
2105 let arbitrary_value = self.rng().random();
2106 self.arbitrary_storage.as_mut().unwrap().save(
2107 ecx,
2108 target_address,
2109 key,
2110 arbitrary_value,
2111 );
2112 } else if self.is_arbitrary_storage_copy(&target_address) {
2113 let arbitrary_value = self.rng().random();
2114 self.arbitrary_storage.as_mut().unwrap().copy(
2115 ecx,
2116 target_address,
2117 key,
2118 arbitrary_value,
2119 );
2120 }
2121 }
2122 }
2123
2124 #[cold]
2126 fn record_accesses(&mut self, interpreter: &mut Interpreter) {
2127 let access = &mut self.accesses;
2128 match interpreter.bytecode.opcode() {
2129 op::SLOAD => {
2130 let key = try_or_return!(interpreter.stack.peek(0));
2131 access.record_read(interpreter.input.target_address, key);
2132 }
2133 op::SSTORE => {
2134 let key = try_or_return!(interpreter.stack.peek(0));
2135 access.record_write(interpreter.input.target_address, key);
2136 }
2137 _ => {}
2138 }
2139 }
2140
2141 #[cold]
2142 fn record_state_diffs(
2143 &mut self,
2144 interpreter: &mut Interpreter,
2145 ecx: &mut FoundryContextFor<'_, FEN>,
2146 ) {
2147 let Some(account_accesses) = &mut self.recorded_account_diffs_stack else { return };
2148 match interpreter.bytecode.opcode() {
2149 op::SELFDESTRUCT => {
2150 let Some(last) = account_accesses.last_mut() else { return };
2152
2153 let target = try_or_return!(interpreter.stack.peek(0));
2155 let target = Address::from_word(B256::from(target));
2156 let (initialized, old_balance, old_nonce) = ecx
2157 .journal_mut()
2158 .load_account(target)
2159 .map(|account| {
2160 (
2161 account.data.info.exists(),
2162 account.data.info.balance,
2163 account.data.info.nonce,
2164 )
2165 })
2166 .unwrap_or_default();
2167
2168 let value = ecx
2170 .balance(interpreter.input.target_address)
2171 .map(|b| b.data)
2172 .unwrap_or(U256::ZERO);
2173
2174 last.push(crate::Vm::AccountAccess {
2176 chainInfo: crate::Vm::ChainInfo {
2177 forkId: ecx.db().active_fork_id().unwrap_or_default(),
2178 chainId: U256::from(ecx.cfg().chain_id()),
2179 },
2180 accessor: interpreter.input.target_address,
2181 account: target,
2182 kind: crate::Vm::AccountAccessKind::SelfDestruct,
2183 initialized,
2184 oldBalance: old_balance,
2185 newBalance: old_balance + value,
2186 oldNonce: old_nonce,
2187 newNonce: old_nonce, value,
2189 data: Bytes::new(),
2190 reverted: false,
2191 deployedCode: Bytes::new(),
2192 storageAccesses: vec![],
2193 depth: ecx
2194 .journal()
2195 .depth()
2196 .try_into()
2197 .expect("journaled state depth exceeds u64"),
2198 });
2199 }
2200
2201 op::SLOAD => {
2202 let Some(last) = account_accesses.last_mut() else { return };
2203
2204 let key = try_or_return!(interpreter.stack.peek(0));
2205 let address = interpreter.input.target_address;
2206
2207 let present_value = if ecx.journal_mut().load_account(address).is_ok()
2211 && let Some(previous) = ecx.sload(address, key)
2212 {
2213 previous.data
2214 } else {
2215 U256::ZERO
2216 };
2217 let access = crate::Vm::StorageAccess {
2218 account: interpreter.input.target_address,
2219 slot: key.into(),
2220 isWrite: false,
2221 previousValue: present_value.into(),
2222 newValue: present_value.into(),
2223 reverted: false,
2224 };
2225 let curr_depth =
2226 ecx.journal().depth().try_into().expect("journaled state depth exceeds u64");
2227 append_storage_access(last, access, curr_depth);
2228 }
2229 op::SSTORE => {
2230 let Some(last) = account_accesses.last_mut() else { return };
2231
2232 let key = try_or_return!(interpreter.stack.peek(0));
2233 let value = try_or_return!(interpreter.stack.peek(1));
2234 let address = interpreter.input.target_address;
2235 let previous_value = if ecx.journal_mut().load_account(address).is_ok()
2238 && let Some(previous) = ecx.sload(address, key)
2239 {
2240 previous.data
2241 } else {
2242 U256::ZERO
2243 };
2244
2245 let access = crate::Vm::StorageAccess {
2246 account: address,
2247 slot: key.into(),
2248 isWrite: true,
2249 previousValue: previous_value.into(),
2250 newValue: value.into(),
2251 reverted: false,
2252 };
2253 let curr_depth =
2254 ecx.journal().depth().try_into().expect("journaled state depth exceeds u64");
2255 append_storage_access(last, access, curr_depth);
2256 }
2257
2258 op::EXTCODECOPY | op::EXTCODESIZE | op::EXTCODEHASH | op::BALANCE => {
2260 let kind = match interpreter.bytecode.opcode() {
2261 op::EXTCODECOPY => crate::Vm::AccountAccessKind::Extcodecopy,
2262 op::EXTCODESIZE => crate::Vm::AccountAccessKind::Extcodesize,
2263 op::EXTCODEHASH => crate::Vm::AccountAccessKind::Extcodehash,
2264 op::BALANCE => crate::Vm::AccountAccessKind::Balance,
2265 _ => unreachable!(),
2266 };
2267 let address =
2268 Address::from_word(B256::from(try_or_return!(interpreter.stack.peek(0))));
2269 let (initialized, balance, nonce) =
2270 if let Ok(acc) = ecx.journal_mut().load_account(address) {
2271 (acc.data.info.exists(), acc.data.info.balance, acc.data.info.nonce)
2272 } else {
2273 (false, U256::ZERO, 0)
2274 };
2275 let curr_depth =
2276 ecx.journal().depth().try_into().expect("journaled state depth exceeds u64");
2277 let account_access = crate::Vm::AccountAccess {
2278 chainInfo: crate::Vm::ChainInfo {
2279 forkId: ecx.db().active_fork_id().unwrap_or_default(),
2280 chainId: U256::from(ecx.cfg().chain_id()),
2281 },
2282 accessor: interpreter.input.target_address,
2283 account: address,
2284 kind,
2285 initialized,
2286 oldBalance: balance,
2287 newBalance: balance,
2288 oldNonce: nonce,
2289 newNonce: nonce, value: U256::ZERO,
2291 data: Bytes::new(),
2292 reverted: false,
2293 deployedCode: Bytes::new(),
2294 storageAccesses: vec![],
2295 depth: curr_depth,
2296 };
2297 if let Some(last) = account_accesses.last_mut() {
2300 last.push(account_access);
2301 } else {
2302 account_accesses.push(vec![account_access]);
2303 }
2304 }
2305 _ => {}
2306 }
2307 }
2308
2309 #[cold]
2314 fn check_mem_opcodes(&self, interpreter: &mut Interpreter, depth: u64) {
2315 let Some(ranges) = self.allowed_mem_writes.get(&depth) else {
2316 return;
2317 };
2318
2319 macro_rules! mem_opcode_match {
2328 ($(($opcode:ident, $offset_depth:expr, $size_depth:expr, $writes:expr)),* $(,)?) => {
2329 match interpreter.bytecode.opcode() {
2330 op::MSTORE => {
2335 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2337
2338 if !ranges.iter().any(|range| {
2341 range.contains(&offset) && range.contains(&(offset + 31))
2342 }) {
2343 let value = try_or_return!(interpreter.stack.peek(1)).to_be_bytes::<32>();
2348 if value[..SELECTOR_LEN] == stopExpectSafeMemoryCall::SELECTOR {
2349 return
2350 }
2351
2352 disallowed_mem_write(offset, 32, interpreter, ranges);
2353 return
2354 }
2355 }
2356 op::MSTORE8 => {
2357 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2359
2360 if !ranges.iter().any(|range| range.contains(&offset)) {
2363 disallowed_mem_write(offset, 1, interpreter, ranges);
2364 return
2365 }
2366 }
2367
2368 op::MLOAD => {
2373 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2375
2376 if offset >= interpreter.memory.size() as u64 && !ranges.iter().any(|range| {
2380 range.contains(&offset) && range.contains(&(offset + 31))
2381 }) {
2382 disallowed_mem_write(offset, 32, interpreter, ranges);
2383 return
2384 }
2385 }
2386
2387 op::CALL => {
2392 let dest_offset = try_or_return!(interpreter.stack.peek(5)).saturating_to::<u64>();
2394
2395 let size = try_or_return!(interpreter.stack.peek(6)).saturating_to::<u64>();
2397
2398 let fail_cond = !ranges.iter().any(|range| {
2402 range.contains(&dest_offset) &&
2403 range.contains(&(dest_offset + size.saturating_sub(1)))
2404 });
2405
2406 if fail_cond {
2409 let to = Address::from_word(try_or_return!(interpreter.stack.peek(1)).to_be_bytes::<32>().into());
2413 if to == CHEATCODE_ADDRESS {
2414 let args_offset = try_or_return!(interpreter.stack.peek(3)).saturating_to::<usize>();
2415 let args_size = try_or_return!(interpreter.stack.peek(4)).saturating_to::<usize>();
2416 let memory_word = interpreter.memory.slice_len(args_offset, args_size);
2417 if memory_word[..SELECTOR_LEN] == stopExpectSafeMemoryCall::SELECTOR {
2418 return
2419 }
2420 }
2421
2422 disallowed_mem_write(dest_offset, size, interpreter, ranges);
2423 return
2424 }
2425 }
2426
2427 $(op::$opcode => {
2428 let dest_offset = try_or_return!(interpreter.stack.peek($offset_depth)).saturating_to::<u64>();
2430
2431 let size = try_or_return!(interpreter.stack.peek($size_depth)).saturating_to::<u64>();
2433
2434 let fail_cond = !ranges.iter().any(|range| {
2438 range.contains(&dest_offset) &&
2439 range.contains(&(dest_offset + size.saturating_sub(1)))
2440 }) && ($writes ||
2441 [dest_offset, (dest_offset + size).saturating_sub(1)].into_iter().any(|offset| {
2442 offset >= interpreter.memory.size() as u64
2443 })
2444 );
2445
2446 if fail_cond {
2449 disallowed_mem_write(dest_offset, size, interpreter, ranges);
2450 return
2451 }
2452 })*
2453
2454 _ => {}
2455 }
2456 }
2457 }
2458
2459 mem_opcode_match!(
2462 (CALLDATACOPY, 0, 2, true),
2463 (CODECOPY, 0, 2, true),
2464 (RETURNDATACOPY, 0, 2, true),
2465 (EXTCODECOPY, 1, 3, true),
2466 (CALLCODE, 5, 6, true),
2467 (STATICCALL, 4, 5, true),
2468 (DELEGATECALL, 4, 5, true),
2469 (KECCAK256, 0, 1, false),
2470 (LOG0, 0, 1, false),
2471 (LOG1, 0, 1, false),
2472 (LOG2, 0, 1, false),
2473 (LOG3, 0, 1, false),
2474 (LOG4, 0, 1, false),
2475 (CREATE, 1, 2, false),
2476 (CREATE2, 1, 2, false),
2477 (RETURN, 0, 1, false),
2478 (REVERT, 0, 1, false),
2479 );
2480 }
2481
2482 #[cold]
2483 fn set_gas_limit_type(&mut self, interpreter: &mut Interpreter) {
2484 match interpreter.bytecode.opcode() {
2485 op::CREATE2 => self.dynamic_gas_limit = true,
2486 op::CALL => {
2487 self.dynamic_gas_limit =
2490 try_or_return!(interpreter.stack.peek(0)) >= interpreter.gas.remaining() - 100
2491 }
2492 _ => self.dynamic_gas_limit = false,
2493 }
2494 }
2495}
2496
2497fn disallowed_mem_write(
2503 dest_offset: u64,
2504 size: u64,
2505 interpreter: &mut Interpreter,
2506 ranges: &[Range<u64>],
2507) {
2508 let revert_string = format!(
2509 "memory write at offset 0x{:02X} of size 0x{:02X} not allowed; safe range: {}",
2510 dest_offset,
2511 size,
2512 ranges.iter().map(|r| format!("[0x{:02X}, 0x{:02X})", r.start, r.end)).join(" U ")
2513 );
2514
2515 interpreter.bytecode.set_action(InterpreterAction::new_return(
2516 InstructionResult::Revert,
2517 Bytes::from(revert_string.into_bytes()),
2518 interpreter.gas,
2519 ));
2520}
2521
2522const fn access_is_call(kind: crate::Vm::AccountAccessKind) -> bool {
2524 matches!(
2525 kind,
2526 crate::Vm::AccountAccessKind::Call
2527 | crate::Vm::AccountAccessKind::StaticCall
2528 | crate::Vm::AccountAccessKind::CallCode
2529 | crate::Vm::AccountAccessKind::DelegateCall
2530 )
2531}
2532
2533fn record_logs(recorded_logs: &mut Option<Vec<Vm::Log>>, log: &Log) {
2535 if let Some(storage_recorded_logs) = recorded_logs {
2536 storage_recorded_logs.push(Vm::Log {
2537 topics: log.data.topics().to_vec(),
2538 data: log.data.data.clone(),
2539 emitter: log.address,
2540 });
2541 }
2542}
2543
2544fn append_storage_access(
2546 last: &mut Vec<AccountAccess>,
2547 storage_access: crate::Vm::StorageAccess,
2548 storage_depth: u64,
2549) {
2550 if !last.is_empty() && last.first().unwrap().depth < storage_depth {
2552 if last.len() == 1 {
2558 last.first_mut().unwrap().storageAccesses.push(storage_access);
2559 } else {
2560 let last_record = last.last_mut().unwrap();
2561 if last_record.kind as u8 == crate::Vm::AccountAccessKind::Resume as u8 {
2562 last_record.storageAccesses.push(storage_access);
2563 } else {
2564 let entry = last.first().unwrap();
2565 let resume_record = crate::Vm::AccountAccess {
2566 chainInfo: crate::Vm::ChainInfo {
2567 forkId: entry.chainInfo.forkId,
2568 chainId: entry.chainInfo.chainId,
2569 },
2570 accessor: entry.accessor,
2571 account: entry.account,
2572 kind: crate::Vm::AccountAccessKind::Resume,
2573 initialized: entry.initialized,
2574 storageAccesses: vec![storage_access],
2575 reverted: entry.reverted,
2576 oldBalance: U256::ZERO,
2578 newBalance: U256::ZERO,
2579 oldNonce: 0,
2580 newNonce: 0,
2581 value: U256::ZERO,
2582 data: Bytes::new(),
2583 deployedCode: Bytes::new(),
2584 depth: entry.depth,
2585 };
2586 last.push(resume_record);
2587 }
2588 }
2589 }
2590}
2591
2592const fn cheatcode_of<T: spec::CheatcodeDef>(_: &T) -> &'static spec::Cheatcode<'static> {
2594 T::CHEATCODE
2595}
2596
2597fn cheatcode_name(cheat: &spec::Cheatcode<'static>) -> &'static str {
2598 cheat.func.signature.split('(').next().unwrap()
2599}
2600
2601const fn cheatcode_id(cheat: &spec::Cheatcode<'static>) -> &'static str {
2602 cheat.func.id
2603}
2604
2605const fn cheatcode_signature(cheat: &spec::Cheatcode<'static>) -> &'static str {
2606 cheat.func.signature
2607}
2608
2609fn apply_dispatch<FEN: FoundryEvmNetwork>(
2611 calls: &Vm::VmCalls,
2612 ccx: &mut CheatsCtxt<'_, '_, FEN>,
2613 executor: &mut dyn CheatcodesExecutor<FEN>,
2614) -> Result {
2615 macro_rules! get_cheatcode {
2617 ($($variant:ident),*) => {
2618 match calls {
2619 $(Vm::VmCalls::$variant(cheat) => cheatcode_of(cheat),)*
2620 }
2621 };
2622 }
2623 let cheat = vm_calls!(get_cheatcode);
2624
2625 let _guard = debug_span!(target: "cheatcodes", "apply", id = %cheatcode_id(cheat)).entered();
2626 trace!(target: "cheatcodes", cheat = %cheatcode_signature(cheat), "applying");
2627
2628 if let spec::Status::Deprecated(replacement) = cheat.status {
2629 ccx.state.deprecated.insert(cheatcode_signature(cheat), replacement);
2630 }
2631
2632 macro_rules! dispatch {
2634 ($($variant:ident),*) => {
2635 match calls {
2636 $(Vm::VmCalls::$variant(cheat) => Cheatcode::apply_full(cheat, ccx, executor),)*
2637 }
2638 };
2639 }
2640 let mut result = vm_calls!(dispatch);
2641
2642 if let Err(e) = &mut result
2644 && e.is_str()
2645 {
2646 let name = cheatcode_name(cheat);
2647 if !name.contains("assert") && name != "rpcUrl" {
2651 *e = fmt_err!("vm.{name}: {e}");
2652 }
2653 }
2654
2655 trace!(
2656 target: "cheatcodes",
2657 return = %match &result {
2658 Ok(b) => hex::encode(b),
2659 Err(e) => e.to_string(),
2660 }
2661 );
2662
2663 result
2664}
2665
2666const fn will_exit(action: &InterpreterAction) -> bool {
2668 match action {
2669 InterpreterAction::Return(result) => {
2670 result.result.is_ok_or_revert() || result.result.is_error()
2671 }
2672 _ => false,
2673 }
2674}