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 IntoNestedEvm, NestedEvm, NestedEvmClosure, SpecFor, TransactionRequestFor, TxEnvFor,
44 with_cloned_context,
45 },
46};
47use foundry_evm_traces::{
48 TracingInspector, TracingInspectorConfig, identifier::SignaturesIdentifier,
49};
50use foundry_wallets::wallet_multi::MultiWallet;
51use itertools::Itertools;
52use proptest::test_runner::{RngAlgorithm, TestRng, TestRunner};
53use rand::Rng;
54use revm::{
55 Inspector,
56 bytecode::opcode as op,
57 context::{Cfg, ContextTr, Host, JournalTr, Transaction, TransactionType, result::EVMError},
58 context_interface::{CreateScheme, transaction::SignedAuthorization},
59 handler::FrameResult,
60 inspector::JournalExt,
61 interpreter::{
62 CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, FrameInput, Gas,
63 InstructionResult, Interpreter, InterpreterAction, InterpreterResult,
64 interpreter_types::{Jumps, LoopControl, MemoryTr},
65 },
66};
67use serde_json::Value;
68use std::{
69 cmp::max,
70 collections::{BTreeMap, VecDeque},
71 fmt::Debug,
72 fs::File,
73 io::BufReader,
74 ops::Range,
75 path::PathBuf,
76 sync::{Arc, OnceLock},
77};
78
79mod utils;
80
81pub mod analysis;
82pub use analysis::CheatcodeAnalysis;
83
84pub trait CheatcodesExecutor<FEN: FoundryEvmNetwork> {
86 fn with_nested_evm(
89 &mut self,
90 cheats: &mut Cheatcodes<FEN>,
91 ecx: &mut FoundryContextFor<'_, FEN>,
92 f: NestedEvmClosure<'_, SpecFor<FEN>, BlockEnvFor<FEN>, TxEnvFor<FEN>>,
93 ) -> Result<(), EVMError<DatabaseError>>;
94
95 fn transact_on_db(
97 &mut self,
98 cheats: &mut Cheatcodes<FEN>,
99 ecx: &mut FoundryContextFor<'_, FEN>,
100 fork_id: Option<U256>,
101 transaction: B256,
102 ) -> eyre::Result<()>;
103
104 fn transact_from_tx_on_db(
106 &mut self,
107 cheats: &mut Cheatcodes<FEN>,
108 ecx: &mut FoundryContextFor<'_, FEN>,
109 tx: TxEnvFor<FEN>,
110 ) -> eyre::Result<()>;
111
112 #[allow(clippy::type_complexity)]
117 fn with_fresh_nested_evm(
118 &mut self,
119 cheats: &mut Cheatcodes<FEN>,
120 db: &mut <FoundryContextFor<'_, FEN> as ContextTr>::Db,
121 evm_env: EvmEnv<SpecFor<FEN>, BlockEnvFor<FEN>>,
122 f: NestedEvmClosure<'_, SpecFor<FEN>, BlockEnvFor<FEN>, TxEnvFor<FEN>>,
123 ) -> Result<EvmEnv<SpecFor<FEN>, BlockEnvFor<FEN>>, EVMError<DatabaseError>>;
124
125 fn console_log(&mut self, msg: &str);
127
128 fn tracing_inspector(&mut self) -> Option<&mut TracingInspector> {
130 None
131 }
132
133 fn set_in_inner_context(&mut self, _enabled: bool, _original_origin: Option<Address>) {}
137}
138
139pub(crate) fn exec_create<FEN: FoundryEvmNetwork>(
141 executor: &mut dyn CheatcodesExecutor<FEN>,
142 inputs: CreateInputs,
143 ccx: &mut CheatsCtxt<'_, '_, FEN>,
144) -> std::result::Result<CreateOutcome, EVMError<DatabaseError>> {
145 let mut inputs = Some(inputs);
146 let mut outcome = None;
147 executor.with_nested_evm(ccx.state, ccx.ecx, &mut |evm| {
148 let inputs = inputs.take().unwrap();
149 evm.journal_inner_mut().depth += 1;
150
151 let frame = FrameInput::Create(Box::new(inputs));
152
153 let result = match evm.run_execution(frame)? {
154 FrameResult::Call(_) => unreachable!(),
155 FrameResult::Create(create) => create,
156 };
157
158 evm.journal_inner_mut().depth -= 1;
159
160 outcome = Some(result);
161 Ok(())
162 })?;
163 Ok(outcome.unwrap())
164}
165
166#[derive(Debug, Default, Clone, Copy)]
169struct TransparentCheatcodesExecutor;
170
171impl<FEN: FoundryEvmNetwork> CheatcodesExecutor<FEN> for TransparentCheatcodesExecutor {
172 fn with_nested_evm(
173 &mut self,
174 cheats: &mut Cheatcodes<FEN>,
175 ecx: &mut FoundryContextFor<'_, FEN>,
176 f: NestedEvmClosure<'_, SpecFor<FEN>, BlockEnvFor<FEN>, TxEnvFor<FEN>>,
177 ) -> Result<(), EVMError<DatabaseError>> {
178 with_cloned_context(ecx, |db, evm_env, journal_inner| {
179 let mut evm = FEN::EvmFactory::default()
180 .create_foundry_evm_with_inspector(db, evm_env, cheats)
181 .into_nested_evm();
182 *evm.journal_inner_mut() = journal_inner;
183 f(&mut evm)?;
184 let sub_inner = evm.journal_inner_mut().clone();
185 let sub_evm_env = evm.to_evm_env();
186 Ok((sub_evm_env, sub_inner))
187 })
188 }
189
190 fn with_fresh_nested_evm(
191 &mut self,
192 cheats: &mut Cheatcodes<FEN>,
193 db: &mut <FoundryContextFor<'_, FEN> as ContextTr>::Db,
194 evm_env: EvmEnv<SpecFor<FEN>, BlockEnvFor<FEN>>,
195 f: NestedEvmClosure<'_, SpecFor<FEN>, BlockEnvFor<FEN>, TxEnvFor<FEN>>,
196 ) -> Result<EvmEnv<SpecFor<FEN>, BlockEnvFor<FEN>>, EVMError<DatabaseError>> {
197 let mut evm = FEN::EvmFactory::default()
198 .create_foundry_evm_with_inspector(db, evm_env, cheats)
199 .into_nested_evm();
200 f(&mut evm)?;
201 Ok(evm.to_evm_env())
202 }
203
204 fn transact_on_db(
205 &mut self,
206 cheats: &mut Cheatcodes<FEN>,
207 ecx: &mut FoundryContextFor<'_, FEN>,
208 fork_id: Option<U256>,
209 transaction: B256,
210 ) -> eyre::Result<()> {
211 let evm_env = ecx.evm_clone();
212 let (db, inner) = ecx.db_journal_inner_mut();
213 db.transact(fork_id, transaction, evm_env, inner, cheats)
214 }
215
216 fn transact_from_tx_on_db(
217 &mut self,
218 cheats: &mut Cheatcodes<FEN>,
219 ecx: &mut FoundryContextFor<'_, FEN>,
220 tx: TxEnvFor<FEN>,
221 ) -> eyre::Result<()> {
222 let evm_env = ecx.evm_clone();
223 let (db, inner) = ecx.db_journal_inner_mut();
224 db.transact_from_tx(tx, evm_env, inner, cheats)
225 }
226
227 fn console_log(&mut self, _msg: &str) {}
228}
229
230macro_rules! try_or_return {
231 ($e:expr) => {
232 match $e {
233 Ok(v) => v,
234 Err(_) => return,
235 }
236 };
237}
238
239#[derive(Debug, Default)]
241pub struct TestContext {
242 pub opened_read_files: HashMap<PathBuf, BufReader<File>>,
244}
245
246impl Clone for TestContext {
248 fn clone(&self) -> Self {
249 Default::default()
250 }
251}
252
253impl TestContext {
254 pub fn clear(&mut self) {
256 self.opened_read_files.clear();
257 }
258}
259
260#[derive(Clone, Debug)]
262pub struct BroadcastableTransaction<N: Network = Ethereum> {
263 pub rpc: Option<String>,
265 pub transaction: TransactionMaybeSigned<N>,
267}
268
269#[derive(Clone, Debug, Copy)]
270pub struct RecordDebugStepInfo {
271 pub start_node_idx: usize,
273 pub original_tracer_config: TracingInspectorConfig,
275}
276
277#[derive(Clone, Debug, Default)]
279pub struct GasMetering {
280 pub paused: bool,
282 pub touched: bool,
285 pub reset: bool,
287 pub paused_frames: Vec<Gas>,
289
290 pub active_gas_snapshot: Option<(String, String)>,
292
293 pub last_call_gas: Option<crate::Vm::Gas>,
296
297 pub recording: bool,
299 pub last_gas_used: u64,
301 pub gas_records: Vec<GasRecord>,
303}
304
305impl GasMetering {
306 pub fn start(&mut self) {
308 self.recording = true;
309 }
310
311 pub fn stop(&mut self) {
313 self.recording = false;
314 }
315
316 pub fn resume(&mut self) {
318 if self.paused {
319 self.paused = false;
320 self.touched = true;
321 }
322 self.paused_frames.clear();
323 }
324
325 pub fn reset(&mut self) {
327 self.paused = false;
328 self.touched = true;
329 self.reset = true;
330 self.paused_frames.clear();
331 }
332}
333
334#[derive(Clone, Debug, Default)]
336pub struct ArbitraryStorage {
337 pub values: HashMap<Address, HashMap<U256, U256>>,
341 pub copies: HashMap<Address, Address>,
343 pub overwrites: HashSet<Address>,
345}
346
347impl ArbitraryStorage {
348 pub fn mark_arbitrary(&mut self, address: &Address, overwrite: bool) {
350 self.values.insert(*address, HashMap::default());
351 if overwrite {
352 self.overwrites.insert(*address);
353 } else {
354 self.overwrites.remove(address);
355 }
356 }
357
358 pub fn mark_copy(&mut self, from: &Address, to: &Address) {
360 if self.values.contains_key(from) {
361 self.copies.insert(*to, *from);
362 }
363 }
364
365 pub fn save<CTX: ContextTr>(
369 &mut self,
370 ecx: &mut CTX,
371 address: Address,
372 slot: U256,
373 data: U256,
374 ) {
375 self.values.get_mut(&address).expect("missing arbitrary address entry").insert(slot, data);
376 if ecx.journal_mut().load_account(address).is_ok() {
377 ecx.journal_mut()
378 .sstore(address, slot, data)
379 .expect("could not set arbitrary storage value");
380 }
381 }
382
383 pub fn copy<CTX: ContextTr>(
389 &mut self,
390 ecx: &mut CTX,
391 target: Address,
392 slot: U256,
393 new_value: U256,
394 ) -> U256 {
395 let source = self.copies.get(&target).expect("missing arbitrary copy target entry");
396 let storage_cache = self.values.get_mut(source).expect("missing arbitrary source storage");
397 let value = match storage_cache.get(&slot) {
398 Some(value) => *value,
399 None => {
400 storage_cache.insert(slot, new_value);
401 if ecx.journal_mut().load_account(*source).is_ok() {
403 ecx.journal_mut()
404 .sstore(*source, slot, new_value)
405 .expect("could not copy arbitrary storage value");
406 }
407 new_value
408 }
409 };
410 if ecx.journal_mut().load_account(target).is_ok() {
412 ecx.journal_mut().sstore(target, slot, value).expect("could not set storage");
413 }
414 value
415 }
416}
417
418pub type BroadcastableTransactions<N> = VecDeque<BroadcastableTransaction<N>>;
420
421#[derive(Clone, Debug)]
439pub struct Cheatcodes<FEN: FoundryEvmNetwork = EthEvmNetwork> {
440 pub analysis: Option<CheatcodeAnalysis>,
442
443 pub block: Option<BlockEnvFor<FEN>>,
448
449 pub active_delegations: Vec<SignedAuthorization>,
453
454 pub active_blob_sidecar: Option<BlobTransactionSidecarVariant>,
456
457 pub gas_price: Option<u128>,
462
463 pub labels: AddressHashMap<String>,
465
466 pub pranks: BTreeMap<usize, Prank>,
468
469 pub expected_revert: Option<ExpectedRevert>,
471
472 pub assume_no_revert: Option<AssumeNoRevert>,
474
475 pub fork_revert_diagnostic: Option<RevertDiagnostic>,
477
478 pub accesses: RecordAccess,
480
481 pub recording_accesses: bool,
483
484 pub recorded_account_diffs_stack: Option<Vec<Vec<AccountAccess>>>,
490
491 pub record_debug_steps_info: Option<RecordDebugStepInfo>,
493
494 pub recorded_logs: Option<Vec<crate::Vm::Log>>,
496
497 pub mocked_calls: HashMap<Address, BTreeMap<MockCallDataContext, VecDeque<MockCallReturnData>>>,
500
501 pub mocked_functions: HashMap<Address, HashMap<Bytes, Address>>,
503
504 pub expected_calls: ExpectedCallTracker,
506 pub expected_emits: ExpectedEmitTracker,
508 pub expected_creates: Vec<ExpectedCreate>,
510
511 pub allowed_mem_writes: HashMap<u64, Vec<Range<u64>>>,
513
514 pub broadcast: Option<Broadcast>,
516
517 pub broadcastable_transactions: BroadcastableTransactions<FEN::Network>,
519
520 pub access_list: Option<AccessList>,
522
523 pub config: Arc<CheatsConfig>,
525
526 pub test_context: TestContext,
528
529 pub fs_commit: bool,
532
533 pub serialized_jsons: BTreeMap<String, BTreeMap<String, Value>>,
536
537 pub eth_deals: Vec<DealRecord>,
539
540 pub gas_metering: GasMetering,
542
543 pub gas_snapshots: BTreeMap<String, BTreeMap<String, String>>,
546
547 pub mapping_slots: Option<AddressHashMap<MappingSlots>>,
549
550 pub pc: usize,
552 pub breakpoints: Breakpoints,
555
556 pub intercept_next_create_call: bool,
558
559 test_runner: Option<TestRunner>,
562
563 pub ignored_traces: IgnoredTraces,
565
566 pub arbitrary_storage: Option<ArbitraryStorage>,
568
569 pub deprecated: HashMap<&'static str, Option<&'static str>>,
571 pub wallets: Option<Wallets>,
573 signatures_identifier: OnceLock<Option<SignaturesIdentifier>>,
575 pub dynamic_gas_limit: bool,
577 pub execution_evm_version: Option<SpecFor<FEN>>,
579}
580
581impl Default for Cheatcodes {
585 fn default() -> Self {
586 Self::new(Arc::default())
587 }
588}
589
590impl<FEN: FoundryEvmNetwork> Cheatcodes<FEN> {
591 pub fn new(config: Arc<CheatsConfig>) -> Self {
593 Self {
594 analysis: None,
595 fs_commit: true,
596 labels: config.labels.clone(),
597 config,
598 block: Default::default(),
599 active_delegations: Default::default(),
600 active_blob_sidecar: Default::default(),
601 gas_price: Default::default(),
602 pranks: Default::default(),
603 expected_revert: Default::default(),
604 assume_no_revert: Default::default(),
605 fork_revert_diagnostic: Default::default(),
606 accesses: Default::default(),
607 recording_accesses: Default::default(),
608 recorded_account_diffs_stack: Default::default(),
609 recorded_logs: Default::default(),
610 record_debug_steps_info: Default::default(),
611 mocked_calls: Default::default(),
612 mocked_functions: Default::default(),
613 expected_calls: Default::default(),
614 expected_emits: Default::default(),
615 expected_creates: Default::default(),
616 allowed_mem_writes: Default::default(),
617 broadcast: Default::default(),
618 broadcastable_transactions: Default::default(),
619 access_list: Default::default(),
620 test_context: Default::default(),
621 serialized_jsons: Default::default(),
622 eth_deals: Default::default(),
623 gas_metering: Default::default(),
624 gas_snapshots: Default::default(),
625 mapping_slots: Default::default(),
626 pc: Default::default(),
627 breakpoints: Default::default(),
628 intercept_next_create_call: Default::default(),
629 test_runner: Default::default(),
630 ignored_traces: Default::default(),
631 arbitrary_storage: Default::default(),
632 deprecated: Default::default(),
633 wallets: Default::default(),
634 signatures_identifier: Default::default(),
635 dynamic_gas_limit: Default::default(),
636 execution_evm_version: None,
637 }
638 }
639
640 pub fn set_analysis(&mut self, analysis: CheatcodeAnalysis) {
642 self.analysis = Some(analysis);
643 }
644
645 pub fn get_prank(&self, depth: usize) -> Option<&Prank> {
649 self.pranks.range(..=depth).last().map(|(_, prank)| prank)
650 }
651
652 pub fn wallets(&mut self) -> &Wallets {
654 self.wallets.get_or_insert_with(|| Wallets::new(MultiWallet::default(), None))
655 }
656
657 pub fn set_wallets(&mut self, wallets: Wallets) {
659 self.wallets = Some(wallets);
660 }
661
662 pub fn add_delegation(&mut self, authorization: SignedAuthorization) {
664 self.active_delegations.push(authorization);
665 }
666
667 pub fn signatures_identifier(&self) -> Option<&SignaturesIdentifier> {
669 self.signatures_identifier.get_or_init(|| SignaturesIdentifier::new(true).ok()).as_ref()
670 }
671
672 fn apply_cheatcode(
674 &mut self,
675 ecx: &mut FoundryContextFor<'_, FEN>,
676 call: &CallInputs,
677 executor: &mut dyn CheatcodesExecutor<FEN>,
678 ) -> Result {
679 let decoded = Vm::VmCalls::abi_decode(&call.input.bytes(ecx)).map_err(|e| {
681 if let alloy_sol_types::Error::UnknownSelector { name: _, selector } = e {
682 let msg = format!(
683 "unknown cheatcode with selector {selector}; \
684 you may have a mismatch between the `Vm` interface (likely in `forge-std`) \
685 and the `forge` version"
686 );
687 return alloy_sol_types::Error::Other(std::borrow::Cow::Owned(msg));
688 }
689 e
690 })?;
691
692 let caller = call.caller;
693
694 ecx.db_mut().ensure_cheatcode_access_forking_mode(&caller)?;
697
698 apply_dispatch(
699 &decoded,
700 &mut CheatsCtxt { state: self, ecx, gas_limit: call.gas_limit, caller },
701 executor,
702 )
703 }
704
705 fn allow_cheatcodes_on_create(
711 &self,
712 ecx: &mut FoundryContextFor<FEN>,
713 caller: Address,
714 created_address: Address,
715 ) {
716 if ecx.journal().depth() <= 1 || ecx.db().has_cheatcode_access(&caller) {
717 ecx.db_mut().allow_cheatcode_access(created_address);
718 }
719 }
720
721 fn apply_accesslist(&mut self, ecx: &mut FoundryContextFor<FEN>) {
727 if let Some(access_list) = &self.access_list {
728 ecx.tx_mut().set_access_list(access_list.clone());
729
730 if ecx.tx().tx_type() == TransactionType::Legacy as u8 {
731 ecx.tx_mut().set_tx_type(TransactionType::Eip2930 as u8);
732 }
733 }
734 }
735
736 pub fn on_revert(&mut self, ecx: &mut FoundryContextFor<FEN>) {
741 trace!(deals=?self.eth_deals.len(), "rolling back deals");
742
743 if self.expected_revert.is_some() {
745 return;
746 }
747
748 if ecx.journal().depth() > 0 {
750 return;
751 }
752
753 while let Some(record) = self.eth_deals.pop() {
757 if let Some(acc) = ecx.journal_mut().evm_state_mut().get_mut(&record.address) {
758 acc.info.balance = record.old_balance;
759 }
760 }
761 }
762
763 pub fn call_with_executor(
764 &mut self,
765 ecx: &mut FoundryContextFor<'_, FEN>,
766 call: &mut CallInputs,
767 executor: &mut dyn CheatcodesExecutor<FEN>,
768 ) -> Option<CallOutcome> {
769 if let Some(spec_id) = self.execution_evm_version {
771 ecx.cfg_mut().set_spec_and_mainnet_gas_params(spec_id);
772 }
773
774 let gas = Gas::new(call.gas_limit);
775 let curr_depth = ecx.journal().depth();
776
777 if curr_depth == 0 {
781 let sender = ecx.tx().caller();
782 let account = match super::evm::journaled_account(ecx, sender) {
783 Ok(account) => account,
784 Err(err) => {
785 return Some(CallOutcome {
786 result: InterpreterResult {
787 result: InstructionResult::Revert,
788 output: err.abi_encode().into(),
789 gas,
790 },
791 memory_offset: call.return_memory_offset.clone(),
792 was_precompile_called: false,
793 precompile_call_logs: vec![],
794 });
795 }
796 };
797 let prev = account.info.nonce;
798 account.info.nonce = prev.saturating_sub(1);
799
800 trace!(target: "cheatcodes", %sender, nonce=account.info.nonce, prev, "corrected nonce");
801 }
802
803 if call.target_address == CHEATCODE_ADDRESS {
804 return match self.apply_cheatcode(ecx, call, executor) {
805 Ok(retdata) => Some(CallOutcome {
806 result: InterpreterResult {
807 result: InstructionResult::Return,
808 output: retdata.into(),
809 gas,
810 },
811 memory_offset: call.return_memory_offset.clone(),
812 was_precompile_called: true,
813 precompile_call_logs: vec![],
814 }),
815 Err(err) => Some(CallOutcome {
816 result: InterpreterResult {
817 result: InstructionResult::Revert,
818 output: err.abi_encode().into(),
819 gas,
820 },
821 memory_offset: call.return_memory_offset.clone(),
822 was_precompile_called: false,
823 precompile_call_logs: vec![],
824 }),
825 };
826 }
827
828 if call.target_address == HARDHAT_CONSOLE_ADDRESS {
829 return None;
830 }
831
832 if let Some(expected) = &mut self.expected_revert {
836 expected.max_depth = max(curr_depth + 1, expected.max_depth);
837 }
838
839 if let Some(expected_calls_for_target) = self.expected_calls.get_mut(&call.bytecode_address)
843 {
844 for (calldata, (expected, actual_count)) in expected_calls_for_target {
846 if calldata.len() <= call.input.len() &&
849 *calldata == call.input.bytes(ecx)[..calldata.len()] &&
851 expected
853 .value.is_none_or(|value| Some(value) == call.transfer_value()) &&
854 expected.gas.is_none_or(|gas| gas == call.gas_limit) &&
856 expected.min_gas.is_none_or(|min_gas| min_gas <= call.gas_limit)
858 {
859 *actual_count += 1;
860 }
861 }
862 }
863
864 if let Some(mocks) = self.mocked_calls.get_mut(&call.bytecode_address) {
866 let ctx = MockCallDataContext {
867 calldata: call.input.bytes(ecx),
868 value: call.transfer_value(),
869 };
870
871 if let Some(return_data_queue) = match mocks.get_mut(&ctx) {
872 Some(queue) => Some(queue),
873 None => mocks
874 .iter_mut()
875 .find(|(mock, _)| {
876 call.input.bytes(ecx).get(..mock.calldata.len()) == Some(&mock.calldata[..])
877 && mock.value.is_none_or(|value| Some(value) == call.transfer_value())
878 })
879 .map(|(_, v)| v),
880 } && let Some(return_data) = if return_data_queue.len() == 1 {
881 return_data_queue.front().map(|x| x.to_owned())
883 } else {
884 return_data_queue.pop_front()
886 } {
887 return Some(CallOutcome {
888 result: InterpreterResult {
889 result: return_data.ret_type,
890 output: return_data.data,
891 gas,
892 },
893 memory_offset: call.return_memory_offset.clone(),
894 was_precompile_called: true,
895 precompile_call_logs: vec![],
896 });
897 }
898 }
899
900 if let Some(prank) = &self.get_prank(curr_depth) {
902 if prank.delegate_call
904 && curr_depth == prank.depth
905 && let CallScheme::DelegateCall = call.scheme
906 {
907 call.target_address = prank.new_caller;
908 call.caller = prank.new_caller;
909 if let Some(new_origin) = prank.new_origin {
910 ecx.tx_mut().set_caller(new_origin);
911 }
912 }
913
914 if curr_depth >= prank.depth && call.caller == prank.prank_caller {
915 let mut prank_applied = false;
916
917 if curr_depth == prank.depth {
919 let _ = journaled_account(ecx, prank.new_caller);
921 call.caller = prank.new_caller;
922 prank_applied = true;
923 }
924
925 if let Some(new_origin) = prank.new_origin {
927 ecx.tx_mut().set_caller(new_origin);
928 prank_applied = true;
929 }
930
931 if prank_applied && let Some(applied_prank) = prank.first_time_applied() {
933 self.pranks.insert(curr_depth, applied_prank);
934 }
935 }
936 }
937
938 self.apply_accesslist(ecx);
940
941 if let Some(broadcast) = &self.broadcast {
943 let is_fixed_gas_limit = call.gas_limit >= 21_000 && !self.dynamic_gas_limit;
946 self.dynamic_gas_limit = false;
947
948 if curr_depth == broadcast.depth && call.caller == broadcast.original_caller {
953 ecx.tx_mut().set_caller(broadcast.new_origin);
957
958 call.caller = broadcast.new_origin;
959 if !call.is_static {
964 if let Err(err) = ecx.journal_mut().load_account(broadcast.new_origin) {
965 return Some(CallOutcome {
966 result: InterpreterResult {
967 result: InstructionResult::Revert,
968 output: Error::encode(err),
969 gas,
970 },
971 memory_offset: call.return_memory_offset.clone(),
972 was_precompile_called: false,
973 precompile_call_logs: vec![],
974 });
975 }
976
977 let input = call.input.bytes(ecx);
978 let chain_id = ecx.cfg().chain_id();
979 let rpc = ecx.db().active_fork_url();
980 let account =
981 ecx.journal_mut().evm_state_mut().get_mut(&broadcast.new_origin).unwrap();
982
983 let mut tx_req = TransactionRequestFor::<FEN>::default()
984 .with_from(broadcast.new_origin)
985 .with_to(call.target_address)
986 .with_value(call.transfer_value().unwrap_or_default())
987 .with_input(input)
988 .with_nonce(account.info.nonce)
989 .with_chain_id(chain_id);
990 if is_fixed_gas_limit {
991 tx_req.set_gas_limit(call.gas_limit)
992 }
993
994 let active_delegations = std::mem::take(&mut self.active_delegations);
995 if let Some(blob_sidecar) = self.active_blob_sidecar.take() {
997 if !active_delegations.is_empty() {
999 let msg = "both delegation and blob are active; `attachBlob` and `attachDelegation` are not compatible";
1000 return Some(CallOutcome {
1001 result: InterpreterResult {
1002 result: InstructionResult::Revert,
1003 output: Error::encode(msg),
1004 gas,
1005 },
1006 memory_offset: call.return_memory_offset.clone(),
1007 was_precompile_called: false,
1008 precompile_call_logs: vec![],
1009 });
1010 }
1011 tx_req.set_blob_sidecar(blob_sidecar);
1012 }
1013
1014 if !active_delegations.is_empty() {
1016 for auth in &active_delegations {
1017 let Ok(authority) = auth.recover_authority() else {
1018 continue;
1019 };
1020 if authority == broadcast.new_origin {
1021 account.info.nonce += 1;
1024 }
1025 }
1026 tx_req.set_authorization_list(active_delegations);
1027 }
1028 if let Some(fee_token) = self.config.fee_token {
1029 tx_req.set_fee_token(fee_token);
1030 }
1031 self.broadcastable_transactions.push_back(BroadcastableTransaction {
1032 rpc,
1033 transaction: TransactionMaybeSigned::new(tx_req),
1034 });
1035 debug!(target: "cheatcodes", tx=?self.broadcastable_transactions.back().unwrap(), "broadcastable call");
1036
1037 if !self.config.evm_opts.isolate {
1039 let prev = account.info.nonce;
1040 account.info.nonce += 1;
1041 debug!(target: "cheatcodes", address=%broadcast.new_origin, nonce=prev+1, prev, "incremented nonce");
1042 }
1043 } else if broadcast.single_call {
1044 let msg = "`staticcall`s are not allowed after `broadcast`; use `startBroadcast` instead";
1045 return Some(CallOutcome {
1046 result: InterpreterResult {
1047 result: InstructionResult::Revert,
1048 output: Error::encode(msg),
1049 gas,
1050 },
1051 memory_offset: call.return_memory_offset.clone(),
1052 was_precompile_called: false,
1053 precompile_call_logs: vec![],
1054 });
1055 }
1056 }
1057 }
1058
1059 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1061 let initialized;
1064 let old_balance;
1065 let old_nonce;
1066
1067 if let Ok(acc) = ecx.journal_mut().load_account(call.target_address) {
1068 initialized = acc.data.info.exists();
1069 old_balance = acc.data.info.balance;
1070 old_nonce = acc.data.info.nonce;
1071 } else {
1072 initialized = false;
1073 old_balance = U256::ZERO;
1074 old_nonce = 0;
1075 }
1076
1077 let kind = match call.scheme {
1078 CallScheme::Call => crate::Vm::AccountAccessKind::Call,
1079 CallScheme::CallCode => crate::Vm::AccountAccessKind::CallCode,
1080 CallScheme::DelegateCall => crate::Vm::AccountAccessKind::DelegateCall,
1081 CallScheme::StaticCall => crate::Vm::AccountAccessKind::StaticCall,
1082 };
1083
1084 recorded_account_diffs_stack.push(vec![AccountAccess {
1090 chainInfo: crate::Vm::ChainInfo {
1091 forkId: ecx.db().active_fork_id().unwrap_or_default(),
1092 chainId: U256::from(ecx.cfg().chain_id()),
1093 },
1094 accessor: call.caller,
1095 account: call.bytecode_address,
1096 kind,
1097 initialized,
1098 oldBalance: old_balance,
1099 newBalance: U256::ZERO, oldNonce: old_nonce,
1101 newNonce: 0, value: call.call_value(),
1103 data: call.input.bytes(ecx),
1104 reverted: false,
1105 deployedCode: Bytes::new(),
1106 storageAccesses: vec![], depth: ecx.journal().depth().try_into().expect("journaled state depth exceeds u64"),
1108 }]);
1109 }
1110
1111 None
1112 }
1113
1114 pub fn rng(&mut self) -> &mut impl Rng {
1115 self.test_runner().rng()
1116 }
1117
1118 pub fn test_runner(&mut self) -> &mut TestRunner {
1119 self.test_runner.get_or_insert_with(|| match self.config.seed {
1120 Some(seed) => TestRunner::new_with_rng(
1121 proptest::test_runner::Config::default(),
1122 TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>()),
1123 ),
1124 None => TestRunner::new(proptest::test_runner::Config::default()),
1125 })
1126 }
1127
1128 pub fn set_seed(&mut self, seed: U256) {
1129 self.test_runner = Some(TestRunner::new_with_rng(
1130 proptest::test_runner::Config::default(),
1131 TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>()),
1132 ));
1133 }
1134
1135 pub fn arbitrary_storage(&mut self) -> &mut ArbitraryStorage {
1138 self.arbitrary_storage.get_or_insert_with(ArbitraryStorage::default)
1139 }
1140
1141 pub fn has_arbitrary_storage(&self, address: &Address) -> bool {
1143 match &self.arbitrary_storage {
1144 Some(storage) => storage.values.contains_key(address),
1145 None => false,
1146 }
1147 }
1148
1149 pub fn should_overwrite_arbitrary_storage(
1153 &self,
1154 address: &Address,
1155 storage_slot: U256,
1156 ) -> bool {
1157 match &self.arbitrary_storage {
1158 Some(storage) => {
1159 storage.overwrites.contains(address)
1160 && storage
1161 .values
1162 .get(address)
1163 .and_then(|arbitrary_values| arbitrary_values.get(&storage_slot))
1164 .is_none()
1165 }
1166 None => false,
1167 }
1168 }
1169
1170 pub fn is_arbitrary_storage_copy(&self, address: &Address) -> bool {
1172 match &self.arbitrary_storage {
1173 Some(storage) => storage.copies.contains_key(address),
1174 None => false,
1175 }
1176 }
1177
1178 pub fn struct_defs(&self) -> Option<&foundry_common::fmt::StructDefinitions> {
1180 self.analysis.as_ref().and_then(|analysis| analysis.struct_defs().ok())
1181 }
1182}
1183
1184impl<FEN: FoundryEvmNetwork> Inspector<FoundryContextFor<'_, FEN>> for Cheatcodes<FEN> {
1185 fn initialize_interp(
1186 &mut self,
1187 interpreter: &mut Interpreter,
1188 ecx: &mut FoundryContextFor<'_, FEN>,
1189 ) {
1190 if let Some(block) = self.block.take() {
1193 ecx.set_block(block);
1194 }
1195 if let Some(gas_price) = self.gas_price.take() {
1196 ecx.tx_mut().set_gas_price(gas_price);
1197 }
1198
1199 if self.gas_metering.paused {
1201 self.gas_metering.paused_frames.push(interpreter.gas);
1202 }
1203
1204 if let Some(expected) = &mut self.expected_revert {
1206 expected.max_depth = max(ecx.journal().depth(), expected.max_depth);
1207 }
1208 }
1209
1210 fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut FoundryContextFor<'_, FEN>) {
1211 self.pc = interpreter.bytecode.pc();
1212
1213 if self.broadcast.is_some() {
1214 self.set_gas_limit_type(interpreter);
1215 }
1216
1217 if self.gas_metering.paused {
1219 self.meter_gas(interpreter);
1220 }
1221
1222 if self.gas_metering.reset {
1224 self.meter_gas_reset(interpreter);
1225 }
1226
1227 if self.recording_accesses {
1229 self.record_accesses(interpreter);
1230 }
1231
1232 if self.recorded_account_diffs_stack.is_some() {
1234 self.record_state_diffs(interpreter, ecx);
1235 }
1236
1237 if !self.allowed_mem_writes.is_empty() {
1239 self.check_mem_opcodes(
1240 interpreter,
1241 ecx.journal().depth().try_into().expect("journaled state depth exceeds u64"),
1242 );
1243 }
1244
1245 if let Some(mapping_slots) = &mut self.mapping_slots {
1247 mapping_step(mapping_slots, interpreter);
1248 }
1249
1250 if self.gas_metering.recording {
1252 self.meter_gas_record(interpreter, ecx);
1253 }
1254 }
1255
1256 fn step_end(&mut self, interpreter: &mut Interpreter, ecx: &mut FoundryContextFor<'_, FEN>) {
1257 if self.gas_metering.paused {
1258 self.meter_gas_end(interpreter);
1259 }
1260
1261 if self.gas_metering.touched {
1262 self.meter_gas_check(interpreter);
1263 }
1264
1265 if self.arbitrary_storage.is_some() {
1267 self.arbitrary_storage_end(interpreter, ecx);
1268 }
1269 }
1270
1271 fn log(&mut self, _ecx: &mut FoundryContextFor<'_, FEN>, log: Log) {
1272 if !self.expected_emits.is_empty()
1273 && let Some(err) = expect::handle_expect_emit(self, &log, None)
1274 {
1275 let _ = sh_err!("{err:?}");
1279 }
1280
1281 record_logs(&mut self.recorded_logs, &log);
1283 }
1284
1285 fn log_full(
1286 &mut self,
1287 interpreter: &mut Interpreter,
1288 _ecx: &mut FoundryContextFor<'_, FEN>,
1289 log: Log,
1290 ) {
1291 if !self.expected_emits.is_empty() {
1292 expect::handle_expect_emit(self, &log, Some(interpreter));
1293 }
1294
1295 record_logs(&mut self.recorded_logs, &log);
1297 }
1298
1299 fn call(
1300 &mut self,
1301 ecx: &mut FoundryContextFor<'_, FEN>,
1302 inputs: &mut CallInputs,
1303 ) -> Option<CallOutcome> {
1304 Self::call_with_executor(self, ecx, inputs, &mut TransparentCheatcodesExecutor)
1305 }
1306
1307 fn call_end(
1308 &mut self,
1309 ecx: &mut FoundryContextFor<'_, FEN>,
1310 call: &CallInputs,
1311 outcome: &mut CallOutcome,
1312 ) {
1313 let cheatcode_call = call.target_address == CHEATCODE_ADDRESS
1314 || call.target_address == HARDHAT_CONSOLE_ADDRESS;
1315
1316 if !cheatcode_call {
1320 let curr_depth = ecx.journal().depth();
1322 if let Some(prank) = &self.get_prank(curr_depth)
1323 && curr_depth == prank.depth
1324 {
1325 ecx.tx_mut().set_caller(prank.prank_origin);
1326
1327 if prank.single_call {
1329 self.pranks.remove(&curr_depth);
1330 }
1331 }
1332
1333 if let Some(broadcast) = &self.broadcast
1335 && curr_depth == broadcast.depth
1336 {
1337 ecx.tx_mut().set_caller(broadcast.original_origin);
1338
1339 if broadcast.single_call {
1341 let _ = self.broadcast.take();
1342 }
1343 }
1344 }
1345
1346 if let Some(assume_no_revert) = &mut self.assume_no_revert {
1348 if outcome.result.is_revert() && assume_no_revert.reverted_by.is_none() {
1351 assume_no_revert.reverted_by = Some(call.target_address);
1352 }
1353
1354 let curr_depth = ecx.journal().depth();
1356 if curr_depth <= assume_no_revert.depth && !cheatcode_call {
1357 if outcome.result.is_revert() {
1360 let assume_no_revert = std::mem::take(&mut self.assume_no_revert).unwrap();
1361 return match revert_handlers::handle_assume_no_revert(
1362 &assume_no_revert,
1363 outcome.result.result,
1364 &outcome.result.output,
1365 &self.config.available_artifacts,
1366 ) {
1367 Ok(_) => {
1370 outcome.result.output = Error::from(MAGIC_ASSUME).abi_encode().into();
1371 }
1372 Err(error) => {
1375 trace!(expected=?assume_no_revert, ?error, status=?outcome.result.result, "Expected revert mismatch");
1376 outcome.result.result = InstructionResult::Revert;
1377 outcome.result.output = error.abi_encode().into();
1378 }
1379 };
1380 }
1381 self.assume_no_revert = None;
1383 }
1384 }
1385
1386 if let Some(expected_revert) = &mut self.expected_revert {
1388 if outcome.result.is_revert() {
1391 if expected_revert.reverter.is_some()
1395 && (expected_revert.reverted_by.is_none() || expected_revert.count > 1)
1396 {
1397 expected_revert.reverted_by = Some(call.target_address);
1398 }
1399 }
1400
1401 let curr_depth = ecx.journal().depth();
1402 if curr_depth <= expected_revert.depth {
1403 let needs_processing = match expected_revert.kind {
1404 ExpectedRevertKind::Default => !cheatcode_call,
1405 ExpectedRevertKind::Cheatcode { pending_processing } => {
1408 cheatcode_call && !pending_processing
1409 }
1410 };
1411
1412 if needs_processing {
1413 let mut expected_revert = std::mem::take(&mut self.expected_revert).unwrap();
1414 return match revert_handlers::handle_expect_revert(
1415 cheatcode_call,
1416 false,
1417 self.config.internal_expect_revert,
1418 &expected_revert,
1419 outcome.result.result,
1420 outcome.result.output.clone(),
1421 &self.config.available_artifacts,
1422 ) {
1423 Err(error) => {
1424 trace!(expected=?expected_revert, ?error, status=?outcome.result.result, "Expected revert mismatch");
1425 outcome.result.result = InstructionResult::Revert;
1426 outcome.result.output = error.abi_encode().into();
1427 }
1428 Ok((_, retdata)) => {
1429 expected_revert.actual_count += 1;
1430 if expected_revert.actual_count < expected_revert.count {
1431 self.expected_revert = Some(expected_revert);
1432 }
1433 outcome.result.result = InstructionResult::Return;
1434 outcome.result.output = retdata;
1435 }
1436 };
1437 }
1438
1439 if let ExpectedRevertKind::Cheatcode { pending_processing } =
1442 &mut self.expected_revert.as_mut().unwrap().kind
1443 {
1444 *pending_processing = false;
1445 }
1446 }
1447 }
1448
1449 if cheatcode_call {
1452 return;
1453 }
1454
1455 let gas = outcome.result.gas;
1458 self.gas_metering.last_call_gas = Some(crate::Vm::Gas {
1459 gasLimit: gas.limit(),
1460 gasTotalUsed: gas.spent(),
1461 gasMemoryUsed: 0,
1462 gasRefunded: gas.refunded(),
1463 gasRemaining: gas.remaining(),
1464 });
1465
1466 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1469 if ecx.journal().depth() > 0
1471 && let Some(mut last_recorded_depth) = recorded_account_diffs_stack.pop()
1472 {
1473 if outcome.result.is_revert() {
1476 last_recorded_depth.iter_mut().for_each(|element| {
1477 element.reverted = true;
1478 element
1479 .storageAccesses
1480 .iter_mut()
1481 .for_each(|storage_access| storage_access.reverted = true);
1482 })
1483 }
1484
1485 if let Some(call_access) = last_recorded_depth.first_mut() {
1486 let curr_depth = ecx.journal().depth();
1491 if call_access.depth == curr_depth as u64
1492 && let Ok(acc) = ecx.journal_mut().load_account(call.target_address)
1493 {
1494 debug_assert!(access_is_call(call_access.kind));
1495 call_access.newBalance = acc.data.info.balance;
1496 call_access.newNonce = acc.data.info.nonce;
1497 }
1498 if let Some(last) = recorded_account_diffs_stack.last_mut() {
1503 last.extend(last_recorded_depth);
1504 } else {
1505 recorded_account_diffs_stack.push(last_recorded_depth);
1506 }
1507 }
1508 }
1509 }
1510
1511 let should_check_emits = self
1523 .expected_emits
1524 .iter()
1525 .any(|(expected, _)| {
1526 let curr_depth = ecx.journal().depth();
1527 expected.depth == curr_depth
1528 }) &&
1529 !call.is_static;
1531 if should_check_emits {
1532 let expected_counts = self
1533 .expected_emits
1534 .iter()
1535 .filter_map(|(expected, count_map)| {
1536 let count = match expected.address {
1537 Some(emitter) => match count_map.get(&emitter) {
1538 Some(log_count) => expected
1539 .log
1540 .as_ref()
1541 .map(|l| log_count.count(l))
1542 .unwrap_or_else(|| log_count.count_unchecked()),
1543 None => 0,
1544 },
1545 None => match &expected.log {
1546 Some(log) => count_map.values().map(|logs| logs.count(log)).sum(),
1547 None => count_map.values().map(|logs| logs.count_unchecked()).sum(),
1548 },
1549 };
1550
1551 (count != expected.count).then_some((expected, count))
1552 })
1553 .collect::<Vec<_>>();
1554
1555 if let Some((expected, _)) = self
1557 .expected_emits
1558 .iter()
1559 .find(|(expected, _)| !expected.found && expected.count > 0)
1560 {
1561 outcome.result.result = InstructionResult::Revert;
1562 let error_msg = expected.mismatch_error.as_deref().unwrap_or("log != expected log");
1563 outcome.result.output = error_msg.abi_encode().into();
1564 return;
1565 }
1566
1567 if !expected_counts.is_empty() {
1568 let msg = if outcome.result.is_ok() {
1569 let (expected, count) = expected_counts.first().unwrap();
1570 format!("log emitted {count} times, expected {}", expected.count)
1571 } else {
1572 "expected an emit, but the call reverted instead. \
1573 ensure you're testing the happy path when using `expectEmit`"
1574 .to_string()
1575 };
1576
1577 outcome.result.result = InstructionResult::Revert;
1578 outcome.result.output = Error::encode(msg);
1579 return;
1580 }
1581
1582 self.expected_emits.clear()
1586 }
1587
1588 let diag = self.fork_revert_diagnostic.take();
1591
1592 if outcome.result.is_revert()
1595 && let Some(err) = diag
1596 {
1597 outcome.result.output = Error::encode(err.to_error_msg(&self.labels));
1598 return;
1599 }
1600
1601 if let TxKind::Call(test_contract) = ecx.tx().kind() {
1604 if ecx.db().is_forked_mode()
1607 && outcome.result.result == InstructionResult::Stop
1608 && call.target_address != test_contract
1609 {
1610 self.fork_revert_diagnostic =
1611 ecx.db().diagnose_revert(call.target_address, ecx.journal().evm_state());
1612 }
1613 }
1614
1615 if ecx.journal().depth() == 0 {
1617 if outcome.result.is_revert() {
1621 return;
1622 }
1623
1624 for (address, calldatas) in &self.expected_calls {
1629 for (calldata, (expected, actual_count)) in calldatas {
1631 let ExpectedCallData { gas, min_gas, value, count, call_type } = expected;
1633
1634 let failed = match call_type {
1635 ExpectedCallType::Count => *count != *actual_count,
1639 ExpectedCallType::NonCount => *count > *actual_count,
1644 };
1645 if failed {
1646 let expected_values = [
1647 Some(format!("data {}", hex::encode_prefixed(calldata))),
1648 value.as_ref().map(|v| format!("value {v}")),
1649 gas.map(|g| format!("gas {g}")),
1650 min_gas.map(|g| format!("minimum gas {g}")),
1651 ]
1652 .into_iter()
1653 .flatten()
1654 .join(", ");
1655 let but = if outcome.result.is_ok() {
1656 let s = if *actual_count == 1 { "" } else { "s" };
1657 format!("was called {actual_count} time{s}")
1658 } else {
1659 "the call reverted instead; \
1660 ensure you're testing the happy path when using `expectCall`"
1661 .to_string()
1662 };
1663 let s = if *count == 1 { "" } else { "s" };
1664 let msg = format!(
1665 "expected call to {address} with {expected_values} \
1666 to be called {count} time{s}, but {but}"
1667 );
1668 outcome.result.result = InstructionResult::Revert;
1669 outcome.result.output = Error::encode(msg);
1670
1671 return;
1672 }
1673 }
1674 }
1675
1676 for (expected, _) in &mut self.expected_emits {
1680 if expected.count == 0 && !expected.found {
1681 expected.found = true;
1682 }
1683 }
1684 self.expected_emits.retain(|(expected, _)| !expected.found);
1685 if !self.expected_emits.is_empty() {
1687 let msg = if outcome.result.is_ok() {
1688 "expected an emit, but no logs were emitted afterwards. \
1689 you might have mismatched events or not enough events were emitted"
1690 } else {
1691 "expected an emit, but the call reverted instead. \
1692 ensure you're testing the happy path when using `expectEmit`"
1693 };
1694 outcome.result.result = InstructionResult::Revert;
1695 outcome.result.output = Error::encode(msg);
1696 return;
1697 }
1698
1699 if let Some(expected_create) = self.expected_creates.first() {
1701 let msg = format!(
1702 "expected {} call by address {} for bytecode {} but not found",
1703 expected_create.create_scheme,
1704 hex::encode_prefixed(expected_create.deployer),
1705 hex::encode_prefixed(&expected_create.bytecode),
1706 );
1707 outcome.result.result = InstructionResult::Revert;
1708 outcome.result.output = Error::encode(msg);
1709 }
1710 }
1711 }
1712
1713 fn create(
1714 &mut self,
1715 ecx: &mut FoundryContextFor<'_, FEN>,
1716 mut input: &mut CreateInputs,
1717 ) -> Option<CreateOutcome> {
1718 if let Some(spec_id) = self.execution_evm_version {
1720 ecx.cfg_mut().set_spec_and_mainnet_gas_params(spec_id);
1721 }
1722
1723 let gas = Gas::new(input.gas_limit());
1724 if self.intercept_next_create_call {
1726 self.intercept_next_create_call = false;
1728
1729 let output = input.init_code();
1731
1732 return Some(CreateOutcome {
1734 result: InterpreterResult { result: InstructionResult::Revert, output, gas },
1735 address: None,
1736 });
1737 }
1738
1739 let curr_depth = ecx.journal().depth();
1740
1741 if let Some(prank) = &self.get_prank(curr_depth)
1743 && curr_depth >= prank.depth
1744 && input.caller() == prank.prank_caller
1745 {
1746 let mut prank_applied = false;
1747
1748 if curr_depth == prank.depth {
1750 let _ = journaled_account(ecx, prank.new_caller);
1752 input.set_caller(prank.new_caller);
1753 prank_applied = true;
1754 }
1755
1756 if let Some(new_origin) = prank.new_origin {
1758 ecx.tx_mut().set_caller(new_origin);
1759 prank_applied = true;
1760 }
1761
1762 if prank_applied && let Some(applied_prank) = prank.first_time_applied() {
1764 self.pranks.insert(curr_depth, applied_prank);
1765 }
1766 }
1767
1768 self.apply_accesslist(ecx);
1770
1771 if let Some(broadcast) = &mut self.broadcast
1773 && curr_depth >= broadcast.depth
1774 && input.caller() == broadcast.original_caller
1775 {
1776 if let Err(err) = ecx.journal_mut().load_account(broadcast.new_origin) {
1777 return Some(CreateOutcome {
1778 result: InterpreterResult {
1779 result: InstructionResult::Revert,
1780 output: Error::encode(err),
1781 gas,
1782 },
1783 address: None,
1784 });
1785 }
1786
1787 ecx.tx_mut().set_caller(broadcast.new_origin);
1788
1789 if curr_depth == broadcast.depth || broadcast.deploy_from_code {
1790 broadcast.deploy_from_code = false;
1792
1793 input.set_caller(broadcast.new_origin);
1794
1795 let rpc = ecx.db().active_fork_url();
1796 let account = &ecx.journal().evm_state()[&broadcast.new_origin];
1797 let mut tx_req = TransactionRequestFor::<FEN>::default()
1798 .with_from(broadcast.new_origin)
1799 .with_kind(TxKind::Create)
1800 .with_value(input.value())
1801 .with_input(input.init_code())
1802 .with_nonce(account.info.nonce);
1803 if let Some(fee_token) = self.config.fee_token {
1804 tx_req.set_fee_token(fee_token);
1805 }
1806 self.broadcastable_transactions.push_back(BroadcastableTransaction {
1807 rpc,
1808 transaction: TransactionMaybeSigned::new(tx_req),
1809 });
1810
1811 input.log_debug(self, &input.scheme().unwrap_or(CreateScheme::Create));
1812 }
1813 }
1814
1815 let address = input.allow_cheatcodes(self, ecx);
1817
1818 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1820 recorded_account_diffs_stack.push(vec![AccountAccess {
1821 chainInfo: crate::Vm::ChainInfo {
1822 forkId: ecx.db().active_fork_id().unwrap_or_default(),
1823 chainId: U256::from(ecx.cfg().chain_id()),
1824 },
1825 accessor: input.caller(),
1826 account: address,
1827 kind: crate::Vm::AccountAccessKind::Create,
1828 initialized: true,
1829 oldBalance: U256::ZERO, newBalance: U256::ZERO, oldNonce: 0, newNonce: 1, value: input.value(),
1834 data: input.init_code(),
1835 reverted: false,
1836 deployedCode: Bytes::new(), storageAccesses: vec![], depth: curr_depth as u64,
1839 }]);
1840 }
1841
1842 None
1843 }
1844
1845 fn create_end(
1846 &mut self,
1847 ecx: &mut FoundryContextFor<'_, FEN>,
1848 call: &CreateInputs,
1849 outcome: &mut CreateOutcome,
1850 ) {
1851 let call = Some(call);
1852 let curr_depth = ecx.journal().depth();
1853
1854 if let Some(prank) = &self.get_prank(curr_depth)
1856 && curr_depth == prank.depth
1857 {
1858 ecx.tx_mut().set_caller(prank.prank_origin);
1859
1860 if prank.single_call {
1862 std::mem::take(&mut self.pranks);
1863 }
1864 }
1865
1866 if let Some(broadcast) = &self.broadcast
1868 && curr_depth == broadcast.depth
1869 {
1870 ecx.tx_mut().set_caller(broadcast.original_origin);
1871
1872 if broadcast.single_call {
1874 std::mem::take(&mut self.broadcast);
1875 }
1876 }
1877
1878 if let Some(expected_revert) = &self.expected_revert
1880 && curr_depth <= expected_revert.depth
1881 && matches!(expected_revert.kind, ExpectedRevertKind::Default)
1882 {
1883 let mut expected_revert = std::mem::take(&mut self.expected_revert).unwrap();
1884 return match revert_handlers::handle_expect_revert(
1885 false,
1886 true,
1887 self.config.internal_expect_revert,
1888 &expected_revert,
1889 outcome.result.result,
1890 outcome.result.output.clone(),
1891 &self.config.available_artifacts,
1892 ) {
1893 Ok((address, retdata)) => {
1894 expected_revert.actual_count += 1;
1895 if expected_revert.actual_count < expected_revert.count {
1896 self.expected_revert = Some(expected_revert.clone());
1897 }
1898
1899 outcome.result.result = InstructionResult::Return;
1900 outcome.result.output = retdata;
1901 outcome.address = address;
1902 }
1903 Err(err) => {
1904 outcome.result.result = InstructionResult::Revert;
1905 outcome.result.output = err.abi_encode().into();
1906 }
1907 };
1908 }
1909
1910 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1913 if curr_depth > 0
1915 && let Some(last_depth) = &mut recorded_account_diffs_stack.pop()
1916 {
1917 if outcome.result.is_revert() {
1920 last_depth.iter_mut().for_each(|element| {
1921 element.reverted = true;
1922 element
1923 .storageAccesses
1924 .iter_mut()
1925 .for_each(|storage_access| storage_access.reverted = true);
1926 })
1927 }
1928
1929 if let Some(create_access) = last_depth.first_mut() {
1930 let depth = ecx.journal().depth();
1935 if create_access.depth == depth as u64 {
1936 debug_assert_eq!(
1937 create_access.kind as u8,
1938 crate::Vm::AccountAccessKind::Create as u8
1939 );
1940 if let Some(address) = outcome.address
1941 && let Ok(created_acc) = ecx.journal_mut().load_account(address)
1942 {
1943 create_access.newBalance = created_acc.data.info.balance;
1944 create_access.newNonce = created_acc.data.info.nonce;
1945 create_access.deployedCode = created_acc
1946 .data
1947 .info
1948 .code
1949 .clone()
1950 .unwrap_or_default()
1951 .original_bytes();
1952 }
1953 }
1954 if let Some(last) = recorded_account_diffs_stack.last_mut() {
1959 last.append(last_depth);
1960 } else {
1961 recorded_account_diffs_stack.push(last_depth.clone());
1962 }
1963 }
1964 }
1965 }
1966
1967 if !self.expected_creates.is_empty()
1969 && let (Some(address), Some(call)) = (outcome.address, call)
1970 && let Ok(created_acc) = ecx.journal_mut().load_account(address)
1971 {
1972 let bytecode = created_acc.data.info.code.clone().unwrap_or_default().original_bytes();
1973 if let Some((index, _)) =
1974 self.expected_creates.iter().find_position(|expected_create| {
1975 expected_create.deployer == call.caller()
1976 && expected_create.create_scheme.eq(call.scheme().into())
1977 && expected_create.bytecode == bytecode
1978 })
1979 {
1980 self.expected_creates.swap_remove(index);
1981 }
1982 }
1983 }
1984}
1985
1986impl<FEN: FoundryEvmNetwork> InspectorExt for Cheatcodes<FEN> {
1987 fn should_use_create2_factory(&mut self, depth: usize, inputs: &CreateInputs) -> bool {
1988 if let CreateScheme::Create2 { .. } = inputs.scheme() {
1989 let target_depth = if let Some(prank) = &self.get_prank(depth) {
1990 prank.depth
1991 } else if let Some(broadcast) = &self.broadcast {
1992 broadcast.depth
1993 } else {
1994 1
1995 };
1996
1997 depth == target_depth
1998 && (self.broadcast.is_some() || self.config.always_use_create_2_factory)
1999 } else {
2000 false
2001 }
2002 }
2003
2004 fn create2_deployer(&self) -> Address {
2005 self.config.evm_opts.create2_deployer
2006 }
2007}
2008
2009impl<FEN: FoundryEvmNetwork> Cheatcodes<FEN> {
2010 #[cold]
2011 fn meter_gas(&mut self, interpreter: &mut Interpreter) {
2012 if let Some(paused_gas) = self.gas_metering.paused_frames.last() {
2013 let memory = *interpreter.gas.memory();
2016 interpreter.gas = *paused_gas;
2017 interpreter.gas.memory_mut().words_num = memory.words_num;
2018 interpreter.gas.memory_mut().expansion_cost = memory.expansion_cost;
2019 } else {
2020 self.gas_metering.paused_frames.push(interpreter.gas);
2022 }
2023 }
2024
2025 #[cold]
2026 fn meter_gas_record(
2027 &mut self,
2028 interpreter: &mut Interpreter,
2029 ecx: &mut FoundryContextFor<'_, FEN>,
2030 ) {
2031 if interpreter.bytecode.action.as_ref().and_then(|i| i.instruction_result()).is_none() {
2032 self.gas_metering.gas_records.iter_mut().for_each(|record| {
2033 let curr_depth = ecx.journal().depth();
2034 if curr_depth == record.depth {
2035 if self.gas_metering.last_gas_used != 0 {
2038 let gas_diff =
2039 interpreter.gas.spent().saturating_sub(self.gas_metering.last_gas_used);
2040 record.gas_used = record.gas_used.saturating_add(gas_diff);
2041 }
2042
2043 self.gas_metering.last_gas_used = interpreter.gas.spent();
2046 }
2047 });
2048 }
2049 }
2050
2051 #[cold]
2052 fn meter_gas_end(&mut self, interpreter: &mut Interpreter) {
2053 if let Some(interpreter_action) = interpreter.bytecode.action.as_ref()
2055 && will_exit(interpreter_action)
2056 {
2057 self.gas_metering.paused_frames.pop();
2058 }
2059 }
2060
2061 #[cold]
2062 fn meter_gas_reset(&mut self, interpreter: &mut Interpreter) {
2063 let mut gas = Gas::new(interpreter.gas.limit());
2064 gas.memory_mut().words_num = interpreter.gas.memory().words_num;
2065 gas.memory_mut().expansion_cost = interpreter.gas.memory().expansion_cost;
2066 interpreter.gas = gas;
2067 self.gas_metering.reset = false;
2068 }
2069
2070 #[cold]
2071 fn meter_gas_check(&mut self, interpreter: &mut Interpreter) {
2072 if let Some(interpreter_action) = interpreter.bytecode.action.as_ref()
2073 && will_exit(interpreter_action)
2074 {
2075 if interpreter.gas.spent()
2079 < u64::try_from(interpreter.gas.refunded()).unwrap_or_default()
2080 {
2081 interpreter.gas = Gas::new(interpreter.gas.limit());
2082 }
2083 }
2084 }
2085
2086 #[cold]
2094 fn arbitrary_storage_end(
2095 &mut self,
2096 interpreter: &mut Interpreter,
2097 ecx: &mut FoundryContextFor<'_, FEN>,
2098 ) {
2099 let (key, target_address) = if interpreter.bytecode.opcode() == op::SLOAD {
2100 (try_or_return!(interpreter.stack.peek(0)), interpreter.input.target_address)
2101 } else {
2102 return;
2103 };
2104
2105 let Some(value) = ecx.sload(target_address, key) else {
2106 return;
2107 };
2108
2109 if (value.is_cold && value.data.is_zero())
2110 || self.should_overwrite_arbitrary_storage(&target_address, key)
2111 {
2112 if self.has_arbitrary_storage(&target_address) {
2113 let arbitrary_value = self.rng().random();
2114 self.arbitrary_storage.as_mut().unwrap().save(
2115 ecx,
2116 target_address,
2117 key,
2118 arbitrary_value,
2119 );
2120 } else if self.is_arbitrary_storage_copy(&target_address) {
2121 let arbitrary_value = self.rng().random();
2122 self.arbitrary_storage.as_mut().unwrap().copy(
2123 ecx,
2124 target_address,
2125 key,
2126 arbitrary_value,
2127 );
2128 }
2129 }
2130 }
2131
2132 #[cold]
2134 fn record_accesses(&mut self, interpreter: &mut Interpreter) {
2135 let access = &mut self.accesses;
2136 match interpreter.bytecode.opcode() {
2137 op::SLOAD => {
2138 let key = try_or_return!(interpreter.stack.peek(0));
2139 access.record_read(interpreter.input.target_address, key);
2140 }
2141 op::SSTORE => {
2142 let key = try_or_return!(interpreter.stack.peek(0));
2143 access.record_write(interpreter.input.target_address, key);
2144 }
2145 _ => {}
2146 }
2147 }
2148
2149 #[cold]
2150 fn record_state_diffs(
2151 &mut self,
2152 interpreter: &mut Interpreter,
2153 ecx: &mut FoundryContextFor<'_, FEN>,
2154 ) {
2155 let Some(account_accesses) = &mut self.recorded_account_diffs_stack else { return };
2156 match interpreter.bytecode.opcode() {
2157 op::SELFDESTRUCT => {
2158 let Some(last) = account_accesses.last_mut() else { return };
2160
2161 let target = try_or_return!(interpreter.stack.peek(0));
2163 let target = Address::from_word(B256::from(target));
2164 let (initialized, old_balance, old_nonce) = ecx
2165 .journal_mut()
2166 .load_account(target)
2167 .map(|account| {
2168 (
2169 account.data.info.exists(),
2170 account.data.info.balance,
2171 account.data.info.nonce,
2172 )
2173 })
2174 .unwrap_or_default();
2175
2176 let value = ecx
2178 .balance(interpreter.input.target_address)
2179 .map(|b| b.data)
2180 .unwrap_or(U256::ZERO);
2181
2182 last.push(crate::Vm::AccountAccess {
2184 chainInfo: crate::Vm::ChainInfo {
2185 forkId: ecx.db().active_fork_id().unwrap_or_default(),
2186 chainId: U256::from(ecx.cfg().chain_id()),
2187 },
2188 accessor: interpreter.input.target_address,
2189 account: target,
2190 kind: crate::Vm::AccountAccessKind::SelfDestruct,
2191 initialized,
2192 oldBalance: old_balance,
2193 newBalance: old_balance + value,
2194 oldNonce: old_nonce,
2195 newNonce: old_nonce, value,
2197 data: Bytes::new(),
2198 reverted: false,
2199 deployedCode: Bytes::new(),
2200 storageAccesses: vec![],
2201 depth: ecx
2202 .journal()
2203 .depth()
2204 .try_into()
2205 .expect("journaled state depth exceeds u64"),
2206 });
2207 }
2208
2209 op::SLOAD => {
2210 let Some(last) = account_accesses.last_mut() else { return };
2211
2212 let key = try_or_return!(interpreter.stack.peek(0));
2213 let address = interpreter.input.target_address;
2214
2215 let mut present_value = U256::ZERO;
2218 if ecx.journal_mut().load_account(address).is_ok()
2220 && let Some(previous) = ecx.sload(address, key)
2221 {
2222 present_value = previous.data;
2223 }
2224 let access = crate::Vm::StorageAccess {
2225 account: interpreter.input.target_address,
2226 slot: key.into(),
2227 isWrite: false,
2228 previousValue: present_value.into(),
2229 newValue: present_value.into(),
2230 reverted: false,
2231 };
2232 let curr_depth =
2233 ecx.journal().depth().try_into().expect("journaled state depth exceeds u64");
2234 append_storage_access(last, access, curr_depth);
2235 }
2236 op::SSTORE => {
2237 let Some(last) = account_accesses.last_mut() else { return };
2238
2239 let key = try_or_return!(interpreter.stack.peek(0));
2240 let value = try_or_return!(interpreter.stack.peek(1));
2241 let address = interpreter.input.target_address;
2242 let mut previous_value = U256::ZERO;
2245 if ecx.journal_mut().load_account(address).is_ok()
2246 && let Some(previous) = ecx.sload(address, key)
2247 {
2248 previous_value = previous.data;
2249 }
2250
2251 let access = crate::Vm::StorageAccess {
2252 account: address,
2253 slot: key.into(),
2254 isWrite: true,
2255 previousValue: previous_value.into(),
2256 newValue: value.into(),
2257 reverted: false,
2258 };
2259 let curr_depth =
2260 ecx.journal().depth().try_into().expect("journaled state depth exceeds u64");
2261 append_storage_access(last, access, curr_depth);
2262 }
2263
2264 op::EXTCODECOPY | op::EXTCODESIZE | op::EXTCODEHASH | op::BALANCE => {
2266 let kind = match interpreter.bytecode.opcode() {
2267 op::EXTCODECOPY => crate::Vm::AccountAccessKind::Extcodecopy,
2268 op::EXTCODESIZE => crate::Vm::AccountAccessKind::Extcodesize,
2269 op::EXTCODEHASH => crate::Vm::AccountAccessKind::Extcodehash,
2270 op::BALANCE => crate::Vm::AccountAccessKind::Balance,
2271 _ => unreachable!(),
2272 };
2273 let address =
2274 Address::from_word(B256::from(try_or_return!(interpreter.stack.peek(0))));
2275 let initialized;
2276 let balance;
2277 let nonce;
2278 if let Ok(acc) = ecx.journal_mut().load_account(address) {
2279 initialized = acc.data.info.exists();
2280 balance = acc.data.info.balance;
2281 nonce = acc.data.info.nonce;
2282 } else {
2283 initialized = false;
2284 balance = U256::ZERO;
2285 nonce = 0;
2286 }
2287 let curr_depth =
2288 ecx.journal().depth().try_into().expect("journaled state depth exceeds u64");
2289 let account_access = crate::Vm::AccountAccess {
2290 chainInfo: crate::Vm::ChainInfo {
2291 forkId: ecx.db().active_fork_id().unwrap_or_default(),
2292 chainId: U256::from(ecx.cfg().chain_id()),
2293 },
2294 accessor: interpreter.input.target_address,
2295 account: address,
2296 kind,
2297 initialized,
2298 oldBalance: balance,
2299 newBalance: balance,
2300 oldNonce: nonce,
2301 newNonce: nonce, value: U256::ZERO,
2303 data: Bytes::new(),
2304 reverted: false,
2305 deployedCode: Bytes::new(),
2306 storageAccesses: vec![],
2307 depth: curr_depth,
2308 };
2309 if let Some(last) = account_accesses.last_mut() {
2312 last.push(account_access);
2313 } else {
2314 account_accesses.push(vec![account_access]);
2315 }
2316 }
2317 _ => {}
2318 }
2319 }
2320
2321 #[cold]
2326 fn check_mem_opcodes(&self, interpreter: &mut Interpreter, depth: u64) {
2327 let Some(ranges) = self.allowed_mem_writes.get(&depth) else {
2328 return;
2329 };
2330
2331 macro_rules! mem_opcode_match {
2340 ($(($opcode:ident, $offset_depth:expr, $size_depth:expr, $writes:expr)),* $(,)?) => {
2341 match interpreter.bytecode.opcode() {
2342 op::MSTORE => {
2347 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2349
2350 if !ranges.iter().any(|range| {
2353 range.contains(&offset) && range.contains(&(offset + 31))
2354 }) {
2355 let value = try_or_return!(interpreter.stack.peek(1)).to_be_bytes::<32>();
2360 if value[..SELECTOR_LEN] == stopExpectSafeMemoryCall::SELECTOR {
2361 return
2362 }
2363
2364 disallowed_mem_write(offset, 32, interpreter, ranges);
2365 return
2366 }
2367 }
2368 op::MSTORE8 => {
2369 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2371
2372 if !ranges.iter().any(|range| range.contains(&offset)) {
2375 disallowed_mem_write(offset, 1, interpreter, ranges);
2376 return
2377 }
2378 }
2379
2380 op::MLOAD => {
2385 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2387
2388 if offset >= interpreter.memory.size() as u64 && !ranges.iter().any(|range| {
2392 range.contains(&offset) && range.contains(&(offset + 31))
2393 }) {
2394 disallowed_mem_write(offset, 32, interpreter, ranges);
2395 return
2396 }
2397 }
2398
2399 op::CALL => {
2404 let dest_offset = try_or_return!(interpreter.stack.peek(5)).saturating_to::<u64>();
2406
2407 let size = try_or_return!(interpreter.stack.peek(6)).saturating_to::<u64>();
2409
2410 let fail_cond = !ranges.iter().any(|range| {
2414 range.contains(&dest_offset) &&
2415 range.contains(&(dest_offset + size.saturating_sub(1)))
2416 });
2417
2418 if fail_cond {
2421 let to = Address::from_word(try_or_return!(interpreter.stack.peek(1)).to_be_bytes::<32>().into());
2425 if to == CHEATCODE_ADDRESS {
2426 let args_offset = try_or_return!(interpreter.stack.peek(3)).saturating_to::<usize>();
2427 let args_size = try_or_return!(interpreter.stack.peek(4)).saturating_to::<usize>();
2428 let memory_word = interpreter.memory.slice_len(args_offset, args_size);
2429 if memory_word[..SELECTOR_LEN] == stopExpectSafeMemoryCall::SELECTOR {
2430 return
2431 }
2432 }
2433
2434 disallowed_mem_write(dest_offset, size, interpreter, ranges);
2435 return
2436 }
2437 }
2438
2439 $(op::$opcode => {
2440 let dest_offset = try_or_return!(interpreter.stack.peek($offset_depth)).saturating_to::<u64>();
2442
2443 let size = try_or_return!(interpreter.stack.peek($size_depth)).saturating_to::<u64>();
2445
2446 let fail_cond = !ranges.iter().any(|range| {
2450 range.contains(&dest_offset) &&
2451 range.contains(&(dest_offset + size.saturating_sub(1)))
2452 }) && ($writes ||
2453 [dest_offset, (dest_offset + size).saturating_sub(1)].into_iter().any(|offset| {
2454 offset >= interpreter.memory.size() as u64
2455 })
2456 );
2457
2458 if fail_cond {
2461 disallowed_mem_write(dest_offset, size, interpreter, ranges);
2462 return
2463 }
2464 })*
2465
2466 _ => {}
2467 }
2468 }
2469 }
2470
2471 mem_opcode_match!(
2474 (CALLDATACOPY, 0, 2, true),
2475 (CODECOPY, 0, 2, true),
2476 (RETURNDATACOPY, 0, 2, true),
2477 (EXTCODECOPY, 1, 3, true),
2478 (CALLCODE, 5, 6, true),
2479 (STATICCALL, 4, 5, true),
2480 (DELEGATECALL, 4, 5, true),
2481 (KECCAK256, 0, 1, false),
2482 (LOG0, 0, 1, false),
2483 (LOG1, 0, 1, false),
2484 (LOG2, 0, 1, false),
2485 (LOG3, 0, 1, false),
2486 (LOG4, 0, 1, false),
2487 (CREATE, 1, 2, false),
2488 (CREATE2, 1, 2, false),
2489 (RETURN, 0, 1, false),
2490 (REVERT, 0, 1, false),
2491 );
2492 }
2493
2494 #[cold]
2495 fn set_gas_limit_type(&mut self, interpreter: &mut Interpreter) {
2496 match interpreter.bytecode.opcode() {
2497 op::CREATE2 => self.dynamic_gas_limit = true,
2498 op::CALL => {
2499 self.dynamic_gas_limit =
2502 try_or_return!(interpreter.stack.peek(0)) >= interpreter.gas.remaining() - 100
2503 }
2504 _ => self.dynamic_gas_limit = false,
2505 }
2506 }
2507}
2508
2509fn disallowed_mem_write(
2515 dest_offset: u64,
2516 size: u64,
2517 interpreter: &mut Interpreter,
2518 ranges: &[Range<u64>],
2519) {
2520 let revert_string = format!(
2521 "memory write at offset 0x{:02X} of size 0x{:02X} not allowed; safe range: {}",
2522 dest_offset,
2523 size,
2524 ranges.iter().map(|r| format!("[0x{:02X}, 0x{:02X})", r.start, r.end)).join(" U ")
2525 );
2526
2527 interpreter.bytecode.set_action(InterpreterAction::new_return(
2528 InstructionResult::Revert,
2529 Bytes::from(revert_string.into_bytes()),
2530 interpreter.gas,
2531 ));
2532}
2533
2534fn access_is_call(kind: crate::Vm::AccountAccessKind) -> bool {
2536 matches!(
2537 kind,
2538 crate::Vm::AccountAccessKind::Call
2539 | crate::Vm::AccountAccessKind::StaticCall
2540 | crate::Vm::AccountAccessKind::CallCode
2541 | crate::Vm::AccountAccessKind::DelegateCall
2542 )
2543}
2544
2545fn record_logs(recorded_logs: &mut Option<Vec<Vm::Log>>, log: &Log) {
2547 if let Some(storage_recorded_logs) = recorded_logs {
2548 storage_recorded_logs.push(Vm::Log {
2549 topics: log.data.topics().to_vec(),
2550 data: log.data.data.clone(),
2551 emitter: log.address,
2552 });
2553 }
2554}
2555
2556fn append_storage_access(
2558 last: &mut Vec<AccountAccess>,
2559 storage_access: crate::Vm::StorageAccess,
2560 storage_depth: u64,
2561) {
2562 if !last.is_empty() && last.first().unwrap().depth < storage_depth {
2564 if last.len() == 1 {
2570 last.first_mut().unwrap().storageAccesses.push(storage_access);
2571 } else {
2572 let last_record = last.last_mut().unwrap();
2573 if last_record.kind as u8 == crate::Vm::AccountAccessKind::Resume as u8 {
2574 last_record.storageAccesses.push(storage_access);
2575 } else {
2576 let entry = last.first().unwrap();
2577 let resume_record = crate::Vm::AccountAccess {
2578 chainInfo: crate::Vm::ChainInfo {
2579 forkId: entry.chainInfo.forkId,
2580 chainId: entry.chainInfo.chainId,
2581 },
2582 accessor: entry.accessor,
2583 account: entry.account,
2584 kind: crate::Vm::AccountAccessKind::Resume,
2585 initialized: entry.initialized,
2586 storageAccesses: vec![storage_access],
2587 reverted: entry.reverted,
2588 oldBalance: U256::ZERO,
2590 newBalance: U256::ZERO,
2591 oldNonce: 0,
2592 newNonce: 0,
2593 value: U256::ZERO,
2594 data: Bytes::new(),
2595 deployedCode: Bytes::new(),
2596 depth: entry.depth,
2597 };
2598 last.push(resume_record);
2599 }
2600 }
2601 }
2602}
2603
2604fn cheatcode_of<T: spec::CheatcodeDef>(_: &T) -> &'static spec::Cheatcode<'static> {
2606 T::CHEATCODE
2607}
2608
2609fn cheatcode_name(cheat: &spec::Cheatcode<'static>) -> &'static str {
2610 cheat.func.signature.split('(').next().unwrap()
2611}
2612
2613fn cheatcode_id(cheat: &spec::Cheatcode<'static>) -> &'static str {
2614 cheat.func.id
2615}
2616
2617fn cheatcode_signature(cheat: &spec::Cheatcode<'static>) -> &'static str {
2618 cheat.func.signature
2619}
2620
2621fn apply_dispatch<FEN: FoundryEvmNetwork>(
2623 calls: &Vm::VmCalls,
2624 ccx: &mut CheatsCtxt<'_, '_, FEN>,
2625 executor: &mut dyn CheatcodesExecutor<FEN>,
2626) -> Result {
2627 macro_rules! get_cheatcode {
2629 ($($variant:ident),*) => {
2630 match calls {
2631 $(Vm::VmCalls::$variant(cheat) => cheatcode_of(cheat),)*
2632 }
2633 };
2634 }
2635 let cheat = vm_calls!(get_cheatcode);
2636
2637 let _guard = debug_span!(target: "cheatcodes", "apply", id = %cheatcode_id(cheat)).entered();
2638 trace!(target: "cheatcodes", cheat = %cheatcode_signature(cheat), "applying");
2639
2640 if let spec::Status::Deprecated(replacement) = cheat.status {
2641 ccx.state.deprecated.insert(cheatcode_signature(cheat), replacement);
2642 }
2643
2644 macro_rules! dispatch {
2646 ($($variant:ident),*) => {
2647 match calls {
2648 $(Vm::VmCalls::$variant(cheat) => Cheatcode::apply_full(cheat, ccx, executor),)*
2649 }
2650 };
2651 }
2652 let mut result = vm_calls!(dispatch);
2653
2654 if let Err(e) = &mut result
2656 && e.is_str()
2657 {
2658 let name = cheatcode_name(cheat);
2659 if !name.contains("assert") && name != "rpcUrl" {
2663 *e = fmt_err!("vm.{name}: {e}");
2664 }
2665 }
2666
2667 trace!(
2668 target: "cheatcodes",
2669 return = %match &result {
2670 Ok(b) => hex::encode(b),
2671 Err(e) => e.to_string(),
2672 }
2673 );
2674
2675 result
2676}
2677
2678fn will_exit(action: &InterpreterAction) -> bool {
2680 match action {
2681 InterpreterAction::Return(result) => {
2682 result.result.is_ok_or_revert() || result.result.is_error()
2683 }
2684 _ => false,
2685 }
2686}