1use crate::{
4 CheatsConfig, CheatsCtxt, DynCheatcode, 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::BlobTransactionSidecar;
24use alloy_evm::eth::EthEvmContext;
25use alloy_network::TransactionBuilder4844;
26use alloy_primitives::{
27 Address, B256, Bytes, Log, TxKind, U256, hex,
28 map::{AddressHashMap, HashMap, HashSet},
29};
30use alloy_rpc_types::{
31 AccessList,
32 request::{TransactionInput, TransactionRequest},
33};
34use alloy_sol_types::{SolCall, SolInterface, SolValue};
35use foundry_common::{
36 SELECTOR_LEN, TransactionMaybeSigned,
37 mapping_slots::{MappingSlots, step as mapping_step},
38};
39use foundry_evm_core::{
40 Breakpoints, InspectorExt,
41 abi::Vm::stopExpectSafeMemoryCall,
42 backend::{DatabaseError, DatabaseExt, RevertDiagnostic},
43 constants::{CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS, MAGIC_ASSUME},
44 evm::{FoundryEvm, new_evm_with_existing_context},
45};
46use foundry_evm_traces::{
47 TracingInspector, TracingInspectorConfig, identifier::SignaturesIdentifier,
48};
49use foundry_wallets::multi_wallet::MultiWallet;
50use itertools::Itertools;
51use proptest::test_runner::{RngAlgorithm, TestRng, TestRunner};
52use rand::Rng;
53use revm::{
54 Inspector, Journal,
55 bytecode::opcode as op,
56 context::{BlockEnv, JournalTr, LocalContext, TransactionType, result::EVMError},
57 context_interface::{CreateScheme, transaction::SignedAuthorization},
58 handler::FrameResult,
59 interpreter::{
60 CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, FrameInput, Gas, Host,
61 InstructionResult, Interpreter, InterpreterAction, InterpreterResult,
62 interpreter_types::{Jumps, LoopControl, MemoryTr},
63 },
64 primitives::hardfork::SpecId,
65 state::EvmStorageSlot,
66};
67use serde_json::Value;
68use std::{
69 cmp::max,
70 collections::{BTreeMap, VecDeque},
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 type Ecx<'a, 'b, 'c> = &'a mut EthEvmContext<&'b mut (dyn DatabaseExt + 'c)>;
84
85pub trait CheatcodesExecutor {
91 fn get_inspector<'a>(&'a mut self, cheats: &'a mut Cheatcodes) -> Box<dyn InspectorExt + 'a>;
94
95 fn exec_create(
97 &mut self,
98 inputs: CreateInputs,
99 ccx: &mut CheatsCtxt,
100 ) -> Result<CreateOutcome, EVMError<DatabaseError>> {
101 with_evm(self, ccx, |evm| {
102 evm.journaled_state.depth += 1;
103
104 let frame = FrameInput::Create(Box::new(inputs));
105
106 let outcome = match evm.run_execution(frame)? {
107 FrameResult::Call(_) => unreachable!(),
108 FrameResult::Create(create) => create,
109 };
110
111 evm.journaled_state.depth -= 1;
112
113 Ok(outcome)
114 })
115 }
116
117 fn console_log(&mut self, ccx: &mut CheatsCtxt, msg: &str) {
118 self.get_inspector(ccx.state).console_log(msg);
119 }
120
121 fn tracing_inspector(&mut self) -> Option<&mut TracingInspector> {
123 None
124 }
125}
126
127fn with_evm<E, F, O>(
129 executor: &mut E,
130 ccx: &mut CheatsCtxt,
131 f: F,
132) -> Result<O, EVMError<DatabaseError>>
133where
134 E: CheatcodesExecutor + ?Sized,
135 F: for<'a, 'b> FnOnce(
136 &mut FoundryEvm<'a, &'b mut dyn InspectorExt>,
137 ) -> Result<O, EVMError<DatabaseError>>,
138{
139 let mut inspector = executor.get_inspector(ccx.state);
140 let error = std::mem::replace(&mut ccx.ecx.error, Ok(()));
141
142 let ctx = EthEvmContext {
143 block: ccx.ecx.block.clone(),
144 cfg: ccx.ecx.cfg.clone(),
145 tx: ccx.ecx.tx.clone(),
146 journaled_state: Journal {
147 inner: ccx.ecx.journaled_state.inner.clone(),
148 database: &mut *ccx.ecx.journaled_state.database as &mut dyn DatabaseExt,
149 },
150 local: LocalContext::default(),
151 chain: (),
152 error,
153 };
154
155 let mut evm = new_evm_with_existing_context(ctx, &mut *inspector);
156
157 let res = f(&mut evm)?;
158
159 let ctx = evm.into_context();
160 ccx.ecx.journaled_state.inner = ctx.journaled_state.inner;
161 ccx.ecx.block = ctx.block;
162 ccx.ecx.tx = ctx.tx;
163 ccx.ecx.cfg = ctx.cfg;
164 ccx.ecx.error = ctx.error;
165
166 Ok(res)
167}
168
169#[derive(Debug, Default, Clone, Copy)]
172struct TransparentCheatcodesExecutor;
173
174impl CheatcodesExecutor for TransparentCheatcodesExecutor {
175 fn get_inspector<'a>(&'a mut self, cheats: &'a mut Cheatcodes) -> Box<dyn InspectorExt + 'a> {
176 Box::new(cheats)
177 }
178}
179
180macro_rules! try_or_return {
181 ($e:expr) => {
182 match $e {
183 Ok(v) => v,
184 Err(_) => return,
185 }
186 };
187}
188
189#[derive(Debug, Default)]
191pub struct TestContext {
192 pub opened_read_files: HashMap<PathBuf, BufReader<File>>,
194}
195
196impl Clone for TestContext {
198 fn clone(&self) -> Self {
199 Default::default()
200 }
201}
202
203impl TestContext {
204 pub fn clear(&mut self) {
206 self.opened_read_files.clear();
207 }
208}
209
210#[derive(Clone, Debug)]
212pub struct BroadcastableTransaction {
213 pub rpc: Option<String>,
215 pub transaction: TransactionMaybeSigned,
217}
218
219#[derive(Clone, Debug, Copy)]
220pub struct RecordDebugStepInfo {
221 pub start_node_idx: usize,
223 pub original_tracer_config: TracingInspectorConfig,
225}
226
227#[derive(Clone, Debug, Default)]
229pub struct GasMetering {
230 pub paused: bool,
232 pub touched: bool,
235 pub reset: bool,
237 pub paused_frames: Vec<Gas>,
239
240 pub active_gas_snapshot: Option<(String, String)>,
242
243 pub last_call_gas: Option<crate::Vm::Gas>,
246
247 pub recording: bool,
249 pub last_gas_used: u64,
251 pub gas_records: Vec<GasRecord>,
253}
254
255impl GasMetering {
256 pub fn start(&mut self) {
258 self.recording = true;
259 }
260
261 pub fn stop(&mut self) {
263 self.recording = false;
264 }
265
266 pub fn resume(&mut self) {
268 if self.paused {
269 self.paused = false;
270 self.touched = true;
271 }
272 self.paused_frames.clear();
273 }
274
275 pub fn reset(&mut self) {
277 self.paused = false;
278 self.touched = true;
279 self.reset = true;
280 self.paused_frames.clear();
281 }
282}
283
284#[derive(Clone, Debug, Default)]
286pub struct ArbitraryStorage {
287 pub values: HashMap<Address, HashMap<U256, U256>>,
291 pub copies: HashMap<Address, Address>,
293 pub overwrites: HashSet<Address>,
295}
296
297impl ArbitraryStorage {
298 pub fn mark_arbitrary(&mut self, address: &Address, overwrite: bool) {
300 self.values.insert(*address, HashMap::default());
301 if overwrite {
302 self.overwrites.insert(*address);
303 } else {
304 self.overwrites.remove(address);
305 }
306 }
307
308 pub fn mark_copy(&mut self, from: &Address, to: &Address) {
310 if self.values.contains_key(from) {
311 self.copies.insert(*to, *from);
312 }
313 }
314
315 pub fn save(&mut self, ecx: Ecx, address: Address, slot: U256, data: U256) {
319 self.values.get_mut(&address).expect("missing arbitrary address entry").insert(slot, data);
320 if let Ok(mut account) = ecx.journaled_state.load_account(address) {
321 account.storage.insert(slot, EvmStorageSlot::new(data, 0));
322 }
323 }
324
325 pub fn copy(&mut self, ecx: Ecx, target: Address, slot: U256, new_value: U256) -> U256 {
331 let source = self.copies.get(&target).expect("missing arbitrary copy target entry");
332 let storage_cache = self.values.get_mut(source).expect("missing arbitrary source storage");
333 let value = match storage_cache.get(&slot) {
334 Some(value) => *value,
335 None => {
336 storage_cache.insert(slot, new_value);
337 if let Ok(mut source_account) = ecx.journaled_state.load_account(*source) {
339 source_account.storage.insert(slot, EvmStorageSlot::new(new_value, 0));
340 }
341 new_value
342 }
343 };
344 if let Ok(mut target_account) = ecx.journaled_state.load_account(target) {
346 target_account.storage.insert(slot, EvmStorageSlot::new(value, 0));
347 }
348 value
349 }
350}
351
352pub type BroadcastableTransactions = VecDeque<BroadcastableTransaction>;
354
355#[derive(Clone, Debug)]
373pub struct Cheatcodes {
374 pub analysis: Option<CheatcodeAnalysis>,
376
377 pub block: Option<BlockEnv>,
382
383 pub active_delegations: Vec<SignedAuthorization>,
387
388 pub active_blob_sidecar: Option<BlobTransactionSidecar>,
390
391 pub gas_price: Option<u128>,
396
397 pub labels: AddressHashMap<String>,
399
400 pub pranks: BTreeMap<usize, Prank>,
402
403 pub expected_revert: Option<ExpectedRevert>,
405
406 pub assume_no_revert: Option<AssumeNoRevert>,
408
409 pub fork_revert_diagnostic: Option<RevertDiagnostic>,
411
412 pub accesses: RecordAccess,
414
415 pub recording_accesses: bool,
417
418 pub recorded_account_diffs_stack: Option<Vec<Vec<AccountAccess>>>,
424
425 pub record_debug_steps_info: Option<RecordDebugStepInfo>,
427
428 pub recorded_logs: Option<Vec<crate::Vm::Log>>,
430
431 pub mocked_calls: HashMap<Address, BTreeMap<MockCallDataContext, VecDeque<MockCallReturnData>>>,
434
435 pub mocked_functions: HashMap<Address, HashMap<Bytes, Address>>,
437
438 pub expected_calls: ExpectedCallTracker,
440 pub expected_emits: ExpectedEmitTracker,
442 pub expected_creates: Vec<ExpectedCreate>,
444
445 pub allowed_mem_writes: HashMap<u64, Vec<Range<u64>>>,
447
448 pub broadcast: Option<Broadcast>,
450
451 pub broadcastable_transactions: BroadcastableTransactions,
453
454 pub access_list: Option<AccessList>,
456
457 pub config: Arc<CheatsConfig>,
459
460 pub test_context: TestContext,
462
463 pub fs_commit: bool,
466
467 pub serialized_jsons: BTreeMap<String, BTreeMap<String, Value>>,
470
471 pub eth_deals: Vec<DealRecord>,
473
474 pub gas_metering: GasMetering,
476
477 pub gas_snapshots: BTreeMap<String, BTreeMap<String, String>>,
480
481 pub mapping_slots: Option<AddressHashMap<MappingSlots>>,
483
484 pub pc: usize,
486 pub breakpoints: Breakpoints,
489
490 pub intercept_next_create_call: bool,
492
493 test_runner: Option<TestRunner>,
496
497 pub ignored_traces: IgnoredTraces,
499
500 pub arbitrary_storage: Option<ArbitraryStorage>,
502
503 pub deprecated: HashMap<&'static str, Option<&'static str>>,
505 pub wallets: Option<Wallets>,
507 signatures_identifier: OnceLock<Option<SignaturesIdentifier>>,
509 pub dynamic_gas_limit: bool,
511 pub execution_evm_version: Option<SpecId>,
513}
514
515impl Default for Cheatcodes {
519 fn default() -> Self {
520 Self::new(Arc::default())
521 }
522}
523
524impl Cheatcodes {
525 pub fn new(config: Arc<CheatsConfig>) -> Self {
527 Self {
528 analysis: None,
529 fs_commit: true,
530 labels: config.labels.clone(),
531 config,
532 block: Default::default(),
533 active_delegations: Default::default(),
534 active_blob_sidecar: Default::default(),
535 gas_price: Default::default(),
536 pranks: Default::default(),
537 expected_revert: Default::default(),
538 assume_no_revert: Default::default(),
539 fork_revert_diagnostic: Default::default(),
540 accesses: Default::default(),
541 recording_accesses: Default::default(),
542 recorded_account_diffs_stack: Default::default(),
543 recorded_logs: Default::default(),
544 record_debug_steps_info: Default::default(),
545 mocked_calls: Default::default(),
546 mocked_functions: Default::default(),
547 expected_calls: Default::default(),
548 expected_emits: Default::default(),
549 expected_creates: Default::default(),
550 allowed_mem_writes: Default::default(),
551 broadcast: Default::default(),
552 broadcastable_transactions: Default::default(),
553 access_list: Default::default(),
554 test_context: Default::default(),
555 serialized_jsons: Default::default(),
556 eth_deals: Default::default(),
557 gas_metering: Default::default(),
558 gas_snapshots: Default::default(),
559 mapping_slots: Default::default(),
560 pc: Default::default(),
561 breakpoints: Default::default(),
562 intercept_next_create_call: Default::default(),
563 test_runner: Default::default(),
564 ignored_traces: Default::default(),
565 arbitrary_storage: Default::default(),
566 deprecated: Default::default(),
567 wallets: Default::default(),
568 signatures_identifier: Default::default(),
569 dynamic_gas_limit: Default::default(),
570 execution_evm_version: None,
571 }
572 }
573
574 pub fn set_analysis(&mut self, analysis: CheatcodeAnalysis) {
576 self.analysis = Some(analysis);
577 }
578
579 pub fn get_prank(&self, depth: usize) -> Option<&Prank> {
583 self.pranks.range(..=depth).last().map(|(_, prank)| prank)
584 }
585
586 pub fn wallets(&mut self) -> &Wallets {
588 self.wallets.get_or_insert_with(|| Wallets::new(MultiWallet::default(), None))
589 }
590
591 pub fn set_wallets(&mut self, wallets: Wallets) {
593 self.wallets = Some(wallets);
594 }
595
596 pub fn add_delegation(&mut self, authorization: SignedAuthorization) {
598 self.active_delegations.push(authorization);
599 }
600
601 pub fn signatures_identifier(&self) -> Option<&SignaturesIdentifier> {
603 self.signatures_identifier.get_or_init(|| SignaturesIdentifier::new(true).ok()).as_ref()
604 }
605
606 fn apply_cheatcode(
608 &mut self,
609 ecx: Ecx,
610 call: &CallInputs,
611 executor: &mut dyn CheatcodesExecutor,
612 ) -> Result {
613 let decoded = Vm::VmCalls::abi_decode(&call.input.bytes(ecx)).map_err(|e| {
615 if let alloy_sol_types::Error::UnknownSelector { name: _, selector } = e {
616 let msg = format!(
617 "unknown cheatcode with selector {selector}; \
618 you may have a mismatch between the `Vm` interface (likely in `forge-std`) \
619 and the `forge` version"
620 );
621 return alloy_sol_types::Error::Other(std::borrow::Cow::Owned(msg));
622 }
623 e
624 })?;
625
626 let caller = call.caller;
627
628 ecx.journaled_state.database.ensure_cheatcode_access_forking_mode(&caller)?;
631
632 apply_dispatch(
633 &decoded,
634 &mut CheatsCtxt { state: self, ecx, gas_limit: call.gas_limit, caller },
635 executor,
636 )
637 }
638
639 fn allow_cheatcodes_on_create(&self, ecx: Ecx, caller: Address, created_address: Address) {
645 if ecx.journaled_state.depth <= 1
646 || ecx.journaled_state.database.has_cheatcode_access(&caller)
647 {
648 ecx.journaled_state.database.allow_cheatcode_access(created_address);
649 }
650 }
651
652 fn apply_accesslist(&mut self, ecx: Ecx) {
658 if let Some(access_list) = &self.access_list {
659 ecx.tx.access_list = access_list.clone();
660
661 if ecx.tx.tx_type == TransactionType::Legacy as u8 {
662 ecx.tx.tx_type = TransactionType::Eip2930 as u8;
663 }
664 }
665 }
666
667 pub fn on_revert(&mut self, ecx: Ecx) {
672 trace!(deals=?self.eth_deals.len(), "rolling back deals");
673
674 if self.expected_revert.is_some() {
676 return;
677 }
678
679 if ecx.journaled_state.depth() > 0 {
681 return;
682 }
683
684 while let Some(record) = self.eth_deals.pop() {
688 if let Some(acc) = ecx.journaled_state.state.get_mut(&record.address) {
689 acc.info.balance = record.old_balance;
690 }
691 }
692 }
693
694 pub fn call_with_executor(
695 &mut self,
696 ecx: Ecx,
697 call: &mut CallInputs,
698 executor: &mut dyn CheatcodesExecutor,
699 ) -> Option<CallOutcome> {
700 if let Some(spec_id) = self.execution_evm_version {
702 ecx.cfg.spec = spec_id;
703 }
704
705 let gas = Gas::new(call.gas_limit);
706 let curr_depth = ecx.journaled_state.depth();
707
708 if curr_depth == 0 {
712 let sender = ecx.tx.caller;
713 let account = match super::evm::journaled_account(ecx, sender) {
714 Ok(account) => account,
715 Err(err) => {
716 return Some(CallOutcome {
717 result: InterpreterResult {
718 result: InstructionResult::Revert,
719 output: err.abi_encode().into(),
720 gas,
721 },
722 memory_offset: call.return_memory_offset.clone(),
723 });
724 }
725 };
726 let prev = account.info.nonce;
727 account.info.nonce = prev.saturating_sub(1);
728
729 trace!(target: "cheatcodes", %sender, nonce=account.info.nonce, prev, "corrected nonce");
730 }
731
732 if call.target_address == CHEATCODE_ADDRESS {
733 return match self.apply_cheatcode(ecx, call, executor) {
734 Ok(retdata) => Some(CallOutcome {
735 result: InterpreterResult {
736 result: InstructionResult::Return,
737 output: retdata.into(),
738 gas,
739 },
740 memory_offset: call.return_memory_offset.clone(),
741 }),
742 Err(err) => Some(CallOutcome {
743 result: InterpreterResult {
744 result: InstructionResult::Revert,
745 output: err.abi_encode().into(),
746 gas,
747 },
748 memory_offset: call.return_memory_offset.clone(),
749 }),
750 };
751 }
752
753 if call.target_address == HARDHAT_CONSOLE_ADDRESS {
754 return None;
755 }
756
757 if let Some(expected_calls_for_target) = self.expected_calls.get_mut(&call.bytecode_address)
761 {
762 for (calldata, (expected, actual_count)) in expected_calls_for_target {
764 if calldata.len() <= call.input.len() &&
767 *calldata == call.input.bytes(ecx)[..calldata.len()] &&
769 expected
771 .value.is_none_or(|value| Some(value) == call.transfer_value()) &&
772 expected.gas.is_none_or(|gas| gas == call.gas_limit) &&
774 expected.min_gas.is_none_or(|min_gas| min_gas <= call.gas_limit)
776 {
777 *actual_count += 1;
778 }
779 }
780 }
781
782 if let Some(mocks) = self.mocked_calls.get_mut(&call.bytecode_address) {
784 let ctx = MockCallDataContext {
785 calldata: call.input.bytes(ecx),
786 value: call.transfer_value(),
787 };
788
789 if let Some(return_data_queue) = match mocks.get_mut(&ctx) {
790 Some(queue) => Some(queue),
791 None => mocks
792 .iter_mut()
793 .find(|(mock, _)| {
794 call.input.bytes(ecx).get(..mock.calldata.len()) == Some(&mock.calldata[..])
795 && mock.value.is_none_or(|value| Some(value) == call.transfer_value())
796 })
797 .map(|(_, v)| v),
798 } && let Some(return_data) = if return_data_queue.len() == 1 {
799 return_data_queue.front().map(|x| x.to_owned())
801 } else {
802 return_data_queue.pop_front()
804 } {
805 return Some(CallOutcome {
806 result: InterpreterResult {
807 result: return_data.ret_type,
808 output: return_data.data,
809 gas,
810 },
811 memory_offset: call.return_memory_offset.clone(),
812 });
813 }
814 }
815
816 if let Some(prank) = &self.get_prank(curr_depth) {
818 if prank.delegate_call
820 && curr_depth == prank.depth
821 && let CallScheme::DelegateCall = call.scheme
822 {
823 call.target_address = prank.new_caller;
824 call.caller = prank.new_caller;
825 if let Some(new_origin) = prank.new_origin {
826 ecx.tx.caller = new_origin;
827 }
828 }
829
830 if curr_depth >= prank.depth && call.caller == prank.prank_caller {
831 let mut prank_applied = false;
832
833 if curr_depth == prank.depth {
835 let _ = journaled_account(ecx, prank.new_caller);
837 call.caller = prank.new_caller;
838 prank_applied = true;
839 }
840
841 if let Some(new_origin) = prank.new_origin {
843 ecx.tx.caller = new_origin;
844 prank_applied = true;
845 }
846
847 if prank_applied && let Some(applied_prank) = prank.first_time_applied() {
849 self.pranks.insert(curr_depth, applied_prank);
850 }
851 }
852 }
853
854 self.apply_accesslist(ecx);
856
857 if let Some(broadcast) = &self.broadcast {
859 let is_fixed_gas_limit = call.gas_limit >= 21_000 && !self.dynamic_gas_limit;
862 self.dynamic_gas_limit = false;
863
864 if curr_depth == broadcast.depth && call.caller == broadcast.original_caller {
869 ecx.tx.caller = broadcast.new_origin;
873
874 call.caller = broadcast.new_origin;
875 if !call.is_static {
880 if let Err(err) = ecx.journaled_state.load_account(broadcast.new_origin) {
881 return Some(CallOutcome {
882 result: InterpreterResult {
883 result: InstructionResult::Revert,
884 output: Error::encode(err),
885 gas,
886 },
887 memory_offset: call.return_memory_offset.clone(),
888 });
889 }
890
891 let input = TransactionInput::new(call.input.bytes(ecx));
892
893 let account =
894 ecx.journaled_state.inner.state().get_mut(&broadcast.new_origin).unwrap();
895
896 let mut tx_req = TransactionRequest {
897 from: Some(broadcast.new_origin),
898 to: Some(TxKind::from(Some(call.target_address))),
899 value: call.transfer_value(),
900 input,
901 nonce: Some(account.info.nonce),
902 chain_id: Some(ecx.cfg.chain_id),
903 gas: if is_fixed_gas_limit { Some(call.gas_limit) } else { None },
904 ..Default::default()
905 };
906
907 let active_delegations = std::mem::take(&mut self.active_delegations);
908 if let Some(blob_sidecar) = self.active_blob_sidecar.take() {
910 if !active_delegations.is_empty() {
912 let msg = "both delegation and blob are active; `attachBlob` and `attachDelegation` are not compatible";
913 return Some(CallOutcome {
914 result: InterpreterResult {
915 result: InstructionResult::Revert,
916 output: Error::encode(msg),
917 gas,
918 },
919 memory_offset: call.return_memory_offset.clone(),
920 });
921 }
922 tx_req.set_blob_sidecar(blob_sidecar);
923 }
924
925 if !active_delegations.is_empty() {
927 for auth in &active_delegations {
928 let Ok(authority) = auth.recover_authority() else {
929 continue;
930 };
931 if authority == broadcast.new_origin {
932 account.info.nonce += 1;
935 }
936 }
937 tx_req.authorization_list = Some(active_delegations);
938 }
939
940 self.broadcastable_transactions.push_back(BroadcastableTransaction {
941 rpc: ecx.journaled_state.database.active_fork_url(),
942 transaction: tx_req.into(),
943 });
944 debug!(target: "cheatcodes", tx=?self.broadcastable_transactions.back().unwrap(), "broadcastable call");
945
946 if !self.config.evm_opts.isolate {
948 let prev = account.info.nonce;
949 account.info.nonce += 1;
950 debug!(target: "cheatcodes", address=%broadcast.new_origin, nonce=prev+1, prev, "incremented nonce");
951 }
952 } else if broadcast.single_call {
953 let msg = "`staticcall`s are not allowed after `broadcast`; use `startBroadcast` instead";
954 return Some(CallOutcome {
955 result: InterpreterResult {
956 result: InstructionResult::Revert,
957 output: Error::encode(msg),
958 gas,
959 },
960 memory_offset: call.return_memory_offset.clone(),
961 });
962 }
963 }
964 }
965
966 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
968 let initialized;
971 let old_balance;
972 let old_nonce;
973 if let Ok(acc) = ecx.journaled_state.load_account(call.target_address) {
974 initialized = acc.info.exists();
975 old_balance = acc.info.balance;
976 old_nonce = acc.info.nonce;
977 } else {
978 initialized = false;
979 old_balance = U256::ZERO;
980 old_nonce = 0;
981 }
982 let kind = match call.scheme {
983 CallScheme::Call => crate::Vm::AccountAccessKind::Call,
984 CallScheme::CallCode => crate::Vm::AccountAccessKind::CallCode,
985 CallScheme::DelegateCall => crate::Vm::AccountAccessKind::DelegateCall,
986 CallScheme::StaticCall => crate::Vm::AccountAccessKind::StaticCall,
987 };
988 recorded_account_diffs_stack.push(vec![AccountAccess {
994 chainInfo: crate::Vm::ChainInfo {
995 forkId: ecx.journaled_state.db().active_fork_id().unwrap_or_default(),
996 chainId: U256::from(ecx.cfg.chain_id),
997 },
998 accessor: call.caller,
999 account: call.bytecode_address,
1000 kind,
1001 initialized,
1002 oldBalance: old_balance,
1003 newBalance: U256::ZERO, oldNonce: old_nonce,
1005 newNonce: 0, value: call.call_value(),
1007 data: call.input.bytes(ecx),
1008 reverted: false,
1009 deployedCode: Bytes::new(),
1010 storageAccesses: vec![], depth: ecx
1012 .journaled_state
1013 .depth()
1014 .try_into()
1015 .expect("journaled state depth exceeds u64"),
1016 }]);
1017 }
1018
1019 None
1020 }
1021
1022 pub fn rng(&mut self) -> &mut impl Rng {
1023 self.test_runner().rng()
1024 }
1025
1026 pub fn test_runner(&mut self) -> &mut TestRunner {
1027 self.test_runner.get_or_insert_with(|| match self.config.seed {
1028 Some(seed) => TestRunner::new_with_rng(
1029 proptest::test_runner::Config::default(),
1030 TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>()),
1031 ),
1032 None => TestRunner::new(proptest::test_runner::Config::default()),
1033 })
1034 }
1035
1036 pub fn set_seed(&mut self, seed: U256) {
1037 self.test_runner = Some(TestRunner::new_with_rng(
1038 proptest::test_runner::Config::default(),
1039 TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>()),
1040 ));
1041 }
1042
1043 pub fn arbitrary_storage(&mut self) -> &mut ArbitraryStorage {
1046 self.arbitrary_storage.get_or_insert_with(ArbitraryStorage::default)
1047 }
1048
1049 pub fn has_arbitrary_storage(&self, address: &Address) -> bool {
1051 match &self.arbitrary_storage {
1052 Some(storage) => storage.values.contains_key(address),
1053 None => false,
1054 }
1055 }
1056
1057 pub fn should_overwrite_arbitrary_storage(
1061 &self,
1062 address: &Address,
1063 storage_slot: U256,
1064 ) -> bool {
1065 match &self.arbitrary_storage {
1066 Some(storage) => {
1067 storage.overwrites.contains(address)
1068 && storage
1069 .values
1070 .get(address)
1071 .and_then(|arbitrary_values| arbitrary_values.get(&storage_slot))
1072 .is_none()
1073 }
1074 None => false,
1075 }
1076 }
1077
1078 pub fn is_arbitrary_storage_copy(&self, address: &Address) -> bool {
1080 match &self.arbitrary_storage {
1081 Some(storage) => storage.copies.contains_key(address),
1082 None => false,
1083 }
1084 }
1085
1086 pub fn struct_defs(&self) -> Option<&foundry_common::fmt::StructDefinitions> {
1088 self.analysis.as_ref().and_then(|analysis| analysis.struct_defs().ok())
1089 }
1090}
1091
1092impl Inspector<EthEvmContext<&mut dyn DatabaseExt>> for Cheatcodes {
1093 fn initialize_interp(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1094 if let Some(block) = self.block.take() {
1097 ecx.block = block;
1098 }
1099 if let Some(gas_price) = self.gas_price.take() {
1100 ecx.tx.gas_price = gas_price;
1101 }
1102
1103 if self.gas_metering.paused {
1105 self.gas_metering.paused_frames.push(interpreter.gas);
1106 }
1107
1108 if let Some(expected) = &mut self.expected_revert {
1110 expected.max_depth = max(ecx.journaled_state.depth(), expected.max_depth);
1111 }
1112 }
1113
1114 fn step(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1115 self.pc = interpreter.bytecode.pc();
1116
1117 if self.broadcast.is_some() {
1118 self.set_gas_limit_type(interpreter);
1119 }
1120
1121 if self.gas_metering.paused {
1123 self.meter_gas(interpreter);
1124 }
1125
1126 if self.gas_metering.reset {
1128 self.meter_gas_reset(interpreter);
1129 }
1130
1131 if self.recording_accesses {
1133 self.record_accesses(interpreter);
1134 }
1135
1136 if self.recorded_account_diffs_stack.is_some() {
1138 self.record_state_diffs(interpreter, ecx);
1139 }
1140
1141 if !self.allowed_mem_writes.is_empty() {
1143 self.check_mem_opcodes(
1144 interpreter,
1145 ecx.journaled_state.depth().try_into().expect("journaled state depth exceeds u64"),
1146 );
1147 }
1148
1149 if let Some(mapping_slots) = &mut self.mapping_slots {
1151 mapping_step(mapping_slots, interpreter);
1152 }
1153
1154 if self.gas_metering.recording {
1156 self.meter_gas_record(interpreter, ecx);
1157 }
1158 }
1159
1160 fn step_end(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1161 if self.gas_metering.paused {
1162 self.meter_gas_end(interpreter);
1163 }
1164
1165 if self.gas_metering.touched {
1166 self.meter_gas_check(interpreter);
1167 }
1168
1169 if self.arbitrary_storage.is_some() {
1171 self.arbitrary_storage_end(interpreter, ecx);
1172 }
1173 }
1174
1175 fn log(&mut self, interpreter: &mut Interpreter, _ecx: Ecx, log: Log) {
1176 if !self.expected_emits.is_empty() {
1177 expect::handle_expect_emit(self, &log, interpreter);
1178 }
1179
1180 if let Some(storage_recorded_logs) = &mut self.recorded_logs {
1182 storage_recorded_logs.push(Vm::Log {
1183 topics: log.data.topics().to_vec(),
1184 data: log.data.data.clone(),
1185 emitter: log.address,
1186 });
1187 }
1188 }
1189
1190 fn call(&mut self, ecx: Ecx, inputs: &mut CallInputs) -> Option<CallOutcome> {
1191 Self::call_with_executor(self, ecx, inputs, &mut TransparentCheatcodesExecutor)
1192 }
1193
1194 fn call_end(&mut self, ecx: Ecx, call: &CallInputs, outcome: &mut CallOutcome) {
1195 let cheatcode_call = call.target_address == CHEATCODE_ADDRESS
1196 || call.target_address == HARDHAT_CONSOLE_ADDRESS;
1197
1198 if !cheatcode_call {
1202 let curr_depth = ecx.journaled_state.depth();
1204 if let Some(prank) = &self.get_prank(curr_depth)
1205 && curr_depth == prank.depth
1206 {
1207 ecx.tx.caller = prank.prank_origin;
1208
1209 if prank.single_call {
1211 self.pranks.remove(&curr_depth);
1212 }
1213 }
1214
1215 if let Some(broadcast) = &self.broadcast
1217 && curr_depth == broadcast.depth
1218 {
1219 ecx.tx.caller = broadcast.original_origin;
1220
1221 if broadcast.single_call {
1223 let _ = self.broadcast.take();
1224 }
1225 }
1226 }
1227
1228 if let Some(assume_no_revert) = &mut self.assume_no_revert {
1230 if outcome.result.is_revert() && assume_no_revert.reverted_by.is_none() {
1233 assume_no_revert.reverted_by = Some(call.target_address);
1234 }
1235
1236 let curr_depth = ecx.journaled_state.depth();
1238 if curr_depth <= assume_no_revert.depth && !cheatcode_call {
1239 if outcome.result.is_revert() {
1242 let assume_no_revert = std::mem::take(&mut self.assume_no_revert).unwrap();
1243 return match revert_handlers::handle_assume_no_revert(
1244 &assume_no_revert,
1245 outcome.result.result,
1246 &outcome.result.output,
1247 &self.config.available_artifacts,
1248 ) {
1249 Ok(_) => {
1252 outcome.result.output = Error::from(MAGIC_ASSUME).abi_encode().into();
1253 }
1254 Err(error) => {
1257 trace!(expected=?assume_no_revert, ?error, status=?outcome.result.result, "Expected revert mismatch");
1258 outcome.result.result = InstructionResult::Revert;
1259 outcome.result.output = error.abi_encode().into();
1260 }
1261 };
1262 } else {
1263 self.assume_no_revert = None;
1265 }
1266 }
1267 }
1268
1269 if let Some(expected_revert) = &mut self.expected_revert {
1271 if outcome.result.is_revert() {
1274 if expected_revert.reverter.is_some()
1278 && (expected_revert.reverted_by.is_none() || expected_revert.count > 1)
1279 {
1280 expected_revert.reverted_by = Some(call.target_address);
1281 }
1282 }
1283
1284 let curr_depth = ecx.journaled_state.depth();
1285 if curr_depth <= expected_revert.depth {
1286 let needs_processing = match expected_revert.kind {
1287 ExpectedRevertKind::Default => !cheatcode_call,
1288 ExpectedRevertKind::Cheatcode { pending_processing } => {
1291 cheatcode_call && !pending_processing
1292 }
1293 };
1294
1295 if needs_processing {
1296 let mut expected_revert = std::mem::take(&mut self.expected_revert).unwrap();
1297 return match revert_handlers::handle_expect_revert(
1298 cheatcode_call,
1299 false,
1300 self.config.internal_expect_revert,
1301 &expected_revert,
1302 outcome.result.result,
1303 outcome.result.output.clone(),
1304 &self.config.available_artifacts,
1305 ) {
1306 Err(error) => {
1307 trace!(expected=?expected_revert, ?error, status=?outcome.result.result, "Expected revert mismatch");
1308 outcome.result.result = InstructionResult::Revert;
1309 outcome.result.output = error.abi_encode().into();
1310 }
1311 Ok((_, retdata)) => {
1312 expected_revert.actual_count += 1;
1313 if expected_revert.actual_count < expected_revert.count {
1314 self.expected_revert = Some(expected_revert);
1315 }
1316 outcome.result.result = InstructionResult::Return;
1317 outcome.result.output = retdata;
1318 }
1319 };
1320 }
1321
1322 if let ExpectedRevertKind::Cheatcode { pending_processing } =
1325 &mut self.expected_revert.as_mut().unwrap().kind
1326 {
1327 *pending_processing = false;
1328 }
1329 }
1330 }
1331
1332 if cheatcode_call {
1335 return;
1336 }
1337
1338 let gas = outcome.result.gas;
1341 self.gas_metering.last_call_gas = Some(crate::Vm::Gas {
1342 gasLimit: gas.limit(),
1343 gasTotalUsed: gas.spent(),
1344 gasMemoryUsed: 0,
1345 gasRefunded: gas.refunded(),
1346 gasRemaining: gas.remaining(),
1347 });
1348
1349 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1352 if ecx.journaled_state.depth() > 0
1354 && let Some(mut last_recorded_depth) = recorded_account_diffs_stack.pop()
1355 {
1356 if outcome.result.is_revert() {
1359 last_recorded_depth.iter_mut().for_each(|element| {
1360 element.reverted = true;
1361 element
1362 .storageAccesses
1363 .iter_mut()
1364 .for_each(|storage_access| storage_access.reverted = true);
1365 })
1366 }
1367
1368 if let Some(call_access) = last_recorded_depth.first_mut() {
1369 let curr_depth = ecx.journaled_state.depth();
1374 if call_access.depth == curr_depth as u64
1375 && let Ok(acc) = ecx.journaled_state.load_account(call.target_address)
1376 {
1377 debug_assert!(access_is_call(call_access.kind));
1378 call_access.newBalance = acc.info.balance;
1379 call_access.newNonce = acc.info.nonce;
1380 }
1381 if let Some(last) = recorded_account_diffs_stack.last_mut() {
1386 last.extend(last_recorded_depth);
1387 } else {
1388 recorded_account_diffs_stack.push(last_recorded_depth);
1389 }
1390 }
1391 }
1392 }
1393
1394 let should_check_emits = self
1406 .expected_emits
1407 .iter()
1408 .any(|(expected, _)| {
1409 let curr_depth = ecx.journaled_state.depth();
1410 expected.depth == curr_depth
1411 }) &&
1412 !call.is_static;
1414 if should_check_emits {
1415 let expected_counts = self
1416 .expected_emits
1417 .iter()
1418 .filter_map(|(expected, count_map)| {
1419 let count = match expected.address {
1420 Some(emitter) => match count_map.get(&emitter) {
1421 Some(log_count) => expected
1422 .log
1423 .as_ref()
1424 .map(|l| log_count.count(l))
1425 .unwrap_or_else(|| log_count.count_unchecked()),
1426 None => 0,
1427 },
1428 None => match &expected.log {
1429 Some(log) => count_map.values().map(|logs| logs.count(log)).sum(),
1430 None => count_map.values().map(|logs| logs.count_unchecked()).sum(),
1431 },
1432 };
1433
1434 if count != expected.count { Some((expected, count)) } else { None }
1435 })
1436 .collect::<Vec<_>>();
1437
1438 if let Some((expected, _)) = self
1440 .expected_emits
1441 .iter()
1442 .find(|(expected, _)| !expected.found && expected.count > 0)
1443 {
1444 outcome.result.result = InstructionResult::Revert;
1445 let error_msg = expected.mismatch_error.as_deref().unwrap_or("log != expected log");
1446 outcome.result.output = error_msg.abi_encode().into();
1447 return;
1448 }
1449
1450 if !expected_counts.is_empty() {
1451 let msg = if outcome.result.is_ok() {
1452 let (expected, count) = expected_counts.first().unwrap();
1453 format!("log emitted {count} times, expected {}", expected.count)
1454 } else {
1455 "expected an emit, but the call reverted instead. \
1456 ensure you're testing the happy path when using `expectEmit`"
1457 .to_string()
1458 };
1459
1460 outcome.result.result = InstructionResult::Revert;
1461 outcome.result.output = Error::encode(msg);
1462 return;
1463 }
1464
1465 self.expected_emits.clear()
1469 }
1470
1471 let diag = self.fork_revert_diagnostic.take();
1474
1475 if outcome.result.is_revert()
1478 && let Some(err) = diag
1479 {
1480 outcome.result.output = Error::encode(err.to_error_msg(&self.labels));
1481 return;
1482 }
1483
1484 if let TxKind::Call(test_contract) = ecx.tx.kind {
1487 if ecx.journaled_state.db().is_forked_mode()
1490 && outcome.result.result == InstructionResult::Stop
1491 && call.target_address != test_contract
1492 {
1493 let journaled_state = ecx.journaled_state.clone();
1494 self.fork_revert_diagnostic =
1495 ecx.journaled_state.db().diagnose_revert(call.target_address, &journaled_state);
1496 }
1497 }
1498
1499 if ecx.journaled_state.depth() == 0 {
1501 if outcome.result.is_revert() {
1505 return;
1506 }
1507
1508 for (address, calldatas) in &self.expected_calls {
1513 for (calldata, (expected, actual_count)) in calldatas {
1515 let ExpectedCallData { gas, min_gas, value, count, call_type } = expected;
1517
1518 let failed = match call_type {
1519 ExpectedCallType::Count => *count != *actual_count,
1523 ExpectedCallType::NonCount => *count > *actual_count,
1528 };
1529 if failed {
1530 let expected_values = [
1531 Some(format!("data {}", hex::encode_prefixed(calldata))),
1532 value.as_ref().map(|v| format!("value {v}")),
1533 gas.map(|g| format!("gas {g}")),
1534 min_gas.map(|g| format!("minimum gas {g}")),
1535 ]
1536 .into_iter()
1537 .flatten()
1538 .join(", ");
1539 let but = if outcome.result.is_ok() {
1540 let s = if *actual_count == 1 { "" } else { "s" };
1541 format!("was called {actual_count} time{s}")
1542 } else {
1543 "the call reverted instead; \
1544 ensure you're testing the happy path when using `expectCall`"
1545 .to_string()
1546 };
1547 let s = if *count == 1 { "" } else { "s" };
1548 let msg = format!(
1549 "expected call to {address} with {expected_values} \
1550 to be called {count} time{s}, but {but}"
1551 );
1552 outcome.result.result = InstructionResult::Revert;
1553 outcome.result.output = Error::encode(msg);
1554
1555 return;
1556 }
1557 }
1558 }
1559
1560 for (expected, _) in &mut self.expected_emits {
1564 if expected.count == 0 && !expected.found {
1565 expected.found = true;
1566 }
1567 }
1568 self.expected_emits.retain(|(expected, _)| !expected.found);
1569 if !self.expected_emits.is_empty() {
1571 let msg = if outcome.result.is_ok() {
1572 "expected an emit, but no logs were emitted afterwards. \
1573 you might have mismatched events or not enough events were emitted"
1574 } else {
1575 "expected an emit, but the call reverted instead. \
1576 ensure you're testing the happy path when using `expectEmit`"
1577 };
1578 outcome.result.result = InstructionResult::Revert;
1579 outcome.result.output = Error::encode(msg);
1580 return;
1581 }
1582
1583 if let Some(expected_create) = self.expected_creates.first() {
1585 let msg = format!(
1586 "expected {} call by address {} for bytecode {} but not found",
1587 expected_create.create_scheme,
1588 hex::encode_prefixed(expected_create.deployer),
1589 hex::encode_prefixed(&expected_create.bytecode),
1590 );
1591 outcome.result.result = InstructionResult::Revert;
1592 outcome.result.output = Error::encode(msg);
1593 }
1594 }
1595 }
1596
1597 fn create(&mut self, ecx: Ecx, mut input: &mut CreateInputs) -> Option<CreateOutcome> {
1598 if let Some(spec_id) = self.execution_evm_version {
1600 ecx.cfg.spec = spec_id;
1601 }
1602
1603 let gas = Gas::new(input.gas_limit());
1604 if self.intercept_next_create_call {
1606 self.intercept_next_create_call = false;
1608
1609 let output = input.init_code();
1611
1612 return Some(CreateOutcome {
1614 result: InterpreterResult { result: InstructionResult::Revert, output, gas },
1615 address: None,
1616 });
1617 }
1618
1619 let curr_depth = ecx.journaled_state.depth();
1620
1621 if let Some(prank) = &self.get_prank(curr_depth)
1623 && curr_depth >= prank.depth
1624 && input.caller() == prank.prank_caller
1625 {
1626 let mut prank_applied = false;
1627
1628 if curr_depth == prank.depth {
1630 let _ = journaled_account(ecx, prank.new_caller);
1632 input.set_caller(prank.new_caller);
1633 prank_applied = true;
1634 }
1635
1636 if let Some(new_origin) = prank.new_origin {
1638 ecx.tx.caller = new_origin;
1639 prank_applied = true;
1640 }
1641
1642 if prank_applied && let Some(applied_prank) = prank.first_time_applied() {
1644 self.pranks.insert(curr_depth, applied_prank);
1645 }
1646 }
1647
1648 self.apply_accesslist(ecx);
1650
1651 if let Some(broadcast) = &mut self.broadcast
1653 && curr_depth >= broadcast.depth
1654 && input.caller() == broadcast.original_caller
1655 {
1656 if let Err(err) = ecx.journaled_state.load_account(broadcast.new_origin) {
1657 return Some(CreateOutcome {
1658 result: InterpreterResult {
1659 result: InstructionResult::Revert,
1660 output: Error::encode(err),
1661 gas,
1662 },
1663 address: None,
1664 });
1665 }
1666
1667 ecx.tx.caller = broadcast.new_origin;
1668
1669 if curr_depth == broadcast.depth || broadcast.deploy_from_code {
1670 broadcast.deploy_from_code = false;
1672
1673 input.set_caller(broadcast.new_origin);
1674
1675 let account = &ecx.journaled_state.inner.state()[&broadcast.new_origin];
1676 self.broadcastable_transactions.push_back(BroadcastableTransaction {
1677 rpc: ecx.journaled_state.database.active_fork_url(),
1678 transaction: TransactionRequest {
1679 from: Some(broadcast.new_origin),
1680 to: None,
1681 value: Some(input.value()),
1682 input: TransactionInput::new(input.init_code()),
1683 nonce: Some(account.info.nonce),
1684 ..Default::default()
1685 }
1686 .into(),
1687 });
1688
1689 input.log_debug(self, &input.scheme().unwrap_or(CreateScheme::Create));
1690 }
1691 }
1692
1693 let address = input.allow_cheatcodes(self, ecx);
1695
1696 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1698 recorded_account_diffs_stack.push(vec![AccountAccess {
1699 chainInfo: crate::Vm::ChainInfo {
1700 forkId: ecx.journaled_state.db().active_fork_id().unwrap_or_default(),
1701 chainId: U256::from(ecx.cfg.chain_id),
1702 },
1703 accessor: input.caller(),
1704 account: address,
1705 kind: crate::Vm::AccountAccessKind::Create,
1706 initialized: true,
1707 oldBalance: U256::ZERO, newBalance: U256::ZERO, oldNonce: 0, newNonce: 1, value: input.value(),
1712 data: input.init_code(),
1713 reverted: false,
1714 deployedCode: Bytes::new(), storageAccesses: vec![], depth: curr_depth as u64,
1717 }]);
1718 }
1719
1720 None
1721 }
1722
1723 fn create_end(&mut self, ecx: Ecx, call: &CreateInputs, outcome: &mut CreateOutcome) {
1724 let call = Some(call);
1725 let curr_depth = ecx.journaled_state.depth();
1726
1727 if let Some(prank) = &self.get_prank(curr_depth)
1729 && curr_depth == prank.depth
1730 {
1731 ecx.tx.caller = prank.prank_origin;
1732
1733 if prank.single_call {
1735 std::mem::take(&mut self.pranks);
1736 }
1737 }
1738
1739 if let Some(broadcast) = &self.broadcast
1741 && curr_depth == broadcast.depth
1742 {
1743 ecx.tx.caller = broadcast.original_origin;
1744
1745 if broadcast.single_call {
1747 std::mem::take(&mut self.broadcast);
1748 }
1749 }
1750
1751 if let Some(expected_revert) = &self.expected_revert
1753 && curr_depth <= expected_revert.depth
1754 && matches!(expected_revert.kind, ExpectedRevertKind::Default)
1755 {
1756 let mut expected_revert = std::mem::take(&mut self.expected_revert).unwrap();
1757 return match revert_handlers::handle_expect_revert(
1758 false,
1759 true,
1760 self.config.internal_expect_revert,
1761 &expected_revert,
1762 outcome.result.result,
1763 outcome.result.output.clone(),
1764 &self.config.available_artifacts,
1765 ) {
1766 Ok((address, retdata)) => {
1767 expected_revert.actual_count += 1;
1768 if expected_revert.actual_count < expected_revert.count {
1769 self.expected_revert = Some(expected_revert.clone());
1770 }
1771
1772 outcome.result.result = InstructionResult::Return;
1773 outcome.result.output = retdata;
1774 outcome.address = address;
1775 }
1776 Err(err) => {
1777 outcome.result.result = InstructionResult::Revert;
1778 outcome.result.output = err.abi_encode().into();
1779 }
1780 };
1781 }
1782
1783 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1786 if curr_depth > 0
1788 && let Some(last_depth) = &mut recorded_account_diffs_stack.pop()
1789 {
1790 if outcome.result.is_revert() {
1793 last_depth.iter_mut().for_each(|element| {
1794 element.reverted = true;
1795 element
1796 .storageAccesses
1797 .iter_mut()
1798 .for_each(|storage_access| storage_access.reverted = true);
1799 })
1800 }
1801
1802 if let Some(create_access) = last_depth.first_mut() {
1803 let depth = ecx.journaled_state.depth();
1808 if create_access.depth == depth as u64 {
1809 debug_assert_eq!(
1810 create_access.kind as u8,
1811 crate::Vm::AccountAccessKind::Create as u8
1812 );
1813 if let Some(address) = outcome.address
1814 && let Ok(created_acc) = ecx.journaled_state.load_account(address)
1815 {
1816 create_access.newBalance = created_acc.info.balance;
1817 create_access.newNonce = created_acc.info.nonce;
1818 create_access.deployedCode =
1819 created_acc.info.code.clone().unwrap_or_default().original_bytes();
1820 }
1821 }
1822 if let Some(last) = recorded_account_diffs_stack.last_mut() {
1827 last.append(last_depth);
1828 } else {
1829 recorded_account_diffs_stack.push(last_depth.clone());
1830 }
1831 }
1832 }
1833 }
1834
1835 if !self.expected_creates.is_empty()
1837 && let (Some(address), Some(call)) = (outcome.address, call)
1838 && let Ok(created_acc) = ecx.journaled_state.load_account(address)
1839 {
1840 let bytecode = created_acc.info.code.clone().unwrap_or_default().original_bytes();
1841 if let Some((index, _)) =
1842 self.expected_creates.iter().find_position(|expected_create| {
1843 expected_create.deployer == call.caller
1844 && expected_create.create_scheme.eq(call.scheme.into())
1845 && expected_create.bytecode == bytecode
1846 })
1847 {
1848 self.expected_creates.swap_remove(index);
1849 }
1850 }
1851 }
1852}
1853
1854impl InspectorExt for Cheatcodes {
1855 fn should_use_create2_factory(&mut self, ecx: Ecx, inputs: &CreateInputs) -> bool {
1856 if let CreateScheme::Create2 { .. } = inputs.scheme {
1857 let depth = ecx.journaled_state.depth();
1858 let target_depth = if let Some(prank) = &self.get_prank(depth) {
1859 prank.depth
1860 } else if let Some(broadcast) = &self.broadcast {
1861 broadcast.depth
1862 } else {
1863 1
1864 };
1865
1866 depth == target_depth
1867 && (self.broadcast.is_some() || self.config.always_use_create_2_factory)
1868 } else {
1869 false
1870 }
1871 }
1872
1873 fn create2_deployer(&self) -> Address {
1874 self.config.evm_opts.create2_deployer
1875 }
1876}
1877
1878impl Cheatcodes {
1879 #[cold]
1880 fn meter_gas(&mut self, interpreter: &mut Interpreter) {
1881 if let Some(paused_gas) = self.gas_metering.paused_frames.last() {
1882 let memory = *interpreter.gas.memory();
1885 interpreter.gas = *paused_gas;
1886 interpreter.gas.memory_mut().words_num = memory.words_num;
1887 interpreter.gas.memory_mut().expansion_cost = memory.expansion_cost;
1888 } else {
1889 self.gas_metering.paused_frames.push(interpreter.gas);
1891 }
1892 }
1893
1894 #[cold]
1895 fn meter_gas_record(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1896 if interpreter.bytecode.action.as_ref().and_then(|i| i.instruction_result()).is_none() {
1897 self.gas_metering.gas_records.iter_mut().for_each(|record| {
1898 let curr_depth = ecx.journaled_state.depth();
1899 if curr_depth == record.depth {
1900 if self.gas_metering.last_gas_used != 0 {
1903 let gas_diff =
1904 interpreter.gas.spent().saturating_sub(self.gas_metering.last_gas_used);
1905 record.gas_used = record.gas_used.saturating_add(gas_diff);
1906 }
1907
1908 self.gas_metering.last_gas_used = interpreter.gas.spent();
1911 }
1912 });
1913 }
1914 }
1915
1916 #[cold]
1917 fn meter_gas_end(&mut self, interpreter: &mut Interpreter) {
1918 if let Some(interpreter_action) = interpreter.bytecode.action.as_ref()
1920 && will_exit(interpreter_action)
1921 {
1922 self.gas_metering.paused_frames.pop();
1923 }
1924 }
1925
1926 #[cold]
1927 fn meter_gas_reset(&mut self, interpreter: &mut Interpreter) {
1928 interpreter.gas = Gas::new(interpreter.gas.limit());
1929 self.gas_metering.reset = false;
1930 }
1931
1932 #[cold]
1933 fn meter_gas_check(&mut self, interpreter: &mut Interpreter) {
1934 if let Some(interpreter_action) = interpreter.bytecode.action.as_ref()
1935 && will_exit(interpreter_action)
1936 {
1937 if interpreter.gas.spent()
1941 < u64::try_from(interpreter.gas.refunded()).unwrap_or_default()
1942 {
1943 interpreter.gas = Gas::new(interpreter.gas.limit());
1944 }
1945 }
1946 }
1947
1948 #[cold]
1956 fn arbitrary_storage_end(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1957 let (key, target_address) = if interpreter.bytecode.opcode() == op::SLOAD {
1958 (try_or_return!(interpreter.stack.peek(0)), interpreter.input.target_address)
1959 } else {
1960 return;
1961 };
1962
1963 let Some(value) = ecx.sload(target_address, key) else {
1964 return;
1965 };
1966
1967 if (value.is_cold && value.data.is_zero())
1968 || self.should_overwrite_arbitrary_storage(&target_address, key)
1969 {
1970 if self.has_arbitrary_storage(&target_address) {
1971 let arbitrary_value = self.rng().random();
1972 self.arbitrary_storage.as_mut().unwrap().save(
1973 ecx,
1974 target_address,
1975 key,
1976 arbitrary_value,
1977 );
1978 } else if self.is_arbitrary_storage_copy(&target_address) {
1979 let arbitrary_value = self.rng().random();
1980 self.arbitrary_storage.as_mut().unwrap().copy(
1981 ecx,
1982 target_address,
1983 key,
1984 arbitrary_value,
1985 );
1986 }
1987 }
1988 }
1989
1990 #[cold]
1992 fn record_accesses(&mut self, interpreter: &mut Interpreter) {
1993 let access = &mut self.accesses;
1994 match interpreter.bytecode.opcode() {
1995 op::SLOAD => {
1996 let key = try_or_return!(interpreter.stack.peek(0));
1997 access.record_read(interpreter.input.target_address, key);
1998 }
1999 op::SSTORE => {
2000 let key = try_or_return!(interpreter.stack.peek(0));
2001 access.record_write(interpreter.input.target_address, key);
2002 }
2003 _ => {}
2004 }
2005 }
2006
2007 #[cold]
2008 fn record_state_diffs(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
2009 let Some(account_accesses) = &mut self.recorded_account_diffs_stack else { return };
2010 match interpreter.bytecode.opcode() {
2011 op::SELFDESTRUCT => {
2012 let Some(last) = account_accesses.last_mut() else { return };
2014
2015 let target = try_or_return!(interpreter.stack.peek(0));
2017 let target = Address::from_word(B256::from(target));
2018 let (initialized, old_balance, old_nonce) = ecx
2019 .journaled_state
2020 .load_account(target)
2021 .map(|account| {
2022 (account.info.exists(), account.info.balance, account.info.nonce)
2023 })
2024 .unwrap_or_default();
2025
2026 let value = ecx
2028 .balance(interpreter.input.target_address)
2029 .map(|b| b.data)
2030 .unwrap_or(U256::ZERO);
2031
2032 last.push(crate::Vm::AccountAccess {
2034 chainInfo: crate::Vm::ChainInfo {
2035 forkId: ecx.journaled_state.database.active_fork_id().unwrap_or_default(),
2036 chainId: U256::from(ecx.cfg.chain_id),
2037 },
2038 accessor: interpreter.input.target_address,
2039 account: target,
2040 kind: crate::Vm::AccountAccessKind::SelfDestruct,
2041 initialized,
2042 oldBalance: old_balance,
2043 newBalance: old_balance + value,
2044 oldNonce: old_nonce,
2045 newNonce: old_nonce, value,
2047 data: Bytes::new(),
2048 reverted: false,
2049 deployedCode: Bytes::new(),
2050 storageAccesses: vec![],
2051 depth: ecx
2052 .journaled_state
2053 .depth()
2054 .try_into()
2055 .expect("journaled state depth exceeds u64"),
2056 });
2057 }
2058
2059 op::SLOAD => {
2060 let Some(last) = account_accesses.last_mut() else { return };
2061
2062 let key = try_or_return!(interpreter.stack.peek(0));
2063 let address = interpreter.input.target_address;
2064
2065 let mut present_value = U256::ZERO;
2068 if ecx.journaled_state.load_account(address).is_ok()
2070 && let Some(previous) = ecx.sload(address, key)
2071 {
2072 present_value = previous.data;
2073 }
2074 let access = crate::Vm::StorageAccess {
2075 account: interpreter.input.target_address,
2076 slot: key.into(),
2077 isWrite: false,
2078 previousValue: present_value.into(),
2079 newValue: present_value.into(),
2080 reverted: false,
2081 };
2082 let curr_depth = ecx
2083 .journaled_state
2084 .depth()
2085 .try_into()
2086 .expect("journaled state depth exceeds u64");
2087 append_storage_access(last, access, curr_depth);
2088 }
2089 op::SSTORE => {
2090 let Some(last) = account_accesses.last_mut() else { return };
2091
2092 let key = try_or_return!(interpreter.stack.peek(0));
2093 let value = try_or_return!(interpreter.stack.peek(1));
2094 let address = interpreter.input.target_address;
2095 let mut previous_value = U256::ZERO;
2098 if ecx.journaled_state.load_account(address).is_ok()
2099 && let Some(previous) = ecx.sload(address, key)
2100 {
2101 previous_value = previous.data;
2102 }
2103
2104 let access = crate::Vm::StorageAccess {
2105 account: address,
2106 slot: key.into(),
2107 isWrite: true,
2108 previousValue: previous_value.into(),
2109 newValue: value.into(),
2110 reverted: false,
2111 };
2112 let curr_depth = ecx
2113 .journaled_state
2114 .depth()
2115 .try_into()
2116 .expect("journaled state depth exceeds u64");
2117 append_storage_access(last, access, curr_depth);
2118 }
2119
2120 op::EXTCODECOPY | op::EXTCODESIZE | op::EXTCODEHASH | op::BALANCE => {
2122 let kind = match interpreter.bytecode.opcode() {
2123 op::EXTCODECOPY => crate::Vm::AccountAccessKind::Extcodecopy,
2124 op::EXTCODESIZE => crate::Vm::AccountAccessKind::Extcodesize,
2125 op::EXTCODEHASH => crate::Vm::AccountAccessKind::Extcodehash,
2126 op::BALANCE => crate::Vm::AccountAccessKind::Balance,
2127 _ => unreachable!(),
2128 };
2129 let address =
2130 Address::from_word(B256::from(try_or_return!(interpreter.stack.peek(0))));
2131 let initialized;
2132 let balance;
2133 let nonce;
2134 if let Ok(acc) = ecx.journaled_state.load_account(address) {
2135 initialized = acc.info.exists();
2136 balance = acc.info.balance;
2137 nonce = acc.info.nonce;
2138 } else {
2139 initialized = false;
2140 balance = U256::ZERO;
2141 nonce = 0;
2142 }
2143 let curr_depth = ecx
2144 .journaled_state
2145 .depth()
2146 .try_into()
2147 .expect("journaled state depth exceeds u64");
2148 let account_access = crate::Vm::AccountAccess {
2149 chainInfo: crate::Vm::ChainInfo {
2150 forkId: ecx.journaled_state.database.active_fork_id().unwrap_or_default(),
2151 chainId: U256::from(ecx.cfg.chain_id),
2152 },
2153 accessor: interpreter.input.target_address,
2154 account: address,
2155 kind,
2156 initialized,
2157 oldBalance: balance,
2158 newBalance: balance,
2159 oldNonce: nonce,
2160 newNonce: nonce, value: U256::ZERO,
2162 data: Bytes::new(),
2163 reverted: false,
2164 deployedCode: Bytes::new(),
2165 storageAccesses: vec![],
2166 depth: curr_depth,
2167 };
2168 if let Some(last) = account_accesses.last_mut() {
2171 last.push(account_access);
2172 } else {
2173 account_accesses.push(vec![account_access]);
2174 }
2175 }
2176 _ => {}
2177 }
2178 }
2179
2180 #[cold]
2185 fn check_mem_opcodes(&self, interpreter: &mut Interpreter, depth: u64) {
2186 let Some(ranges) = self.allowed_mem_writes.get(&depth) else {
2187 return;
2188 };
2189
2190 macro_rules! mem_opcode_match {
2199 ($(($opcode:ident, $offset_depth:expr, $size_depth:expr, $writes:expr)),* $(,)?) => {
2200 match interpreter.bytecode.opcode() {
2201 op::MSTORE => {
2206 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2208
2209 if !ranges.iter().any(|range| {
2212 range.contains(&offset) && range.contains(&(offset + 31))
2213 }) {
2214 let value = try_or_return!(interpreter.stack.peek(1)).to_be_bytes::<32>();
2219 if value[..SELECTOR_LEN] == stopExpectSafeMemoryCall::SELECTOR {
2220 return
2221 }
2222
2223 disallowed_mem_write(offset, 32, interpreter, ranges);
2224 return
2225 }
2226 }
2227 op::MSTORE8 => {
2228 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2230
2231 if !ranges.iter().any(|range| range.contains(&offset)) {
2234 disallowed_mem_write(offset, 1, interpreter, ranges);
2235 return
2236 }
2237 }
2238
2239 op::MLOAD => {
2244 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2246
2247 if offset >= interpreter.memory.size() as u64 && !ranges.iter().any(|range| {
2251 range.contains(&offset) && range.contains(&(offset + 31))
2252 }) {
2253 disallowed_mem_write(offset, 32, interpreter, ranges);
2254 return
2255 }
2256 }
2257
2258 op::CALL => {
2263 let dest_offset = try_or_return!(interpreter.stack.peek(5)).saturating_to::<u64>();
2265
2266 let size = try_or_return!(interpreter.stack.peek(6)).saturating_to::<u64>();
2268
2269 let fail_cond = !ranges.iter().any(|range| {
2273 range.contains(&dest_offset) &&
2274 range.contains(&(dest_offset + size.saturating_sub(1)))
2275 });
2276
2277 if fail_cond {
2280 let to = Address::from_word(try_or_return!(interpreter.stack.peek(1)).to_be_bytes::<32>().into());
2284 if to == CHEATCODE_ADDRESS {
2285 let args_offset = try_or_return!(interpreter.stack.peek(3)).saturating_to::<usize>();
2286 let args_size = try_or_return!(interpreter.stack.peek(4)).saturating_to::<usize>();
2287 let memory_word = interpreter.memory.slice_len(args_offset, args_size);
2288 if memory_word[..SELECTOR_LEN] == stopExpectSafeMemoryCall::SELECTOR {
2289 return
2290 }
2291 }
2292
2293 disallowed_mem_write(dest_offset, size, interpreter, ranges);
2294 return
2295 }
2296 }
2297
2298 $(op::$opcode => {
2299 let dest_offset = try_or_return!(interpreter.stack.peek($offset_depth)).saturating_to::<u64>();
2301
2302 let size = try_or_return!(interpreter.stack.peek($size_depth)).saturating_to::<u64>();
2304
2305 let fail_cond = !ranges.iter().any(|range| {
2309 range.contains(&dest_offset) &&
2310 range.contains(&(dest_offset + size.saturating_sub(1)))
2311 }) && ($writes ||
2312 [dest_offset, (dest_offset + size).saturating_sub(1)].into_iter().any(|offset| {
2313 offset >= interpreter.memory.size() as u64
2314 })
2315 );
2316
2317 if fail_cond {
2320 disallowed_mem_write(dest_offset, size, interpreter, ranges);
2321 return
2322 }
2323 })*
2324
2325 _ => {}
2326 }
2327 }
2328 }
2329
2330 mem_opcode_match!(
2333 (CALLDATACOPY, 0, 2, true),
2334 (CODECOPY, 0, 2, true),
2335 (RETURNDATACOPY, 0, 2, true),
2336 (EXTCODECOPY, 1, 3, true),
2337 (CALLCODE, 5, 6, true),
2338 (STATICCALL, 4, 5, true),
2339 (DELEGATECALL, 4, 5, true),
2340 (KECCAK256, 0, 1, false),
2341 (LOG0, 0, 1, false),
2342 (LOG1, 0, 1, false),
2343 (LOG2, 0, 1, false),
2344 (LOG3, 0, 1, false),
2345 (LOG4, 0, 1, false),
2346 (CREATE, 1, 2, false),
2347 (CREATE2, 1, 2, false),
2348 (RETURN, 0, 1, false),
2349 (REVERT, 0, 1, false),
2350 );
2351 }
2352
2353 #[cold]
2354 fn set_gas_limit_type(&mut self, interpreter: &mut Interpreter) {
2355 match interpreter.bytecode.opcode() {
2356 op::CREATE2 => self.dynamic_gas_limit = true,
2357 op::CALL => {
2358 self.dynamic_gas_limit =
2361 try_or_return!(interpreter.stack.peek(0)) >= interpreter.gas.remaining() - 100
2362 }
2363 _ => self.dynamic_gas_limit = false,
2364 }
2365 }
2366}
2367
2368fn disallowed_mem_write(
2374 dest_offset: u64,
2375 size: u64,
2376 interpreter: &mut Interpreter,
2377 ranges: &[Range<u64>],
2378) {
2379 let revert_string = format!(
2380 "memory write at offset 0x{:02X} of size 0x{:02X} not allowed; safe range: {}",
2381 dest_offset,
2382 size,
2383 ranges.iter().map(|r| format!("(0x{:02X}, 0x{:02X}]", r.start, r.end)).join(" U ")
2384 );
2385
2386 interpreter.bytecode.set_action(InterpreterAction::new_return(
2387 InstructionResult::Revert,
2388 Bytes::from(revert_string.into_bytes()),
2389 interpreter.gas,
2390 ));
2391}
2392
2393fn access_is_call(kind: crate::Vm::AccountAccessKind) -> bool {
2395 matches!(
2396 kind,
2397 crate::Vm::AccountAccessKind::Call
2398 | crate::Vm::AccountAccessKind::StaticCall
2399 | crate::Vm::AccountAccessKind::CallCode
2400 | crate::Vm::AccountAccessKind::DelegateCall
2401 )
2402}
2403
2404fn append_storage_access(
2406 last: &mut Vec<AccountAccess>,
2407 storage_access: crate::Vm::StorageAccess,
2408 storage_depth: u64,
2409) {
2410 if !last.is_empty() && last.first().unwrap().depth < storage_depth {
2412 if last.len() == 1 {
2418 last.first_mut().unwrap().storageAccesses.push(storage_access);
2419 } else {
2420 let last_record = last.last_mut().unwrap();
2421 if last_record.kind as u8 == crate::Vm::AccountAccessKind::Resume as u8 {
2422 last_record.storageAccesses.push(storage_access);
2423 } else {
2424 let entry = last.first().unwrap();
2425 let resume_record = crate::Vm::AccountAccess {
2426 chainInfo: crate::Vm::ChainInfo {
2427 forkId: entry.chainInfo.forkId,
2428 chainId: entry.chainInfo.chainId,
2429 },
2430 accessor: entry.accessor,
2431 account: entry.account,
2432 kind: crate::Vm::AccountAccessKind::Resume,
2433 initialized: entry.initialized,
2434 storageAccesses: vec![storage_access],
2435 reverted: entry.reverted,
2436 oldBalance: U256::ZERO,
2438 newBalance: U256::ZERO,
2439 oldNonce: 0,
2440 newNonce: 0,
2441 value: U256::ZERO,
2442 data: Bytes::new(),
2443 deployedCode: Bytes::new(),
2444 depth: entry.depth,
2445 };
2446 last.push(resume_record);
2447 }
2448 }
2449 }
2450}
2451
2452fn apply_dispatch(
2454 calls: &Vm::VmCalls,
2455 ccx: &mut CheatsCtxt,
2456 executor: &mut dyn CheatcodesExecutor,
2457) -> Result {
2458 let cheat = calls_as_dyn_cheatcode(calls);
2459
2460 let _guard = debug_span!(target: "cheatcodes", "apply", id = %cheat.id()).entered();
2461 trace!(target: "cheatcodes", ?cheat, "applying");
2462
2463 if let spec::Status::Deprecated(replacement) = *cheat.status() {
2464 ccx.state.deprecated.insert(cheat.signature(), replacement);
2465 }
2466
2467 let mut result = cheat.dyn_apply(ccx, executor);
2469
2470 if let Err(e) = &mut result
2472 && e.is_str()
2473 {
2474 let name = cheat.name();
2475 if !name.contains("assert") && name != "rpcUrl" {
2479 *e = fmt_err!("vm.{name}: {e}");
2480 }
2481 }
2482
2483 trace!(
2484 target: "cheatcodes",
2485 return = %match &result {
2486 Ok(b) => hex::encode(b),
2487 Err(e) => e.to_string(),
2488 }
2489 );
2490
2491 result
2492}
2493
2494fn calls_as_dyn_cheatcode(calls: &Vm::VmCalls) -> &dyn DynCheatcode {
2495 macro_rules! as_dyn {
2496 ($($variant:ident),*) => {
2497 match calls {
2498 $(Vm::VmCalls::$variant(cheat) => cheat,)*
2499 }
2500 };
2501 }
2502 vm_calls!(as_dyn)
2503}
2504
2505fn will_exit(action: &InterpreterAction) -> bool {
2507 match action {
2508 InterpreterAction::Return(result) => {
2509 result.result.is_ok_or_revert() || result.result.is_error()
2510 }
2511 _ => false,
2512 }
2513}