1use crate::{
4 CheatsConfig, CheatsCtxt, DynCheatcode, Error, Result,
5 Vm::{self, AccountAccess},
6 evm::{
7 DealRecord, GasRecord, RecordAccess,
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 state::EvmStorageSlot,
65};
66use serde_json::Value;
67use std::{
68 cmp::max,
69 collections::{BTreeMap, VecDeque},
70 fs::File,
71 io::BufReader,
72 ops::Range,
73 path::PathBuf,
74 sync::{Arc, OnceLock},
75};
76
77mod utils;
78
79pub mod analysis;
80pub use analysis::CheatcodeAnalysis;
81
82pub type Ecx<'a, 'b, 'c> = &'a mut EthEvmContext<&'b mut (dyn DatabaseExt + 'c)>;
83
84pub trait CheatcodesExecutor {
90 fn get_inspector<'a>(&'a mut self, cheats: &'a mut Cheatcodes) -> Box<dyn InspectorExt + 'a>;
93
94 fn exec_create(
96 &mut self,
97 inputs: CreateInputs,
98 ccx: &mut CheatsCtxt,
99 ) -> Result<CreateOutcome, EVMError<DatabaseError>> {
100 with_evm(self, ccx, |evm| {
101 evm.inner.ctx.journaled_state.depth += 1;
102
103 let frame = FrameInput::Create(Box::new(inputs));
104
105 let outcome = match evm.run_execution(frame)? {
106 FrameResult::Call(_) => unreachable!(),
107 FrameResult::Create(create) => create,
108 };
109
110 evm.inner.ctx.journaled_state.depth -= 1;
111
112 Ok(outcome)
113 })
114 }
115
116 fn console_log(&mut self, ccx: &mut CheatsCtxt, msg: &str) {
117 self.get_inspector(ccx.state).console_log(msg);
118 }
119
120 fn tracing_inspector(&mut self) -> Option<&mut TracingInspector> {
122 None
123 }
124}
125
126fn with_evm<E, F, O>(
128 executor: &mut E,
129 ccx: &mut CheatsCtxt,
130 f: F,
131) -> Result<O, EVMError<DatabaseError>>
132where
133 E: CheatcodesExecutor + ?Sized,
134 F: for<'a, 'b> FnOnce(
135 &mut FoundryEvm<'a, &'b mut dyn InspectorExt>,
136 ) -> Result<O, EVMError<DatabaseError>>,
137{
138 let mut inspector = executor.get_inspector(ccx.state);
139 let error = std::mem::replace(&mut ccx.ecx.error, Ok(()));
140
141 let ctx = EthEvmContext {
142 block: ccx.ecx.block.clone(),
143 cfg: ccx.ecx.cfg.clone(),
144 tx: ccx.ecx.tx.clone(),
145 journaled_state: Journal {
146 inner: ccx.ecx.journaled_state.inner.clone(),
147 database: &mut *ccx.ecx.journaled_state.database as &mut dyn DatabaseExt,
148 },
149 local: LocalContext::default(),
150 chain: (),
151 error,
152 };
153
154 let mut evm = new_evm_with_existing_context(ctx, &mut *inspector);
155
156 let res = f(&mut evm)?;
157
158 ccx.ecx.journaled_state.inner = evm.inner.ctx.journaled_state.inner;
159 ccx.ecx.block = evm.inner.ctx.block;
160 ccx.ecx.tx = evm.inner.ctx.tx;
161 ccx.ecx.cfg = evm.inner.ctx.cfg;
162 ccx.ecx.error = evm.inner.ctx.error;
163
164 Ok(res)
165}
166
167#[derive(Debug, Default, Clone, Copy)]
170struct TransparentCheatcodesExecutor;
171
172impl CheatcodesExecutor for TransparentCheatcodesExecutor {
173 fn get_inspector<'a>(&'a mut self, cheats: &'a mut Cheatcodes) -> Box<dyn InspectorExt + 'a> {
174 Box::new(cheats)
175 }
176}
177
178macro_rules! try_or_return {
179 ($e:expr) => {
180 match $e {
181 Ok(v) => v,
182 Err(_) => return,
183 }
184 };
185}
186
187#[derive(Debug, Default)]
189pub struct TestContext {
190 pub opened_read_files: HashMap<PathBuf, BufReader<File>>,
192}
193
194impl Clone for TestContext {
196 fn clone(&self) -> Self {
197 Default::default()
198 }
199}
200
201impl TestContext {
202 pub fn clear(&mut self) {
204 self.opened_read_files.clear();
205 }
206}
207
208#[derive(Clone, Debug)]
210pub struct BroadcastableTransaction {
211 pub rpc: Option<String>,
213 pub transaction: TransactionMaybeSigned,
215}
216
217#[derive(Clone, Debug, Copy)]
218pub struct RecordDebugStepInfo {
219 pub start_node_idx: usize,
221 pub original_tracer_config: TracingInspectorConfig,
223}
224
225#[derive(Clone, Debug, Default)]
227pub struct GasMetering {
228 pub paused: bool,
230 pub touched: bool,
233 pub reset: bool,
235 pub paused_frames: Vec<Gas>,
237
238 pub active_gas_snapshot: Option<(String, String)>,
240
241 pub last_call_gas: Option<crate::Vm::Gas>,
244
245 pub recording: bool,
247 pub last_gas_used: u64,
249 pub gas_records: Vec<GasRecord>,
251}
252
253impl GasMetering {
254 pub fn start(&mut self) {
256 self.recording = true;
257 }
258
259 pub fn stop(&mut self) {
261 self.recording = false;
262 }
263
264 pub fn resume(&mut self) {
266 if self.paused {
267 self.paused = false;
268 self.touched = true;
269 }
270 self.paused_frames.clear();
271 }
272
273 pub fn reset(&mut self) {
275 self.paused = false;
276 self.touched = true;
277 self.reset = true;
278 self.paused_frames.clear();
279 }
280}
281
282#[derive(Clone, Debug, Default)]
284pub struct ArbitraryStorage {
285 pub values: HashMap<Address, HashMap<U256, U256>>,
289 pub copies: HashMap<Address, Address>,
291 pub overwrites: HashSet<Address>,
293}
294
295impl ArbitraryStorage {
296 pub fn mark_arbitrary(&mut self, address: &Address, overwrite: bool) {
298 self.values.insert(*address, HashMap::default());
299 if overwrite {
300 self.overwrites.insert(*address);
301 } else {
302 self.overwrites.remove(address);
303 }
304 }
305
306 pub fn mark_copy(&mut self, from: &Address, to: &Address) {
308 if self.values.contains_key(from) {
309 self.copies.insert(*to, *from);
310 }
311 }
312
313 pub fn save(&mut self, ecx: Ecx, address: Address, slot: U256, data: U256) {
317 self.values.get_mut(&address).expect("missing arbitrary address entry").insert(slot, data);
318 if let Ok(mut account) = ecx.journaled_state.load_account(address) {
319 account.storage.insert(slot, EvmStorageSlot::new(data, 0));
320 }
321 }
322
323 pub fn copy(&mut self, ecx: Ecx, target: Address, slot: U256, new_value: U256) -> U256 {
329 let source = self.copies.get(&target).expect("missing arbitrary copy target entry");
330 let storage_cache = self.values.get_mut(source).expect("missing arbitrary source storage");
331 let value = match storage_cache.get(&slot) {
332 Some(value) => *value,
333 None => {
334 storage_cache.insert(slot, new_value);
335 if let Ok(mut source_account) = ecx.journaled_state.load_account(*source) {
337 source_account.storage.insert(slot, EvmStorageSlot::new(new_value, 0));
338 }
339 new_value
340 }
341 };
342 if let Ok(mut target_account) = ecx.journaled_state.load_account(target) {
344 target_account.storage.insert(slot, EvmStorageSlot::new(value, 0));
345 }
346 value
347 }
348}
349
350pub type BroadcastableTransactions = VecDeque<BroadcastableTransaction>;
352
353#[derive(Clone, Debug)]
371pub struct Cheatcodes {
372 pub analysis: Option<CheatcodeAnalysis>,
374
375 pub block: Option<BlockEnv>,
380
381 pub active_delegations: Vec<SignedAuthorization>,
385
386 pub active_blob_sidecar: Option<BlobTransactionSidecar>,
388
389 pub gas_price: Option<u128>,
394
395 pub labels: AddressHashMap<String>,
397
398 pub pranks: BTreeMap<usize, Prank>,
400
401 pub expected_revert: Option<ExpectedRevert>,
403
404 pub assume_no_revert: Option<AssumeNoRevert>,
406
407 pub fork_revert_diagnostic: Option<RevertDiagnostic>,
409
410 pub accesses: RecordAccess,
412
413 pub recording_accesses: bool,
415
416 pub recorded_account_diffs_stack: Option<Vec<Vec<AccountAccess>>>,
422
423 pub record_debug_steps_info: Option<RecordDebugStepInfo>,
425
426 pub recorded_logs: Option<Vec<crate::Vm::Log>>,
428
429 pub mocked_calls: HashMap<Address, BTreeMap<MockCallDataContext, VecDeque<MockCallReturnData>>>,
432
433 pub mocked_functions: HashMap<Address, HashMap<Bytes, Address>>,
435
436 pub expected_calls: ExpectedCallTracker,
438 pub expected_emits: ExpectedEmitTracker,
440 pub expected_creates: Vec<ExpectedCreate>,
442
443 pub allowed_mem_writes: HashMap<u64, Vec<Range<u64>>>,
445
446 pub broadcast: Option<Broadcast>,
448
449 pub broadcastable_transactions: BroadcastableTransactions,
451
452 pub access_list: Option<AccessList>,
454
455 pub config: Arc<CheatsConfig>,
457
458 pub test_context: TestContext,
460
461 pub fs_commit: bool,
464
465 pub serialized_jsons: BTreeMap<String, BTreeMap<String, Value>>,
468
469 pub eth_deals: Vec<DealRecord>,
471
472 pub gas_metering: GasMetering,
474
475 pub gas_snapshots: BTreeMap<String, BTreeMap<String, String>>,
478
479 pub mapping_slots: Option<AddressHashMap<MappingSlots>>,
481
482 pub pc: usize,
484 pub breakpoints: Breakpoints,
487
488 pub intercept_next_create_call: bool,
490
491 test_runner: Option<TestRunner>,
494
495 pub ignored_traces: IgnoredTraces,
497
498 pub arbitrary_storage: Option<ArbitraryStorage>,
500
501 pub deprecated: HashMap<&'static str, Option<&'static str>>,
503 pub wallets: Option<Wallets>,
505 signatures_identifier: OnceLock<Option<SignaturesIdentifier>>,
507 pub dynamic_gas_limit_sequence: Option<(bool, bool)>,
512}
513
514impl Default for Cheatcodes {
518 fn default() -> Self {
519 Self::new(Arc::default())
520 }
521}
522
523impl Cheatcodes {
524 pub fn new(config: Arc<CheatsConfig>) -> Self {
526 Self {
527 analysis: None,
528 fs_commit: true,
529 labels: config.labels.clone(),
530 config,
531 block: Default::default(),
532 active_delegations: Default::default(),
533 active_blob_sidecar: Default::default(),
534 gas_price: Default::default(),
535 pranks: Default::default(),
536 expected_revert: Default::default(),
537 assume_no_revert: Default::default(),
538 fork_revert_diagnostic: Default::default(),
539 accesses: Default::default(),
540 recording_accesses: Default::default(),
541 recorded_account_diffs_stack: Default::default(),
542 recorded_logs: Default::default(),
543 record_debug_steps_info: Default::default(),
544 mocked_calls: Default::default(),
545 mocked_functions: Default::default(),
546 expected_calls: Default::default(),
547 expected_emits: Default::default(),
548 expected_creates: Default::default(),
549 allowed_mem_writes: Default::default(),
550 broadcast: Default::default(),
551 broadcastable_transactions: Default::default(),
552 access_list: Default::default(),
553 test_context: Default::default(),
554 serialized_jsons: Default::default(),
555 eth_deals: Default::default(),
556 gas_metering: Default::default(),
557 gas_snapshots: Default::default(),
558 mapping_slots: Default::default(),
559 pc: Default::default(),
560 breakpoints: Default::default(),
561 intercept_next_create_call: Default::default(),
562 test_runner: Default::default(),
563 ignored_traces: Default::default(),
564 arbitrary_storage: Default::default(),
565 deprecated: Default::default(),
566 wallets: Default::default(),
567 signatures_identifier: Default::default(),
568 dynamic_gas_limit_sequence: Default::default(),
569 }
570 }
571
572 pub fn set_analysis(&mut self, analysis: CheatcodeAnalysis) {
574 self.analysis = Some(analysis);
575 }
576
577 pub fn get_prank(&self, depth: usize) -> Option<&Prank> {
581 self.pranks.range(..=depth).last().map(|(_, prank)| prank)
582 }
583
584 pub fn wallets(&mut self) -> &Wallets {
586 self.wallets.get_or_insert_with(|| Wallets::new(MultiWallet::default(), None))
587 }
588
589 pub fn set_wallets(&mut self, wallets: Wallets) {
591 self.wallets = Some(wallets);
592 }
593
594 pub fn add_delegation(&mut self, authorization: SignedAuthorization) {
596 self.active_delegations.push(authorization);
597 }
598
599 pub fn signatures_identifier(&self) -> Option<&SignaturesIdentifier> {
601 self.signatures_identifier.get_or_init(|| SignaturesIdentifier::new(true).ok()).as_ref()
602 }
603
604 fn apply_cheatcode(
606 &mut self,
607 ecx: Ecx,
608 call: &CallInputs,
609 executor: &mut dyn CheatcodesExecutor,
610 ) -> Result {
611 let decoded = Vm::VmCalls::abi_decode(&call.input.bytes(ecx)).map_err(|e| {
613 if let alloy_sol_types::Error::UnknownSelector { name: _, selector } = e {
614 let msg = format!(
615 "unknown cheatcode with selector {selector}; \
616 you may have a mismatch between the `Vm` interface (likely in `forge-std`) \
617 and the `forge` version"
618 );
619 return alloy_sol_types::Error::Other(std::borrow::Cow::Owned(msg));
620 }
621 e
622 })?;
623
624 let caller = call.caller;
625
626 ecx.journaled_state.database.ensure_cheatcode_access_forking_mode(&caller)?;
629
630 apply_dispatch(
631 &decoded,
632 &mut CheatsCtxt { state: self, ecx, gas_limit: call.gas_limit, caller },
633 executor,
634 )
635 }
636
637 fn allow_cheatcodes_on_create(&self, ecx: Ecx, caller: Address, created_address: Address) {
643 if ecx.journaled_state.depth <= 1
644 || ecx.journaled_state.database.has_cheatcode_access(&caller)
645 {
646 ecx.journaled_state.database.allow_cheatcode_access(created_address);
647 }
648 }
649
650 fn apply_accesslist(&mut self, ecx: Ecx) {
656 if let Some(access_list) = &self.access_list {
657 ecx.tx.access_list = access_list.clone();
658
659 if ecx.tx.tx_type == TransactionType::Legacy as u8 {
660 ecx.tx.tx_type = TransactionType::Eip2930 as u8;
661 }
662 }
663 }
664
665 pub fn on_revert(&mut self, ecx: Ecx) {
670 trace!(deals=?self.eth_deals.len(), "rolling back deals");
671
672 if self.expected_revert.is_some() {
674 return;
675 }
676
677 if ecx.journaled_state.depth() > 0 {
679 return;
680 }
681
682 while let Some(record) = self.eth_deals.pop() {
686 if let Some(acc) = ecx.journaled_state.state.get_mut(&record.address) {
687 acc.info.balance = record.old_balance;
688 }
689 }
690 }
691
692 pub fn call_with_executor(
693 &mut self,
694 ecx: Ecx,
695 call: &mut CallInputs,
696 executor: &mut dyn CheatcodesExecutor,
697 ) -> Option<CallOutcome> {
698 let gas = Gas::new(call.gas_limit);
699 let curr_depth = ecx.journaled_state.depth();
700
701 if curr_depth == 0 {
705 let sender = ecx.tx.caller;
706 let account = match super::evm::journaled_account(ecx, sender) {
707 Ok(account) => account,
708 Err(err) => {
709 return Some(CallOutcome {
710 result: InterpreterResult {
711 result: InstructionResult::Revert,
712 output: err.abi_encode().into(),
713 gas,
714 },
715 memory_offset: call.return_memory_offset.clone(),
716 });
717 }
718 };
719 let prev = account.info.nonce;
720 account.info.nonce = prev.saturating_sub(1);
721
722 trace!(target: "cheatcodes", %sender, nonce=account.info.nonce, prev, "corrected nonce");
723 }
724
725 if call.target_address == CHEATCODE_ADDRESS {
726 return match self.apply_cheatcode(ecx, call, executor) {
727 Ok(retdata) => Some(CallOutcome {
728 result: InterpreterResult {
729 result: InstructionResult::Return,
730 output: retdata.into(),
731 gas,
732 },
733 memory_offset: call.return_memory_offset.clone(),
734 }),
735 Err(err) => Some(CallOutcome {
736 result: InterpreterResult {
737 result: InstructionResult::Revert,
738 output: err.abi_encode().into(),
739 gas,
740 },
741 memory_offset: call.return_memory_offset.clone(),
742 }),
743 };
744 }
745
746 if call.target_address == HARDHAT_CONSOLE_ADDRESS {
747 return None;
748 }
749
750 if let Some(expected_calls_for_target) = self.expected_calls.get_mut(&call.bytecode_address)
754 {
755 for (calldata, (expected, actual_count)) in expected_calls_for_target {
757 if calldata.len() <= call.input.len() &&
760 *calldata == call.input.bytes(ecx)[..calldata.len()] &&
762 expected
764 .value.is_none_or(|value| Some(value) == call.transfer_value()) &&
765 expected.gas.is_none_or(|gas| gas == call.gas_limit) &&
767 expected.min_gas.is_none_or(|min_gas| min_gas <= call.gas_limit)
769 {
770 *actual_count += 1;
771 }
772 }
773 }
774
775 if let Some(mocks) = self.mocked_calls.get_mut(&call.bytecode_address) {
777 let ctx = MockCallDataContext {
778 calldata: call.input.bytes(ecx),
779 value: call.transfer_value(),
780 };
781
782 if let Some(return_data_queue) = match mocks.get_mut(&ctx) {
783 Some(queue) => Some(queue),
784 None => mocks
785 .iter_mut()
786 .find(|(mock, _)| {
787 call.input.bytes(ecx).get(..mock.calldata.len()) == Some(&mock.calldata[..])
788 && mock.value.is_none_or(|value| Some(value) == call.transfer_value())
789 })
790 .map(|(_, v)| v),
791 } && let Some(return_data) = if return_data_queue.len() == 1 {
792 return_data_queue.front().map(|x| x.to_owned())
794 } else {
795 return_data_queue.pop_front()
797 } {
798 return Some(CallOutcome {
799 result: InterpreterResult {
800 result: return_data.ret_type,
801 output: return_data.data,
802 gas,
803 },
804 memory_offset: call.return_memory_offset.clone(),
805 });
806 }
807 }
808
809 if let Some(prank) = &self.get_prank(curr_depth) {
811 if prank.delegate_call
813 && curr_depth == prank.depth
814 && let CallScheme::DelegateCall = call.scheme
815 {
816 call.target_address = prank.new_caller;
817 call.caller = prank.new_caller;
818 if let Some(new_origin) = prank.new_origin {
819 ecx.tx.caller = new_origin;
820 }
821 }
822
823 if curr_depth >= prank.depth && call.caller == prank.prank_caller {
824 let mut prank_applied = false;
825
826 if curr_depth == prank.depth {
828 call.caller = prank.new_caller;
829 prank_applied = true;
830 }
831
832 if let Some(new_origin) = prank.new_origin {
834 ecx.tx.caller = new_origin;
835 prank_applied = true;
836 }
837
838 if prank_applied && let Some(applied_prank) = prank.first_time_applied() {
840 self.pranks.insert(curr_depth, applied_prank);
841 }
842 }
843 }
844
845 self.apply_accesslist(ecx);
847
848 if let Some(broadcast) = &self.broadcast {
850 if curr_depth == broadcast.depth && call.caller == broadcast.original_caller {
855 ecx.tx.caller = broadcast.new_origin;
859
860 call.caller = broadcast.new_origin;
861 if !call.is_static {
866 if let Err(err) = ecx.journaled_state.load_account(broadcast.new_origin) {
867 return Some(CallOutcome {
868 result: InterpreterResult {
869 result: InstructionResult::Revert,
870 output: Error::encode(err),
871 gas,
872 },
873 memory_offset: call.return_memory_offset.clone(),
874 });
875 }
876
877 let (gas_seen, call_seen) =
878 self.dynamic_gas_limit_sequence.take().unwrap_or_default();
879 let mut is_fixed_gas_limit = !(gas_seen && call_seen);
881 if call.gas_limit < 21_000 {
884 is_fixed_gas_limit = false;
885 }
886 let input = TransactionInput::new(call.input.bytes(ecx));
887 ecx.journaled_state.touch(broadcast.new_origin);
889
890 let account =
891 ecx.journaled_state.inner.state().get_mut(&broadcast.new_origin).unwrap();
892
893 let mut tx_req = TransactionRequest {
894 from: Some(broadcast.new_origin),
895 to: Some(TxKind::from(Some(call.target_address))),
896 value: call.transfer_value(),
897 input,
898 nonce: Some(account.info.nonce),
899 chain_id: Some(ecx.cfg.chain_id),
900 gas: if is_fixed_gas_limit { Some(call.gas_limit) } else { None },
901 ..Default::default()
902 };
903
904 let active_delegations = std::mem::take(&mut self.active_delegations);
905 if let Some(blob_sidecar) = self.active_blob_sidecar.take() {
907 if !active_delegations.is_empty() {
909 let msg = "both delegation and blob are active; `attachBlob` and `attachDelegation` are not compatible";
910 return Some(CallOutcome {
911 result: InterpreterResult {
912 result: InstructionResult::Revert,
913 output: Error::encode(msg),
914 gas,
915 },
916 memory_offset: call.return_memory_offset.clone(),
917 });
918 }
919 tx_req.set_blob_sidecar(blob_sidecar);
920 }
921
922 if !active_delegations.is_empty() {
924 for auth in &active_delegations {
925 let Ok(authority) = auth.recover_authority() else {
926 continue;
927 };
928 if authority == broadcast.new_origin {
929 account.info.nonce += 1;
932 }
933 }
934 tx_req.authorization_list = Some(active_delegations);
935 }
936
937 self.broadcastable_transactions.push_back(BroadcastableTransaction {
938 rpc: ecx.journaled_state.database.active_fork_url(),
939 transaction: tx_req.into(),
940 });
941 debug!(target: "cheatcodes", tx=?self.broadcastable_transactions.back().unwrap(), "broadcastable call");
942
943 if !self.config.evm_opts.isolate {
945 let prev = account.info.nonce;
946 account.info.nonce += 1;
947 debug!(target: "cheatcodes", address=%broadcast.new_origin, nonce=prev+1, prev, "incremented nonce");
948 }
949 } else if broadcast.single_call {
950 let msg = "`staticcall`s are not allowed after `broadcast`; use `startBroadcast` instead";
951 return Some(CallOutcome {
952 result: InterpreterResult {
953 result: InstructionResult::Revert,
954 output: Error::encode(msg),
955 gas,
956 },
957 memory_offset: call.return_memory_offset.clone(),
958 });
959 }
960 }
961 }
962
963 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
965 let initialized;
968 let old_balance;
969 let old_nonce;
970 if let Ok(acc) = ecx.journaled_state.load_account(call.target_address) {
971 initialized = acc.info.exists();
972 old_balance = acc.info.balance;
973 old_nonce = acc.info.nonce;
974 } else {
975 initialized = false;
976 old_balance = U256::ZERO;
977 old_nonce = 0;
978 }
979 let kind = match call.scheme {
980 CallScheme::Call => crate::Vm::AccountAccessKind::Call,
981 CallScheme::CallCode => crate::Vm::AccountAccessKind::CallCode,
982 CallScheme::DelegateCall => crate::Vm::AccountAccessKind::DelegateCall,
983 CallScheme::StaticCall => crate::Vm::AccountAccessKind::StaticCall,
984 };
985 recorded_account_diffs_stack.push(vec![AccountAccess {
991 chainInfo: crate::Vm::ChainInfo {
992 forkId: ecx.journaled_state.db().active_fork_id().unwrap_or_default(),
993 chainId: U256::from(ecx.cfg.chain_id),
994 },
995 accessor: call.caller,
996 account: call.bytecode_address,
997 kind,
998 initialized,
999 oldBalance: old_balance,
1000 newBalance: U256::ZERO, oldNonce: old_nonce,
1002 newNonce: 0, value: call.call_value(),
1004 data: call.input.bytes(ecx),
1005 reverted: false,
1006 deployedCode: Bytes::new(),
1007 storageAccesses: vec![], depth: ecx
1009 .journaled_state
1010 .depth()
1011 .try_into()
1012 .expect("journaled state depth exceeds u64"),
1013 }]);
1014 }
1015
1016 None
1017 }
1018
1019 pub fn rng(&mut self) -> &mut impl Rng {
1020 self.test_runner().rng()
1021 }
1022
1023 pub fn test_runner(&mut self) -> &mut TestRunner {
1024 self.test_runner.get_or_insert_with(|| match self.config.seed {
1025 Some(seed) => TestRunner::new_with_rng(
1026 proptest::test_runner::Config::default(),
1027 TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>()),
1028 ),
1029 None => TestRunner::new(proptest::test_runner::Config::default()),
1030 })
1031 }
1032
1033 pub fn set_seed(&mut self, seed: U256) {
1034 self.test_runner = Some(TestRunner::new_with_rng(
1035 proptest::test_runner::Config::default(),
1036 TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>()),
1037 ));
1038 }
1039
1040 pub fn arbitrary_storage(&mut self) -> &mut ArbitraryStorage {
1043 self.arbitrary_storage.get_or_insert_with(ArbitraryStorage::default)
1044 }
1045
1046 pub fn has_arbitrary_storage(&self, address: &Address) -> bool {
1048 match &self.arbitrary_storage {
1049 Some(storage) => storage.values.contains_key(address),
1050 None => false,
1051 }
1052 }
1053
1054 pub fn should_overwrite_arbitrary_storage(
1058 &self,
1059 address: &Address,
1060 storage_slot: U256,
1061 ) -> bool {
1062 match &self.arbitrary_storage {
1063 Some(storage) => {
1064 storage.overwrites.contains(address)
1065 && storage
1066 .values
1067 .get(address)
1068 .and_then(|arbitrary_values| arbitrary_values.get(&storage_slot))
1069 .is_none()
1070 }
1071 None => false,
1072 }
1073 }
1074
1075 pub fn is_arbitrary_storage_copy(&self, address: &Address) -> bool {
1077 match &self.arbitrary_storage {
1078 Some(storage) => storage.copies.contains_key(address),
1079 None => false,
1080 }
1081 }
1082
1083 pub fn struct_defs(&self) -> Option<&foundry_common::fmt::StructDefinitions> {
1085 self.analysis.as_ref().and_then(|analysis| analysis.struct_defs().ok())
1086 }
1087}
1088
1089impl Inspector<EthEvmContext<&mut dyn DatabaseExt>> for Cheatcodes {
1090 fn initialize_interp(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1091 if let Some(block) = self.block.take() {
1094 ecx.block = block;
1095 }
1096 if let Some(gas_price) = self.gas_price.take() {
1097 ecx.tx.gas_price = gas_price;
1098 }
1099
1100 if self.gas_metering.paused {
1102 self.gas_metering.paused_frames.push(interpreter.gas);
1103 }
1104
1105 if let Some(expected) = &mut self.expected_revert {
1107 expected.max_depth = max(ecx.journaled_state.depth(), expected.max_depth);
1108 }
1109 }
1110
1111 fn step(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1112 self.pc = interpreter.bytecode.pc();
1113
1114 if self.broadcast.is_some() {
1115 self.record_gas_limit_opcode(interpreter);
1116 }
1117
1118 if self.gas_metering.paused {
1120 self.meter_gas(interpreter);
1121 }
1122
1123 if self.gas_metering.reset {
1125 self.meter_gas_reset(interpreter);
1126 }
1127
1128 if self.recording_accesses {
1130 self.record_accesses(interpreter);
1131 }
1132
1133 if self.recorded_account_diffs_stack.is_some() {
1135 self.record_state_diffs(interpreter, ecx);
1136 }
1137
1138 if !self.allowed_mem_writes.is_empty() {
1140 self.check_mem_opcodes(
1141 interpreter,
1142 ecx.journaled_state.depth().try_into().expect("journaled state depth exceeds u64"),
1143 );
1144 }
1145
1146 if let Some(mapping_slots) = &mut self.mapping_slots {
1148 mapping_step(mapping_slots, interpreter);
1149 }
1150
1151 if self.gas_metering.recording {
1153 self.meter_gas_record(interpreter, ecx);
1154 }
1155 }
1156
1157 fn step_end(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1158 if self.broadcast.is_some() {
1159 self.set_gas_limit_type(interpreter);
1160 }
1161
1162 if self.gas_metering.paused {
1163 self.meter_gas_end(interpreter);
1164 }
1165
1166 if self.gas_metering.touched {
1167 self.meter_gas_check(interpreter);
1168 }
1169
1170 if self.arbitrary_storage.is_some() {
1172 self.arbitrary_storage_end(interpreter, ecx);
1173 }
1174 }
1175
1176 fn log(&mut self, interpreter: &mut Interpreter, _ecx: Ecx, log: Log) {
1177 if !self.expected_emits.is_empty() {
1178 expect::handle_expect_emit(self, &log, interpreter);
1179 }
1180
1181 if let Some(storage_recorded_logs) = &mut self.recorded_logs {
1183 storage_recorded_logs.push(Vm::Log {
1184 topics: log.data.topics().to_vec(),
1185 data: log.data.data.clone(),
1186 emitter: log.address,
1187 });
1188 }
1189 }
1190
1191 fn call(&mut self, ecx: Ecx, inputs: &mut CallInputs) -> Option<CallOutcome> {
1192 Self::call_with_executor(self, ecx, inputs, &mut TransparentCheatcodesExecutor)
1193 }
1194
1195 fn call_end(&mut self, ecx: Ecx, call: &CallInputs, outcome: &mut CallOutcome) {
1196 let cheatcode_call = call.target_address == CHEATCODE_ADDRESS
1197 || call.target_address == HARDHAT_CONSOLE_ADDRESS;
1198
1199 if !cheatcode_call {
1203 let curr_depth = ecx.journaled_state.depth();
1205 if let Some(prank) = &self.get_prank(curr_depth)
1206 && curr_depth == prank.depth
1207 {
1208 ecx.tx.caller = prank.prank_origin;
1209
1210 if prank.single_call {
1212 self.pranks.remove(&curr_depth);
1213 }
1214 }
1215
1216 if let Some(broadcast) = &self.broadcast
1218 && curr_depth == broadcast.depth
1219 {
1220 ecx.tx.caller = broadcast.original_origin;
1221
1222 if broadcast.single_call {
1224 let _ = self.broadcast.take();
1225 }
1226 }
1227 }
1228
1229 if let Some(assume_no_revert) = &mut self.assume_no_revert {
1231 if outcome.result.is_revert() && assume_no_revert.reverted_by.is_none() {
1234 assume_no_revert.reverted_by = Some(call.target_address);
1235 }
1236
1237 let curr_depth = ecx.journaled_state.depth();
1239 if curr_depth <= assume_no_revert.depth && !cheatcode_call {
1240 if outcome.result.is_revert() {
1243 let assume_no_revert = std::mem::take(&mut self.assume_no_revert).unwrap();
1244 return match revert_handlers::handle_assume_no_revert(
1245 &assume_no_revert,
1246 outcome.result.result,
1247 &outcome.result.output,
1248 &self.config.available_artifacts,
1249 ) {
1250 Ok(_) => {
1253 outcome.result.output = Error::from(MAGIC_ASSUME).abi_encode().into();
1254 }
1255 Err(error) => {
1258 trace!(expected=?assume_no_revert, ?error, status=?outcome.result.result, "Expected revert mismatch");
1259 outcome.result.result = InstructionResult::Revert;
1260 outcome.result.output = error.abi_encode().into();
1261 }
1262 };
1263 } else {
1264 self.assume_no_revert = None;
1266 }
1267 }
1268 }
1269
1270 if let Some(expected_revert) = &mut self.expected_revert {
1272 if outcome.result.is_revert() {
1275 if expected_revert.reverter.is_some()
1279 && (expected_revert.reverted_by.is_none() || expected_revert.count > 1)
1280 {
1281 expected_revert.reverted_by = Some(call.target_address);
1282 }
1283 }
1284
1285 let curr_depth = ecx.journaled_state.depth();
1286 if curr_depth <= expected_revert.depth {
1287 let needs_processing = match expected_revert.kind {
1288 ExpectedRevertKind::Default => !cheatcode_call,
1289 ExpectedRevertKind::Cheatcode { pending_processing } => {
1292 cheatcode_call && !pending_processing
1293 }
1294 };
1295
1296 if needs_processing {
1297 let mut expected_revert = std::mem::take(&mut self.expected_revert).unwrap();
1298 return match revert_handlers::handle_expect_revert(
1299 cheatcode_call,
1300 false,
1301 self.config.internal_expect_revert,
1302 &expected_revert,
1303 outcome.result.result,
1304 outcome.result.output.clone(),
1305 &self.config.available_artifacts,
1306 ) {
1307 Err(error) => {
1308 trace!(expected=?expected_revert, ?error, status=?outcome.result.result, "Expected revert mismatch");
1309 outcome.result.result = InstructionResult::Revert;
1310 outcome.result.output = error.abi_encode().into();
1311 }
1312 Ok((_, retdata)) => {
1313 expected_revert.actual_count += 1;
1314 if expected_revert.actual_count < expected_revert.count {
1315 self.expected_revert = Some(expected_revert.clone());
1316 }
1317 outcome.result.result = InstructionResult::Return;
1318 outcome.result.output = retdata;
1319 }
1320 };
1321 }
1322
1323 if let ExpectedRevertKind::Cheatcode { pending_processing } =
1326 &mut self.expected_revert.as_mut().unwrap().kind
1327 {
1328 *pending_processing = false;
1329 }
1330 }
1331 }
1332
1333 if cheatcode_call {
1336 return;
1337 }
1338
1339 let gas = outcome.result.gas;
1342 self.gas_metering.last_call_gas = Some(crate::Vm::Gas {
1343 gasLimit: gas.limit(),
1344 gasTotalUsed: gas.spent(),
1345 gasMemoryUsed: 0,
1346 gasRefunded: gas.refunded(),
1347 gasRemaining: gas.remaining(),
1348 });
1349
1350 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1353 if ecx.journaled_state.depth() > 0
1355 && let Some(last_recorded_depth) = &mut recorded_account_diffs_stack.pop()
1356 {
1357 if outcome.result.is_revert() {
1360 last_recorded_depth.iter_mut().for_each(|element| {
1361 element.reverted = true;
1362 element
1363 .storageAccesses
1364 .iter_mut()
1365 .for_each(|storage_access| storage_access.reverted = true);
1366 })
1367 }
1368
1369 if let Some(call_access) = last_recorded_depth.first_mut() {
1370 let curr_depth = ecx.journaled_state.depth();
1375 if call_access.depth == curr_depth as u64
1376 && let Ok(acc) = ecx.journaled_state.load_account(call.target_address)
1377 {
1378 debug_assert!(access_is_call(call_access.kind));
1379 call_access.newBalance = acc.info.balance;
1380 call_access.newNonce = acc.info.nonce;
1381 }
1382 if let Some(last) = recorded_account_diffs_stack.last_mut() {
1387 last.append(last_recorded_depth);
1388 } else {
1389 recorded_account_diffs_stack.push(last_recorded_depth.clone());
1390 }
1391 }
1392 }
1393 }
1394
1395 let should_check_emits = self
1407 .expected_emits
1408 .iter()
1409 .any(|(expected, _)| {
1410 let curr_depth = ecx.journaled_state.depth();
1411 expected.depth == curr_depth
1412 }) &&
1413 !call.is_static;
1415 if should_check_emits {
1416 let expected_counts = self
1417 .expected_emits
1418 .iter()
1419 .filter_map(|(expected, count_map)| {
1420 let count = match expected.address {
1421 Some(emitter) => match count_map.get(&emitter) {
1422 Some(log_count) => expected
1423 .log
1424 .as_ref()
1425 .map(|l| log_count.count(l))
1426 .unwrap_or_else(|| log_count.count_unchecked()),
1427 None => 0,
1428 },
1429 None => match &expected.log {
1430 Some(log) => count_map.values().map(|logs| logs.count(log)).sum(),
1431 None => count_map.values().map(|logs| logs.count_unchecked()).sum(),
1432 },
1433 };
1434
1435 if count != expected.count { Some((expected, count)) } else { None }
1436 })
1437 .collect::<Vec<_>>();
1438
1439 if let Some((expected, _)) = self
1441 .expected_emits
1442 .iter()
1443 .find(|(expected, _)| !expected.found && expected.count > 0)
1444 {
1445 outcome.result.result = InstructionResult::Revert;
1446 let error_msg = expected.mismatch_error.as_deref().unwrap_or("log != expected log");
1447 outcome.result.output = error_msg.abi_encode().into();
1448 return;
1449 }
1450
1451 if !expected_counts.is_empty() {
1452 let msg = if outcome.result.is_ok() {
1453 let (expected, count) = expected_counts.first().unwrap();
1454 format!("log emitted {count} times, expected {}", expected.count)
1455 } else {
1456 "expected an emit, but the call reverted instead. \
1457 ensure you're testing the happy path when using `expectEmit`"
1458 .to_string()
1459 };
1460
1461 outcome.result.result = InstructionResult::Revert;
1462 outcome.result.output = Error::encode(msg);
1463 return;
1464 }
1465
1466 self.expected_emits.clear()
1470 }
1471
1472 let diag = self.fork_revert_diagnostic.take();
1475
1476 if outcome.result.is_revert()
1479 && let Some(err) = diag
1480 {
1481 outcome.result.output = Error::encode(err.to_error_msg(&self.labels));
1482 return;
1483 }
1484
1485 if let TxKind::Call(test_contract) = ecx.tx.kind {
1488 if ecx.journaled_state.db().is_forked_mode()
1491 && outcome.result.result == InstructionResult::Stop
1492 && call.target_address != test_contract
1493 {
1494 let journaled_state = ecx.journaled_state.clone();
1495 self.fork_revert_diagnostic =
1496 ecx.journaled_state.db().diagnose_revert(call.target_address, &journaled_state);
1497 }
1498 }
1499
1500 if ecx.journaled_state.depth() == 0 {
1502 if outcome.result.is_revert() {
1506 return;
1507 }
1508
1509 for (address, calldatas) in &self.expected_calls {
1514 for (calldata, (expected, actual_count)) in calldatas {
1516 let ExpectedCallData { gas, min_gas, value, count, call_type } = expected;
1518
1519 let failed = match call_type {
1520 ExpectedCallType::Count => *count != *actual_count,
1524 ExpectedCallType::NonCount => *count > *actual_count,
1529 };
1530 if failed {
1531 let expected_values = [
1532 Some(format!("data {}", hex::encode_prefixed(calldata))),
1533 value.as_ref().map(|v| format!("value {v}")),
1534 gas.map(|g| format!("gas {g}")),
1535 min_gas.map(|g| format!("minimum gas {g}")),
1536 ]
1537 .into_iter()
1538 .flatten()
1539 .join(", ");
1540 let but = if outcome.result.is_ok() {
1541 let s = if *actual_count == 1 { "" } else { "s" };
1542 format!("was called {actual_count} time{s}")
1543 } else {
1544 "the call reverted instead; \
1545 ensure you're testing the happy path when using `expectCall`"
1546 .to_string()
1547 };
1548 let s = if *count == 1 { "" } else { "s" };
1549 let msg = format!(
1550 "expected call to {address} with {expected_values} \
1551 to be called {count} time{s}, but {but}"
1552 );
1553 outcome.result.result = InstructionResult::Revert;
1554 outcome.result.output = Error::encode(msg);
1555
1556 return;
1557 }
1558 }
1559 }
1560
1561 for (expected, _) in &mut self.expected_emits {
1565 if expected.count == 0 && !expected.found {
1566 expected.found = true;
1567 }
1568 }
1569 self.expected_emits.retain(|(expected, _)| !expected.found);
1570 if !self.expected_emits.is_empty() {
1572 let msg = if outcome.result.is_ok() {
1573 "expected an emit, but no logs were emitted afterwards. \
1574 you might have mismatched events or not enough events were emitted"
1575 } else {
1576 "expected an emit, but the call reverted instead. \
1577 ensure you're testing the happy path when using `expectEmit`"
1578 };
1579 outcome.result.result = InstructionResult::Revert;
1580 outcome.result.output = Error::encode(msg);
1581 return;
1582 }
1583
1584 if let Some(expected_create) = self.expected_creates.first() {
1586 let msg = format!(
1587 "expected {} call by address {} for bytecode {} but not found",
1588 expected_create.create_scheme,
1589 hex::encode_prefixed(expected_create.deployer),
1590 hex::encode_prefixed(&expected_create.bytecode),
1591 );
1592 outcome.result.result = InstructionResult::Revert;
1593 outcome.result.output = Error::encode(msg);
1594 }
1595 }
1596 }
1597
1598 fn create(&mut self, ecx: Ecx, mut input: &mut CreateInputs) -> Option<CreateOutcome> {
1599 let gas = Gas::new(input.gas_limit());
1600 if self.intercept_next_create_call {
1602 self.intercept_next_create_call = false;
1604
1605 let output = input.init_code();
1607
1608 return Some(CreateOutcome {
1610 result: InterpreterResult { result: InstructionResult::Revert, output, gas },
1611 address: None,
1612 });
1613 }
1614
1615 let curr_depth = ecx.journaled_state.depth();
1616
1617 if let Some(prank) = &self.get_prank(curr_depth)
1619 && curr_depth >= prank.depth
1620 && input.caller() == prank.prank_caller
1621 {
1622 let mut prank_applied = false;
1623
1624 if curr_depth == prank.depth {
1626 input.set_caller(prank.new_caller);
1627 prank_applied = true;
1628 }
1629
1630 if let Some(new_origin) = prank.new_origin {
1632 ecx.tx.caller = new_origin;
1633 prank_applied = true;
1634 }
1635
1636 if prank_applied && let Some(applied_prank) = prank.first_time_applied() {
1638 self.pranks.insert(curr_depth, applied_prank);
1639 }
1640 }
1641
1642 self.apply_accesslist(ecx);
1644
1645 if let Some(broadcast) = &mut self.broadcast
1647 && curr_depth >= broadcast.depth
1648 && input.caller() == broadcast.original_caller
1649 {
1650 if let Err(err) = ecx.journaled_state.load_account(broadcast.new_origin) {
1651 return Some(CreateOutcome {
1652 result: InterpreterResult {
1653 result: InstructionResult::Revert,
1654 output: Error::encode(err),
1655 gas,
1656 },
1657 address: None,
1658 });
1659 }
1660
1661 ecx.tx.caller = broadcast.new_origin;
1662
1663 if curr_depth == broadcast.depth || broadcast.deploy_from_code {
1664 broadcast.deploy_from_code = false;
1666
1667 input.set_caller(broadcast.new_origin);
1668
1669 ecx.journaled_state.touch(broadcast.new_origin);
1671
1672 let account = &ecx.journaled_state.inner.state()[&broadcast.new_origin];
1673 self.broadcastable_transactions.push_back(BroadcastableTransaction {
1674 rpc: ecx.journaled_state.database.active_fork_url(),
1675 transaction: TransactionRequest {
1676 from: Some(broadcast.new_origin),
1677 to: None,
1678 value: Some(input.value()),
1679 input: TransactionInput::new(input.init_code()),
1680 nonce: Some(account.info.nonce),
1681 ..Default::default()
1682 }
1683 .into(),
1684 });
1685
1686 input.log_debug(self, &input.scheme().unwrap_or(CreateScheme::Create));
1687 }
1688 }
1689
1690 let address = input.allow_cheatcodes(self, ecx);
1692
1693 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1695 recorded_account_diffs_stack.push(vec![AccountAccess {
1696 chainInfo: crate::Vm::ChainInfo {
1697 forkId: ecx.journaled_state.db().active_fork_id().unwrap_or_default(),
1698 chainId: U256::from(ecx.cfg.chain_id),
1699 },
1700 accessor: input.caller(),
1701 account: address,
1702 kind: crate::Vm::AccountAccessKind::Create,
1703 initialized: true,
1704 oldBalance: U256::ZERO, newBalance: U256::ZERO, oldNonce: 0, newNonce: 1, value: input.value(),
1709 data: input.init_code(),
1710 reverted: false,
1711 deployedCode: Bytes::new(), storageAccesses: vec![], depth: curr_depth as u64,
1714 }]);
1715 }
1716
1717 None
1718 }
1719
1720 fn create_end(&mut self, ecx: Ecx, call: &CreateInputs, outcome: &mut CreateOutcome) {
1721 let call = Some(call);
1722 let curr_depth = ecx.journaled_state.depth();
1723
1724 if let Some(prank) = &self.get_prank(curr_depth)
1726 && curr_depth == prank.depth
1727 {
1728 ecx.tx.caller = prank.prank_origin;
1729
1730 if prank.single_call {
1732 std::mem::take(&mut self.pranks);
1733 }
1734 }
1735
1736 if let Some(broadcast) = &self.broadcast
1738 && curr_depth == broadcast.depth
1739 {
1740 ecx.tx.caller = broadcast.original_origin;
1741
1742 if broadcast.single_call {
1744 std::mem::take(&mut self.broadcast);
1745 }
1746 }
1747
1748 if let Some(expected_revert) = &self.expected_revert
1750 && curr_depth <= expected_revert.depth
1751 && matches!(expected_revert.kind, ExpectedRevertKind::Default)
1752 {
1753 let mut expected_revert = std::mem::take(&mut self.expected_revert).unwrap();
1754 return match revert_handlers::handle_expect_revert(
1755 false,
1756 true,
1757 self.config.internal_expect_revert,
1758 &expected_revert,
1759 outcome.result.result,
1760 outcome.result.output.clone(),
1761 &self.config.available_artifacts,
1762 ) {
1763 Ok((address, retdata)) => {
1764 expected_revert.actual_count += 1;
1765 if expected_revert.actual_count < expected_revert.count {
1766 self.expected_revert = Some(expected_revert.clone());
1767 }
1768
1769 outcome.result.result = InstructionResult::Return;
1770 outcome.result.output = retdata;
1771 outcome.address = address;
1772 }
1773 Err(err) => {
1774 outcome.result.result = InstructionResult::Revert;
1775 outcome.result.output = err.abi_encode().into();
1776 }
1777 };
1778 }
1779
1780 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1783 if curr_depth > 0
1785 && let Some(last_depth) = &mut recorded_account_diffs_stack.pop()
1786 {
1787 if outcome.result.is_revert() {
1790 last_depth.iter_mut().for_each(|element| {
1791 element.reverted = true;
1792 element
1793 .storageAccesses
1794 .iter_mut()
1795 .for_each(|storage_access| storage_access.reverted = true);
1796 })
1797 }
1798
1799 if let Some(create_access) = last_depth.first_mut() {
1800 let depth = ecx.journaled_state.depth();
1805 if create_access.depth == depth as u64 {
1806 debug_assert_eq!(
1807 create_access.kind as u8,
1808 crate::Vm::AccountAccessKind::Create as u8
1809 );
1810 if let Some(address) = outcome.address
1811 && let Ok(created_acc) = ecx.journaled_state.load_account(address)
1812 {
1813 create_access.newBalance = created_acc.info.balance;
1814 create_access.newNonce = created_acc.info.nonce;
1815 create_access.deployedCode =
1816 created_acc.info.code.clone().unwrap_or_default().original_bytes();
1817 }
1818 }
1819 if let Some(last) = recorded_account_diffs_stack.last_mut() {
1824 last.append(last_depth);
1825 } else {
1826 recorded_account_diffs_stack.push(last_depth.clone());
1827 }
1828 }
1829 }
1830 }
1831
1832 if !self.expected_creates.is_empty()
1834 && let (Some(address), Some(call)) = (outcome.address, call)
1835 && let Ok(created_acc) = ecx.journaled_state.load_account(address)
1836 {
1837 let bytecode = created_acc.info.code.clone().unwrap_or_default().original_bytes();
1838 if let Some((index, _)) =
1839 self.expected_creates.iter().find_position(|expected_create| {
1840 expected_create.deployer == call.caller
1841 && expected_create.create_scheme.eq(call.scheme.into())
1842 && expected_create.bytecode == bytecode
1843 })
1844 {
1845 self.expected_creates.swap_remove(index);
1846 }
1847 }
1848 }
1849}
1850
1851impl InspectorExt for Cheatcodes {
1852 fn should_use_create2_factory(&mut self, ecx: Ecx, inputs: &CreateInputs) -> bool {
1853 if let CreateScheme::Create2 { .. } = inputs.scheme {
1854 let depth = ecx.journaled_state.depth();
1855 let target_depth = if let Some(prank) = &self.get_prank(depth) {
1856 prank.depth
1857 } else if let Some(broadcast) = &self.broadcast {
1858 broadcast.depth
1859 } else {
1860 1
1861 };
1862
1863 depth == target_depth
1864 && (self.broadcast.is_some() || self.config.always_use_create_2_factory)
1865 } else {
1866 false
1867 }
1868 }
1869
1870 fn create2_deployer(&self) -> Address {
1871 self.config.evm_opts.create2_deployer
1872 }
1873}
1874
1875impl Cheatcodes {
1876 #[cold]
1877 fn meter_gas(&mut self, interpreter: &mut Interpreter) {
1878 if let Some(paused_gas) = self.gas_metering.paused_frames.last() {
1879 let memory = *interpreter.gas.memory();
1882 interpreter.gas = *paused_gas;
1883 interpreter.gas.memory_mut().words_num = memory.words_num;
1884 interpreter.gas.memory_mut().expansion_cost = memory.expansion_cost;
1885 } else {
1886 self.gas_metering.paused_frames.push(interpreter.gas);
1888 }
1889 }
1890
1891 #[cold]
1892 fn meter_gas_record(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1893 if interpreter.bytecode.action.as_ref().and_then(|i| i.instruction_result()).is_none() {
1894 self.gas_metering.gas_records.iter_mut().for_each(|record| {
1895 let curr_depth = ecx.journaled_state.depth();
1896 if curr_depth == record.depth {
1897 if self.gas_metering.last_gas_used != 0 {
1900 let gas_diff =
1901 interpreter.gas.spent().saturating_sub(self.gas_metering.last_gas_used);
1902 record.gas_used = record.gas_used.saturating_add(gas_diff);
1903 }
1904
1905 self.gas_metering.last_gas_used = interpreter.gas.spent();
1908 }
1909 });
1910 }
1911 }
1912
1913 #[cold]
1914 fn meter_gas_end(&mut self, interpreter: &mut Interpreter) {
1915 if let Some(interpreter_action) = interpreter.bytecode.action.as_ref()
1917 && will_exit(interpreter_action)
1918 {
1919 self.gas_metering.paused_frames.pop();
1920 }
1921 }
1922
1923 #[cold]
1924 fn meter_gas_reset(&mut self, interpreter: &mut Interpreter) {
1925 interpreter.gas = Gas::new(interpreter.gas.limit());
1926 self.gas_metering.reset = false;
1927 }
1928
1929 #[cold]
1930 fn meter_gas_check(&mut self, interpreter: &mut Interpreter) {
1931 if let Some(interpreter_action) = interpreter.bytecode.action.as_ref()
1932 && will_exit(interpreter_action)
1933 {
1934 if interpreter.gas.spent()
1938 < u64::try_from(interpreter.gas.refunded()).unwrap_or_default()
1939 {
1940 interpreter.gas = Gas::new(interpreter.gas.limit());
1941 }
1942 }
1943 }
1944
1945 #[cold]
1953 fn arbitrary_storage_end(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1954 let (key, target_address) = if interpreter.bytecode.opcode() == op::SLOAD {
1955 (try_or_return!(interpreter.stack.peek(0)), interpreter.input.target_address)
1956 } else {
1957 return;
1958 };
1959
1960 let Some(value) = ecx.sload(target_address, key) else {
1961 return;
1962 };
1963
1964 if (value.is_cold && value.data.is_zero())
1965 || self.should_overwrite_arbitrary_storage(&target_address, key)
1966 {
1967 if self.has_arbitrary_storage(&target_address) {
1968 let arbitrary_value = self.rng().random();
1969 self.arbitrary_storage.as_mut().unwrap().save(
1970 ecx,
1971 target_address,
1972 key,
1973 arbitrary_value,
1974 );
1975 } else if self.is_arbitrary_storage_copy(&target_address) {
1976 let arbitrary_value = self.rng().random();
1977 self.arbitrary_storage.as_mut().unwrap().copy(
1978 ecx,
1979 target_address,
1980 key,
1981 arbitrary_value,
1982 );
1983 }
1984 }
1985 }
1986
1987 #[cold]
1989 fn record_accesses(&mut self, interpreter: &mut Interpreter) {
1990 let access = &mut self.accesses;
1991 match interpreter.bytecode.opcode() {
1992 op::SLOAD => {
1993 let key = try_or_return!(interpreter.stack.peek(0));
1994 access.record_read(interpreter.input.target_address, key);
1995 }
1996 op::SSTORE => {
1997 let key = try_or_return!(interpreter.stack.peek(0));
1998 access.record_write(interpreter.input.target_address, key);
1999 }
2000 _ => {}
2001 }
2002 }
2003
2004 #[cold]
2005 fn record_state_diffs(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
2006 let Some(account_accesses) = &mut self.recorded_account_diffs_stack else { return };
2007 match interpreter.bytecode.opcode() {
2008 op::SELFDESTRUCT => {
2009 let Some(last) = account_accesses.last_mut() else { return };
2011
2012 let target = try_or_return!(interpreter.stack.peek(0));
2014 let target = Address::from_word(B256::from(target));
2015 let (initialized, old_balance, old_nonce) = ecx
2016 .journaled_state
2017 .load_account(target)
2018 .map(|account| {
2019 (account.info.exists(), account.info.balance, account.info.nonce)
2020 })
2021 .unwrap_or_default();
2022
2023 let value = ecx
2025 .balance(interpreter.input.target_address)
2026 .map(|b| b.data)
2027 .unwrap_or(U256::ZERO);
2028
2029 last.push(crate::Vm::AccountAccess {
2031 chainInfo: crate::Vm::ChainInfo {
2032 forkId: ecx.journaled_state.database.active_fork_id().unwrap_or_default(),
2033 chainId: U256::from(ecx.cfg.chain_id),
2034 },
2035 accessor: interpreter.input.target_address,
2036 account: target,
2037 kind: crate::Vm::AccountAccessKind::SelfDestruct,
2038 initialized,
2039 oldBalance: old_balance,
2040 newBalance: old_balance + value,
2041 oldNonce: old_nonce,
2042 newNonce: old_nonce, value,
2044 data: Bytes::new(),
2045 reverted: false,
2046 deployedCode: Bytes::new(),
2047 storageAccesses: vec![],
2048 depth: ecx
2049 .journaled_state
2050 .depth()
2051 .try_into()
2052 .expect("journaled state depth exceeds u64"),
2053 });
2054 }
2055
2056 op::SLOAD => {
2057 let Some(last) = account_accesses.last_mut() else { return };
2058
2059 let key = try_or_return!(interpreter.stack.peek(0));
2060 let address = interpreter.input.target_address;
2061
2062 let mut present_value = U256::ZERO;
2065 if ecx.journaled_state.load_account(address).is_ok()
2067 && let Some(previous) = ecx.sload(address, key)
2068 {
2069 present_value = previous.data;
2070 }
2071 let access = crate::Vm::StorageAccess {
2072 account: interpreter.input.target_address,
2073 slot: key.into(),
2074 isWrite: false,
2075 previousValue: present_value.into(),
2076 newValue: present_value.into(),
2077 reverted: false,
2078 };
2079 let curr_depth = ecx
2080 .journaled_state
2081 .depth()
2082 .try_into()
2083 .expect("journaled state depth exceeds u64");
2084 append_storage_access(last, access, curr_depth);
2085 }
2086 op::SSTORE => {
2087 let Some(last) = account_accesses.last_mut() else { return };
2088
2089 let key = try_or_return!(interpreter.stack.peek(0));
2090 let value = try_or_return!(interpreter.stack.peek(1));
2091 let address = interpreter.input.target_address;
2092 let mut previous_value = U256::ZERO;
2095 if ecx.journaled_state.load_account(address).is_ok()
2096 && let Some(previous) = ecx.sload(address, key)
2097 {
2098 previous_value = previous.data;
2099 }
2100
2101 let access = crate::Vm::StorageAccess {
2102 account: address,
2103 slot: key.into(),
2104 isWrite: true,
2105 previousValue: previous_value.into(),
2106 newValue: value.into(),
2107 reverted: false,
2108 };
2109 let curr_depth = ecx
2110 .journaled_state
2111 .depth()
2112 .try_into()
2113 .expect("journaled state depth exceeds u64");
2114 append_storage_access(last, access, curr_depth);
2115 }
2116
2117 op::EXTCODECOPY | op::EXTCODESIZE | op::EXTCODEHASH | op::BALANCE => {
2119 let kind = match interpreter.bytecode.opcode() {
2120 op::EXTCODECOPY => crate::Vm::AccountAccessKind::Extcodecopy,
2121 op::EXTCODESIZE => crate::Vm::AccountAccessKind::Extcodesize,
2122 op::EXTCODEHASH => crate::Vm::AccountAccessKind::Extcodehash,
2123 op::BALANCE => crate::Vm::AccountAccessKind::Balance,
2124 _ => unreachable!(),
2125 };
2126 let address =
2127 Address::from_word(B256::from(try_or_return!(interpreter.stack.peek(0))));
2128 let initialized;
2129 let balance;
2130 let nonce;
2131 if let Ok(acc) = ecx.journaled_state.load_account(address) {
2132 initialized = acc.info.exists();
2133 balance = acc.info.balance;
2134 nonce = acc.info.nonce;
2135 } else {
2136 initialized = false;
2137 balance = U256::ZERO;
2138 nonce = 0;
2139 }
2140 let curr_depth = ecx
2141 .journaled_state
2142 .depth()
2143 .try_into()
2144 .expect("journaled state depth exceeds u64");
2145 let account_access = crate::Vm::AccountAccess {
2146 chainInfo: crate::Vm::ChainInfo {
2147 forkId: ecx.journaled_state.database.active_fork_id().unwrap_or_default(),
2148 chainId: U256::from(ecx.cfg.chain_id),
2149 },
2150 accessor: interpreter.input.target_address,
2151 account: address,
2152 kind,
2153 initialized,
2154 oldBalance: balance,
2155 newBalance: balance,
2156 oldNonce: nonce,
2157 newNonce: nonce, value: U256::ZERO,
2159 data: Bytes::new(),
2160 reverted: false,
2161 deployedCode: Bytes::new(),
2162 storageAccesses: vec![],
2163 depth: curr_depth,
2164 };
2165 if let Some(last) = account_accesses.last_mut() {
2168 last.push(account_access);
2169 } else {
2170 account_accesses.push(vec![account_access]);
2171 }
2172 }
2173 _ => {}
2174 }
2175 }
2176
2177 #[cold]
2182 fn check_mem_opcodes(&self, interpreter: &mut Interpreter, depth: u64) {
2183 let Some(ranges) = self.allowed_mem_writes.get(&depth) else {
2184 return;
2185 };
2186
2187 macro_rules! mem_opcode_match {
2196 ($(($opcode:ident, $offset_depth:expr, $size_depth:expr, $writes:expr)),* $(,)?) => {
2197 match interpreter.bytecode.opcode() {
2198 op::MSTORE => {
2203 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2205
2206 if !ranges.iter().any(|range| {
2209 range.contains(&offset) && range.contains(&(offset + 31))
2210 }) {
2211 let value = try_or_return!(interpreter.stack.peek(1)).to_be_bytes::<32>();
2216 if value[..SELECTOR_LEN] == stopExpectSafeMemoryCall::SELECTOR {
2217 return
2218 }
2219
2220 disallowed_mem_write(offset, 32, interpreter, ranges);
2221 return
2222 }
2223 }
2224 op::MSTORE8 => {
2225 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2227
2228 if !ranges.iter().any(|range| range.contains(&offset)) {
2231 disallowed_mem_write(offset, 1, interpreter, ranges);
2232 return
2233 }
2234 }
2235
2236 op::MLOAD => {
2241 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2243
2244 if offset >= interpreter.memory.size() as u64 && !ranges.iter().any(|range| {
2248 range.contains(&offset) && range.contains(&(offset + 31))
2249 }) {
2250 disallowed_mem_write(offset, 32, interpreter, ranges);
2251 return
2252 }
2253 }
2254
2255 op::CALL => {
2260 let dest_offset = try_or_return!(interpreter.stack.peek(5)).saturating_to::<u64>();
2262
2263 let size = try_or_return!(interpreter.stack.peek(6)).saturating_to::<u64>();
2265
2266 let fail_cond = !ranges.iter().any(|range| {
2270 range.contains(&dest_offset) &&
2271 range.contains(&(dest_offset + size.saturating_sub(1)))
2272 });
2273
2274 if fail_cond {
2277 let to = Address::from_word(try_or_return!(interpreter.stack.peek(1)).to_be_bytes::<32>().into());
2281 if to == CHEATCODE_ADDRESS {
2282 let args_offset = try_or_return!(interpreter.stack.peek(3)).saturating_to::<usize>();
2283 let args_size = try_or_return!(interpreter.stack.peek(4)).saturating_to::<usize>();
2284 let memory_word = interpreter.memory.slice_len(args_offset, args_size);
2285 if memory_word[..SELECTOR_LEN] == stopExpectSafeMemoryCall::SELECTOR {
2286 return
2287 }
2288 }
2289
2290 disallowed_mem_write(dest_offset, size, interpreter, ranges);
2291 return
2292 }
2293 }
2294
2295 $(op::$opcode => {
2296 let dest_offset = try_or_return!(interpreter.stack.peek($offset_depth)).saturating_to::<u64>();
2298
2299 let size = try_or_return!(interpreter.stack.peek($size_depth)).saturating_to::<u64>();
2301
2302 let fail_cond = !ranges.iter().any(|range| {
2306 range.contains(&dest_offset) &&
2307 range.contains(&(dest_offset + size.saturating_sub(1)))
2308 }) && ($writes ||
2309 [dest_offset, (dest_offset + size).saturating_sub(1)].into_iter().any(|offset| {
2310 offset >= interpreter.memory.size() as u64
2311 })
2312 );
2313
2314 if fail_cond {
2317 disallowed_mem_write(dest_offset, size, interpreter, ranges);
2318 return
2319 }
2320 })*
2321
2322 _ => {}
2323 }
2324 }
2325 }
2326
2327 mem_opcode_match!(
2330 (CALLDATACOPY, 0, 2, true),
2331 (CODECOPY, 0, 2, true),
2332 (RETURNDATACOPY, 0, 2, true),
2333 (EXTCODECOPY, 1, 3, true),
2334 (CALLCODE, 5, 6, true),
2335 (STATICCALL, 4, 5, true),
2336 (DELEGATECALL, 4, 5, true),
2337 (KECCAK256, 0, 1, false),
2338 (LOG0, 0, 1, false),
2339 (LOG1, 0, 1, false),
2340 (LOG2, 0, 1, false),
2341 (LOG3, 0, 1, false),
2342 (LOG4, 0, 1, false),
2343 (CREATE, 1, 2, false),
2344 (CREATE2, 1, 2, false),
2345 (RETURN, 0, 1, false),
2346 (REVERT, 0, 1, false),
2347 );
2348 }
2349
2350 #[cold]
2351 fn record_gas_limit_opcode(&mut self, interpreter: &mut Interpreter) {
2352 match interpreter.bytecode.opcode() {
2353 op::CREATE2 => self.dynamic_gas_limit_sequence = Some((true, true)),
2355 op::GAS => {
2356 if self.dynamic_gas_limit_sequence.is_none() {
2357 self.dynamic_gas_limit_sequence = Some((true, false));
2359 }
2360 }
2361 _ => {}
2362 }
2363 }
2364
2365 #[cold]
2366 fn set_gas_limit_type(&mut self, interpreter: &mut Interpreter) {
2367 if matches!(self.dynamic_gas_limit_sequence, Some((true, true))) {
2369 return;
2370 }
2371
2372 if matches!(self.dynamic_gas_limit_sequence, Some((true, false)))
2374 && interpreter.bytecode.opcode() == op::CALL
2375 {
2376 self.dynamic_gas_limit_sequence = Some((true, true));
2377 return;
2378 }
2379
2380 self.dynamic_gas_limit_sequence = None;
2382 }
2383}
2384
2385fn disallowed_mem_write(
2391 dest_offset: u64,
2392 size: u64,
2393 interpreter: &mut Interpreter,
2394 ranges: &[Range<u64>],
2395) {
2396 let revert_string = format!(
2397 "memory write at offset 0x{:02X} of size 0x{:02X} not allowed; safe range: {}",
2398 dest_offset,
2399 size,
2400 ranges.iter().map(|r| format!("(0x{:02X}, 0x{:02X}]", r.start, r.end)).join(" U ")
2401 );
2402
2403 interpreter.bytecode.set_action(InterpreterAction::new_return(
2404 InstructionResult::Revert,
2405 Bytes::from(revert_string.into_bytes()),
2406 interpreter.gas,
2407 ));
2408}
2409
2410fn access_is_call(kind: crate::Vm::AccountAccessKind) -> bool {
2412 matches!(
2413 kind,
2414 crate::Vm::AccountAccessKind::Call
2415 | crate::Vm::AccountAccessKind::StaticCall
2416 | crate::Vm::AccountAccessKind::CallCode
2417 | crate::Vm::AccountAccessKind::DelegateCall
2418 )
2419}
2420
2421fn append_storage_access(
2423 last: &mut Vec<AccountAccess>,
2424 storage_access: crate::Vm::StorageAccess,
2425 storage_depth: u64,
2426) {
2427 if !last.is_empty() && last.first().unwrap().depth < storage_depth {
2429 if last.len() == 1 {
2435 last.first_mut().unwrap().storageAccesses.push(storage_access);
2436 } else {
2437 let last_record = last.last_mut().unwrap();
2438 if last_record.kind as u8 == crate::Vm::AccountAccessKind::Resume as u8 {
2439 last_record.storageAccesses.push(storage_access);
2440 } else {
2441 let entry = last.first().unwrap();
2442 let resume_record = crate::Vm::AccountAccess {
2443 chainInfo: crate::Vm::ChainInfo {
2444 forkId: entry.chainInfo.forkId,
2445 chainId: entry.chainInfo.chainId,
2446 },
2447 accessor: entry.accessor,
2448 account: entry.account,
2449 kind: crate::Vm::AccountAccessKind::Resume,
2450 initialized: entry.initialized,
2451 storageAccesses: vec![storage_access],
2452 reverted: entry.reverted,
2453 oldBalance: U256::ZERO,
2455 newBalance: U256::ZERO,
2456 oldNonce: 0,
2457 newNonce: 0,
2458 value: U256::ZERO,
2459 data: Bytes::new(),
2460 deployedCode: Bytes::new(),
2461 depth: entry.depth,
2462 };
2463 last.push(resume_record);
2464 }
2465 }
2466 }
2467}
2468
2469fn apply_dispatch(
2471 calls: &Vm::VmCalls,
2472 ccx: &mut CheatsCtxt,
2473 executor: &mut dyn CheatcodesExecutor,
2474) -> Result {
2475 let cheat = calls_as_dyn_cheatcode(calls);
2476
2477 let _guard = debug_span!(target: "cheatcodes", "apply", id = %cheat.id()).entered();
2478 trace!(target: "cheatcodes", ?cheat, "applying");
2479
2480 if let spec::Status::Deprecated(replacement) = *cheat.status() {
2481 ccx.state.deprecated.insert(cheat.signature(), replacement);
2482 }
2483
2484 let mut result = cheat.dyn_apply(ccx, executor);
2486
2487 if let Err(e) = &mut result
2489 && e.is_str()
2490 {
2491 let name = cheat.name();
2492 if !name.contains("assert") && name != "rpcUrl" {
2496 *e = fmt_err!("vm.{name}: {e}");
2497 }
2498 }
2499
2500 trace!(
2501 target: "cheatcodes",
2502 return = %match &result {
2503 Ok(b) => hex::encode(b),
2504 Err(e) => e.to_string(),
2505 }
2506 );
2507
2508 result
2509}
2510
2511fn calls_as_dyn_cheatcode(calls: &Vm::VmCalls) -> &dyn DynCheatcode {
2512 macro_rules! as_dyn {
2513 ($($variant:ident),*) => {
2514 match calls {
2515 $(Vm::VmCalls::$variant(cheat) => cheat,)*
2516 }
2517 };
2518 }
2519 vm_calls!(as_dyn)
2520}
2521
2522fn will_exit(action: &InterpreterAction) -> bool {
2524 match action {
2525 InterpreterAction::Return(result) => {
2526 result.result.is_ok_or_revert() || result.result.is_error()
2527 }
2528 _ => false,
2529 }
2530}