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 evm::Breakpoints,
38 mapping_slots::{MappingSlots, step as mapping_step},
39};
40use foundry_evm_core::{
41 InspectorExt,
42 abi::Vm::stopExpectSafeMemoryCall,
43 backend::{DatabaseError, DatabaseExt, RevertDiagnostic},
44 constants::{CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS, MAGIC_ASSUME},
45 evm::{FoundryEvm, new_evm_with_existing_context},
46};
47use foundry_evm_traces::{
48 TracingInspector, TracingInspectorConfig, identifier::SignaturesIdentifier,
49};
50use foundry_wallets::multi_wallet::MultiWallet;
51use itertools::Itertools;
52use proptest::test_runner::{RngAlgorithm, TestRng, TestRunner};
53use rand::Rng;
54use revm::{
55 Inspector, Journal,
56 bytecode::opcode as op,
57 context::{BlockEnv, JournalTr, LocalContext, TransactionType, result::EVMError},
58 context_interface::{CreateScheme, transaction::SignedAuthorization},
59 handler::FrameResult,
60 interpreter::{
61 CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, FrameInput, Gas, Host,
62 InstructionResult, Interpreter, InterpreterAction, InterpreterResult,
63 interpreter_types::{Jumps, LoopControl, MemoryTr},
64 },
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 type Ecx<'a, 'b, 'c> = &'a mut EthEvmContext<&'b mut (dyn DatabaseExt + 'c)>;
81
82pub trait CheatcodesExecutor {
88 fn get_inspector<'a>(&'a mut self, cheats: &'a mut Cheatcodes) -> Box<dyn InspectorExt + 'a>;
91
92 fn exec_create(
94 &mut self,
95 inputs: CreateInputs,
96 ccx: &mut CheatsCtxt,
97 ) -> Result<CreateOutcome, EVMError<DatabaseError>> {
98 with_evm(self, ccx, |evm| {
99 evm.inner.ctx.journaled_state.depth += 1;
100
101 let frame = FrameInput::Create(Box::new(inputs));
102
103 let outcome = match evm.run_execution(frame)? {
104 FrameResult::Call(_) => unreachable!(),
105 FrameResult::Create(create) => create,
106 };
107
108 evm.inner.ctx.journaled_state.depth -= 1;
109
110 Ok(outcome)
111 })
112 }
113
114 fn console_log(&mut self, ccx: &mut CheatsCtxt, msg: &str) {
115 self.get_inspector(ccx.state).console_log(msg);
116 }
117
118 fn tracing_inspector(&mut self) -> Option<&mut TracingInspector> {
120 None
121 }
122}
123
124fn with_evm<E, F, O>(
126 executor: &mut E,
127 ccx: &mut CheatsCtxt,
128 f: F,
129) -> Result<O, EVMError<DatabaseError>>
130where
131 E: CheatcodesExecutor + ?Sized,
132 F: for<'a, 'b> FnOnce(
133 &mut FoundryEvm<'a, &'b mut dyn InspectorExt>,
134 ) -> Result<O, EVMError<DatabaseError>>,
135{
136 let mut inspector = executor.get_inspector(ccx.state);
137 let error = std::mem::replace(&mut ccx.ecx.error, Ok(()));
138
139 let ctx = EthEvmContext {
140 block: ccx.ecx.block.clone(),
141 cfg: ccx.ecx.cfg.clone(),
142 tx: ccx.ecx.tx.clone(),
143 journaled_state: Journal {
144 inner: ccx.ecx.journaled_state.inner.clone(),
145 database: &mut *ccx.ecx.journaled_state.database as &mut dyn DatabaseExt,
146 },
147 local: LocalContext::default(),
148 chain: (),
149 error,
150 };
151
152 let mut evm = new_evm_with_existing_context(ctx, &mut *inspector);
153
154 let res = f(&mut evm)?;
155
156 ccx.ecx.journaled_state.inner = evm.inner.ctx.journaled_state.inner;
157 ccx.ecx.block = evm.inner.ctx.block;
158 ccx.ecx.tx = evm.inner.ctx.tx;
159 ccx.ecx.cfg = evm.inner.ctx.cfg;
160 ccx.ecx.error = evm.inner.ctx.error;
161
162 Ok(res)
163}
164
165#[derive(Debug, Default, Clone, Copy)]
168struct TransparentCheatcodesExecutor;
169
170impl CheatcodesExecutor for TransparentCheatcodesExecutor {
171 fn get_inspector<'a>(&'a mut self, cheats: &'a mut Cheatcodes) -> Box<dyn InspectorExt + 'a> {
172 Box::new(cheats)
173 }
174}
175
176macro_rules! try_or_return {
177 ($e:expr) => {
178 match $e {
179 Ok(v) => v,
180 Err(_) => return,
181 }
182 };
183}
184
185#[derive(Debug, Default)]
187pub struct TestContext {
188 pub opened_read_files: HashMap<PathBuf, BufReader<File>>,
190}
191
192impl Clone for TestContext {
194 fn clone(&self) -> Self {
195 Default::default()
196 }
197}
198
199impl TestContext {
200 pub fn clear(&mut self) {
202 self.opened_read_files.clear();
203 }
204}
205
206#[derive(Clone, Debug)]
208pub struct BroadcastableTransaction {
209 pub rpc: Option<String>,
211 pub transaction: TransactionMaybeSigned,
213}
214
215#[derive(Clone, Debug, Copy)]
216pub struct RecordDebugStepInfo {
217 pub start_node_idx: usize,
219 pub original_tracer_config: TracingInspectorConfig,
221}
222
223#[derive(Clone, Debug, Default)]
225pub struct GasMetering {
226 pub paused: bool,
228 pub touched: bool,
231 pub reset: bool,
233 pub paused_frames: Vec<Gas>,
235
236 pub active_gas_snapshot: Option<(String, String)>,
238
239 pub last_call_gas: Option<crate::Vm::Gas>,
242
243 pub recording: bool,
245 pub last_gas_used: u64,
247 pub gas_records: Vec<GasRecord>,
249}
250
251impl GasMetering {
252 pub fn start(&mut self) {
254 self.recording = true;
255 }
256
257 pub fn stop(&mut self) {
259 self.recording = false;
260 }
261
262 pub fn resume(&mut self) {
264 if self.paused {
265 self.paused = false;
266 self.touched = true;
267 }
268 self.paused_frames.clear();
269 }
270
271 pub fn reset(&mut self) {
273 self.paused = false;
274 self.touched = true;
275 self.reset = true;
276 self.paused_frames.clear();
277 }
278}
279
280#[derive(Clone, Debug, Default)]
282pub struct ArbitraryStorage {
283 pub values: HashMap<Address, HashMap<U256, U256>>,
287 pub copies: HashMap<Address, Address>,
289 pub overwrites: HashSet<Address>,
291}
292
293impl ArbitraryStorage {
294 pub fn mark_arbitrary(&mut self, address: &Address, overwrite: bool) {
296 self.values.insert(*address, HashMap::default());
297 if overwrite {
298 self.overwrites.insert(*address);
299 } else {
300 self.overwrites.remove(address);
301 }
302 }
303
304 pub fn mark_copy(&mut self, from: &Address, to: &Address) {
306 if self.values.contains_key(from) {
307 self.copies.insert(*to, *from);
308 }
309 }
310
311 pub fn save(&mut self, ecx: Ecx, address: Address, slot: U256, data: U256) {
315 self.values.get_mut(&address).expect("missing arbitrary address entry").insert(slot, data);
316 if let Ok(mut account) = ecx.journaled_state.load_account(address) {
317 account.storage.insert(slot, EvmStorageSlot::new(data, 0));
318 }
319 }
320
321 pub fn copy(&mut self, ecx: Ecx, target: Address, slot: U256, new_value: U256) -> U256 {
327 let source = self.copies.get(&target).expect("missing arbitrary copy target entry");
328 let storage_cache = self.values.get_mut(source).expect("missing arbitrary source storage");
329 let value = match storage_cache.get(&slot) {
330 Some(value) => *value,
331 None => {
332 storage_cache.insert(slot, new_value);
333 if let Ok(mut source_account) = ecx.journaled_state.load_account(*source) {
335 source_account.storage.insert(slot, EvmStorageSlot::new(new_value, 0));
336 }
337 new_value
338 }
339 };
340 if let Ok(mut target_account) = ecx.journaled_state.load_account(target) {
342 target_account.storage.insert(slot, EvmStorageSlot::new(value, 0));
343 }
344 value
345 }
346}
347
348pub type BroadcastableTransactions = VecDeque<BroadcastableTransaction>;
350
351#[derive(Clone, Debug)]
369pub struct Cheatcodes {
370 pub block: Option<BlockEnv>,
375
376 pub active_delegations: Vec<SignedAuthorization>,
380
381 pub active_blob_sidecar: Option<BlobTransactionSidecar>,
383
384 pub gas_price: Option<u128>,
389
390 pub labels: AddressHashMap<String>,
392
393 pub pranks: BTreeMap<usize, Prank>,
395
396 pub expected_revert: Option<ExpectedRevert>,
398
399 pub assume_no_revert: Option<AssumeNoRevert>,
401
402 pub fork_revert_diagnostic: Option<RevertDiagnostic>,
404
405 pub accesses: RecordAccess,
407
408 pub recording_accesses: bool,
410
411 pub recorded_account_diffs_stack: Option<Vec<Vec<AccountAccess>>>,
417
418 pub record_debug_steps_info: Option<RecordDebugStepInfo>,
420
421 pub recorded_logs: Option<Vec<crate::Vm::Log>>,
423
424 pub mocked_calls: HashMap<Address, BTreeMap<MockCallDataContext, VecDeque<MockCallReturnData>>>,
427
428 pub mocked_functions: HashMap<Address, HashMap<Bytes, Address>>,
430
431 pub expected_calls: ExpectedCallTracker,
433 pub expected_emits: ExpectedEmitTracker,
435 pub expected_creates: Vec<ExpectedCreate>,
437
438 pub allowed_mem_writes: HashMap<u64, Vec<Range<u64>>>,
440
441 pub broadcast: Option<Broadcast>,
443
444 pub broadcastable_transactions: BroadcastableTransactions,
446
447 pub access_list: Option<AccessList>,
449
450 pub config: Arc<CheatsConfig>,
452
453 pub test_context: TestContext,
455
456 pub fs_commit: bool,
459
460 pub serialized_jsons: BTreeMap<String, BTreeMap<String, Value>>,
463
464 pub eth_deals: Vec<DealRecord>,
466
467 pub gas_metering: GasMetering,
469
470 pub gas_snapshots: BTreeMap<String, BTreeMap<String, String>>,
473
474 pub mapping_slots: Option<AddressHashMap<MappingSlots>>,
476
477 pub pc: usize,
479 pub breakpoints: Breakpoints,
482
483 pub intercept_next_create_call: bool,
485
486 test_runner: Option<TestRunner>,
489
490 pub ignored_traces: IgnoredTraces,
492
493 pub arbitrary_storage: Option<ArbitraryStorage>,
495
496 pub deprecated: HashMap<&'static str, Option<&'static str>>,
498 pub wallets: Option<Wallets>,
500 signatures_identifier: OnceLock<Option<SignaturesIdentifier>>,
502 pub dynamic_gas_limit_sequence: Option<(bool, bool)>,
507}
508
509impl Default for Cheatcodes {
513 fn default() -> Self {
514 Self::new(Arc::default())
515 }
516}
517
518impl Cheatcodes {
519 pub fn new(config: Arc<CheatsConfig>) -> Self {
521 Self {
522 fs_commit: true,
523 labels: config.labels.clone(),
524 config,
525 block: Default::default(),
526 active_delegations: Default::default(),
527 active_blob_sidecar: Default::default(),
528 gas_price: Default::default(),
529 pranks: Default::default(),
530 expected_revert: Default::default(),
531 assume_no_revert: Default::default(),
532 fork_revert_diagnostic: Default::default(),
533 accesses: Default::default(),
534 recording_accesses: Default::default(),
535 recorded_account_diffs_stack: Default::default(),
536 recorded_logs: Default::default(),
537 record_debug_steps_info: Default::default(),
538 mocked_calls: Default::default(),
539 mocked_functions: Default::default(),
540 expected_calls: Default::default(),
541 expected_emits: Default::default(),
542 expected_creates: Default::default(),
543 allowed_mem_writes: Default::default(),
544 broadcast: Default::default(),
545 broadcastable_transactions: Default::default(),
546 access_list: Default::default(),
547 test_context: Default::default(),
548 serialized_jsons: Default::default(),
549 eth_deals: Default::default(),
550 gas_metering: Default::default(),
551 gas_snapshots: Default::default(),
552 mapping_slots: Default::default(),
553 pc: Default::default(),
554 breakpoints: Default::default(),
555 intercept_next_create_call: Default::default(),
556 test_runner: Default::default(),
557 ignored_traces: Default::default(),
558 arbitrary_storage: Default::default(),
559 deprecated: Default::default(),
560 wallets: Default::default(),
561 signatures_identifier: Default::default(),
562 dynamic_gas_limit_sequence: Default::default(),
563 }
564 }
565
566 pub fn get_prank(&self, depth: usize) -> Option<&Prank> {
570 self.pranks.range(..=depth).last().map(|(_, prank)| prank)
571 }
572
573 pub fn wallets(&mut self) -> &Wallets {
575 self.wallets.get_or_insert_with(|| Wallets::new(MultiWallet::default(), None))
576 }
577
578 pub fn set_wallets(&mut self, wallets: Wallets) {
580 self.wallets = Some(wallets);
581 }
582
583 pub fn add_delegation(&mut self, authorization: SignedAuthorization) {
585 self.active_delegations.push(authorization);
586 }
587
588 pub fn signatures_identifier(&self) -> Option<&SignaturesIdentifier> {
590 self.signatures_identifier.get_or_init(|| SignaturesIdentifier::new(true).ok()).as_ref()
591 }
592
593 fn apply_cheatcode(
595 &mut self,
596 ecx: Ecx,
597 call: &CallInputs,
598 executor: &mut dyn CheatcodesExecutor,
599 ) -> Result {
600 let decoded = Vm::VmCalls::abi_decode(&call.input.bytes(ecx)).map_err(|e| {
602 if let alloy_sol_types::Error::UnknownSelector { name: _, selector } = e {
603 let msg = format!(
604 "unknown cheatcode with selector {selector}; \
605 you may have a mismatch between the `Vm` interface (likely in `forge-std`) \
606 and the `forge` version"
607 );
608 return alloy_sol_types::Error::Other(std::borrow::Cow::Owned(msg));
609 }
610 e
611 })?;
612
613 let caller = call.caller;
614
615 ecx.journaled_state.database.ensure_cheatcode_access_forking_mode(&caller)?;
618
619 apply_dispatch(
620 &decoded,
621 &mut CheatsCtxt { state: self, ecx, gas_limit: call.gas_limit, caller },
622 executor,
623 )
624 }
625
626 fn allow_cheatcodes_on_create(&self, ecx: Ecx, caller: Address, created_address: Address) {
632 if ecx.journaled_state.depth <= 1
633 || ecx.journaled_state.database.has_cheatcode_access(&caller)
634 {
635 ecx.journaled_state.database.allow_cheatcode_access(created_address);
636 }
637 }
638
639 fn apply_accesslist(&mut self, ecx: Ecx) {
645 if let Some(access_list) = &self.access_list {
646 ecx.tx.access_list = access_list.clone();
647
648 if ecx.tx.tx_type == TransactionType::Legacy as u8 {
649 ecx.tx.tx_type = TransactionType::Eip2930 as u8;
650 }
651 }
652 }
653
654 pub fn on_revert(&mut self, ecx: Ecx) {
659 trace!(deals=?self.eth_deals.len(), "rolling back deals");
660
661 if self.expected_revert.is_some() {
663 return;
664 }
665
666 if ecx.journaled_state.depth() > 0 {
668 return;
669 }
670
671 while let Some(record) = self.eth_deals.pop() {
675 if let Some(acc) = ecx.journaled_state.state.get_mut(&record.address) {
676 acc.info.balance = record.old_balance;
677 }
678 }
679 }
680
681 pub fn call_with_executor(
682 &mut self,
683 ecx: Ecx,
684 call: &mut CallInputs,
685 executor: &mut dyn CheatcodesExecutor,
686 ) -> Option<CallOutcome> {
687 let gas = Gas::new(call.gas_limit);
688 let curr_depth = ecx.journaled_state.depth();
689
690 if curr_depth == 0 {
694 let sender = ecx.tx.caller;
695 let account = match super::evm::journaled_account(ecx, sender) {
696 Ok(account) => account,
697 Err(err) => {
698 return Some(CallOutcome {
699 result: InterpreterResult {
700 result: InstructionResult::Revert,
701 output: err.abi_encode().into(),
702 gas,
703 },
704 memory_offset: call.return_memory_offset.clone(),
705 });
706 }
707 };
708 let prev = account.info.nonce;
709 account.info.nonce = prev.saturating_sub(1);
710
711 trace!(target: "cheatcodes", %sender, nonce=account.info.nonce, prev, "corrected nonce");
712 }
713
714 if call.target_address == CHEATCODE_ADDRESS {
715 return match self.apply_cheatcode(ecx, call, executor) {
716 Ok(retdata) => Some(CallOutcome {
717 result: InterpreterResult {
718 result: InstructionResult::Return,
719 output: retdata.into(),
720 gas,
721 },
722 memory_offset: call.return_memory_offset.clone(),
723 }),
724 Err(err) => Some(CallOutcome {
725 result: InterpreterResult {
726 result: InstructionResult::Revert,
727 output: err.abi_encode().into(),
728 gas,
729 },
730 memory_offset: call.return_memory_offset.clone(),
731 }),
732 };
733 }
734
735 if call.target_address == HARDHAT_CONSOLE_ADDRESS {
736 return None;
737 }
738
739 if let Some(expected_calls_for_target) = self.expected_calls.get_mut(&call.bytecode_address)
743 {
744 for (calldata, (expected, actual_count)) in expected_calls_for_target {
746 if calldata.len() <= call.input.len() &&
749 *calldata == call.input.bytes(ecx)[..calldata.len()] &&
751 expected
753 .value.is_none_or(|value| Some(value) == call.transfer_value()) &&
754 expected.gas.is_none_or(|gas| gas == call.gas_limit) &&
756 expected.min_gas.is_none_or(|min_gas| min_gas <= call.gas_limit)
758 {
759 *actual_count += 1;
760 }
761 }
762 }
763
764 if let Some(mocks) = self.mocked_calls.get_mut(&call.bytecode_address) {
766 let ctx = MockCallDataContext {
767 calldata: call.input.bytes(ecx),
768 value: call.transfer_value(),
769 };
770
771 if let Some(return_data_queue) = match mocks.get_mut(&ctx) {
772 Some(queue) => Some(queue),
773 None => mocks
774 .iter_mut()
775 .find(|(mock, _)| {
776 call.input.bytes(ecx).get(..mock.calldata.len()) == Some(&mock.calldata[..])
777 && mock.value.is_none_or(|value| Some(value) == call.transfer_value())
778 })
779 .map(|(_, v)| v),
780 } && let Some(return_data) = if return_data_queue.len() == 1 {
781 return_data_queue.front().map(|x| x.to_owned())
783 } else {
784 return_data_queue.pop_front()
786 } {
787 return Some(CallOutcome {
788 result: InterpreterResult {
789 result: return_data.ret_type,
790 output: return_data.data,
791 gas,
792 },
793 memory_offset: call.return_memory_offset.clone(),
794 });
795 }
796 }
797
798 if let Some(prank) = &self.get_prank(curr_depth) {
800 if prank.delegate_call
802 && curr_depth == prank.depth
803 && let CallScheme::DelegateCall = call.scheme
804 {
805 call.target_address = prank.new_caller;
806 call.caller = prank.new_caller;
807 if let Some(new_origin) = prank.new_origin {
808 ecx.tx.caller = new_origin;
809 }
810 }
811
812 if curr_depth >= prank.depth && call.caller == prank.prank_caller {
813 let mut prank_applied = false;
814
815 if curr_depth == prank.depth {
817 call.caller = prank.new_caller;
818 prank_applied = true;
819 }
820
821 if let Some(new_origin) = prank.new_origin {
823 ecx.tx.caller = new_origin;
824 prank_applied = true;
825 }
826
827 if prank_applied && let Some(applied_prank) = prank.first_time_applied() {
829 self.pranks.insert(curr_depth, applied_prank);
830 }
831 }
832 }
833
834 self.apply_accesslist(ecx);
836
837 if let Some(broadcast) = &self.broadcast {
839 if curr_depth == broadcast.depth && call.caller == broadcast.original_caller {
844 ecx.tx.caller = broadcast.new_origin;
848
849 call.caller = broadcast.new_origin;
850 if !call.is_static {
855 if let Err(err) = ecx.journaled_state.load_account(broadcast.new_origin) {
856 return Some(CallOutcome {
857 result: InterpreterResult {
858 result: InstructionResult::Revert,
859 output: Error::encode(err),
860 gas,
861 },
862 memory_offset: call.return_memory_offset.clone(),
863 });
864 }
865
866 let (gas_seen, call_seen) =
867 self.dynamic_gas_limit_sequence.take().unwrap_or_default();
868 let mut is_fixed_gas_limit = !(gas_seen && call_seen);
870 if call.gas_limit < 21_000 {
873 is_fixed_gas_limit = false;
874 }
875 let input = TransactionInput::new(call.input.bytes(ecx));
876 ecx.journaled_state.touch(broadcast.new_origin);
878
879 let account =
880 ecx.journaled_state.inner.state().get_mut(&broadcast.new_origin).unwrap();
881
882 let mut tx_req = TransactionRequest {
883 from: Some(broadcast.new_origin),
884 to: Some(TxKind::from(Some(call.target_address))),
885 value: call.transfer_value(),
886 input,
887 nonce: Some(account.info.nonce),
888 chain_id: Some(ecx.cfg.chain_id),
889 gas: if is_fixed_gas_limit { Some(call.gas_limit) } else { None },
890 ..Default::default()
891 };
892
893 let active_delegations = std::mem::take(&mut self.active_delegations);
894 if let Some(blob_sidecar) = self.active_blob_sidecar.take() {
896 if !active_delegations.is_empty() {
898 let msg = "both delegation and blob are active; `attachBlob` and `attachDelegation` are not compatible";
899 return Some(CallOutcome {
900 result: InterpreterResult {
901 result: InstructionResult::Revert,
902 output: Error::encode(msg),
903 gas,
904 },
905 memory_offset: call.return_memory_offset.clone(),
906 });
907 }
908 tx_req.set_blob_sidecar(blob_sidecar);
909 }
910
911 if !active_delegations.is_empty() {
913 for auth in &active_delegations {
914 let Ok(authority) = auth.recover_authority() else {
915 continue;
916 };
917 if authority == broadcast.new_origin {
918 account.info.nonce += 1;
921 }
922 }
923 tx_req.authorization_list = Some(active_delegations);
924 }
925
926 self.broadcastable_transactions.push_back(BroadcastableTransaction {
927 rpc: ecx.journaled_state.database.active_fork_url(),
928 transaction: tx_req.into(),
929 });
930 debug!(target: "cheatcodes", tx=?self.broadcastable_transactions.back().unwrap(), "broadcastable call");
931
932 if !self.config.evm_opts.isolate {
934 let prev = account.info.nonce;
935 account.info.nonce += 1;
936 debug!(target: "cheatcodes", address=%broadcast.new_origin, nonce=prev+1, prev, "incremented nonce");
937 }
938 } else if broadcast.single_call {
939 let msg = "`staticcall`s are not allowed after `broadcast`; use `startBroadcast` instead";
940 return Some(CallOutcome {
941 result: InterpreterResult {
942 result: InstructionResult::Revert,
943 output: Error::encode(msg),
944 gas,
945 },
946 memory_offset: call.return_memory_offset.clone(),
947 });
948 }
949 }
950 }
951
952 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
954 let initialized;
957 let old_balance;
958 let old_nonce;
959 if let Ok(acc) = ecx.journaled_state.load_account(call.target_address) {
960 initialized = acc.info.exists();
961 old_balance = acc.info.balance;
962 old_nonce = acc.info.nonce;
963 } else {
964 initialized = false;
965 old_balance = U256::ZERO;
966 old_nonce = 0;
967 }
968 let kind = match call.scheme {
969 CallScheme::Call => crate::Vm::AccountAccessKind::Call,
970 CallScheme::CallCode => crate::Vm::AccountAccessKind::CallCode,
971 CallScheme::DelegateCall => crate::Vm::AccountAccessKind::DelegateCall,
972 CallScheme::StaticCall => crate::Vm::AccountAccessKind::StaticCall,
973 };
974 recorded_account_diffs_stack.push(vec![AccountAccess {
980 chainInfo: crate::Vm::ChainInfo {
981 forkId: ecx.journaled_state.db().active_fork_id().unwrap_or_default(),
982 chainId: U256::from(ecx.cfg.chain_id),
983 },
984 accessor: call.caller,
985 account: call.bytecode_address,
986 kind,
987 initialized,
988 oldBalance: old_balance,
989 newBalance: U256::ZERO, oldNonce: old_nonce,
991 newNonce: 0, value: call.call_value(),
993 data: call.input.bytes(ecx),
994 reverted: false,
995 deployedCode: Bytes::new(),
996 storageAccesses: vec![], depth: ecx
998 .journaled_state
999 .depth()
1000 .try_into()
1001 .expect("journaled state depth exceeds u64"),
1002 }]);
1003 }
1004
1005 None
1006 }
1007
1008 pub fn rng(&mut self) -> &mut impl Rng {
1009 self.test_runner().rng()
1010 }
1011
1012 pub fn test_runner(&mut self) -> &mut TestRunner {
1013 self.test_runner.get_or_insert_with(|| match self.config.seed {
1014 Some(seed) => TestRunner::new_with_rng(
1015 proptest::test_runner::Config::default(),
1016 TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>()),
1017 ),
1018 None => TestRunner::new(proptest::test_runner::Config::default()),
1019 })
1020 }
1021
1022 pub fn set_seed(&mut self, seed: U256) {
1023 self.test_runner = Some(TestRunner::new_with_rng(
1024 proptest::test_runner::Config::default(),
1025 TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>()),
1026 ));
1027 }
1028
1029 pub fn arbitrary_storage(&mut self) -> &mut ArbitraryStorage {
1032 self.arbitrary_storage.get_or_insert_with(ArbitraryStorage::default)
1033 }
1034
1035 pub fn has_arbitrary_storage(&self, address: &Address) -> bool {
1037 match &self.arbitrary_storage {
1038 Some(storage) => storage.values.contains_key(address),
1039 None => false,
1040 }
1041 }
1042
1043 pub fn should_overwrite_arbitrary_storage(
1047 &self,
1048 address: &Address,
1049 storage_slot: U256,
1050 ) -> bool {
1051 match &self.arbitrary_storage {
1052 Some(storage) => {
1053 storage.overwrites.contains(address)
1054 && storage
1055 .values
1056 .get(address)
1057 .and_then(|arbitrary_values| arbitrary_values.get(&storage_slot))
1058 .is_none()
1059 }
1060 None => false,
1061 }
1062 }
1063
1064 pub fn is_arbitrary_storage_copy(&self, address: &Address) -> bool {
1066 match &self.arbitrary_storage {
1067 Some(storage) => storage.copies.contains_key(address),
1068 None => false,
1069 }
1070 }
1071}
1072
1073impl Inspector<EthEvmContext<&mut dyn DatabaseExt>> for Cheatcodes {
1074 fn initialize_interp(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1075 if let Some(block) = self.block.take() {
1078 ecx.block = block;
1079 }
1080 if let Some(gas_price) = self.gas_price.take() {
1081 ecx.tx.gas_price = gas_price;
1082 }
1083
1084 if self.gas_metering.paused {
1086 self.gas_metering.paused_frames.push(interpreter.gas);
1087 }
1088
1089 if let Some(expected) = &mut self.expected_revert {
1091 expected.max_depth = max(ecx.journaled_state.depth(), expected.max_depth);
1092 }
1093 }
1094
1095 fn step(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1096 self.pc = interpreter.bytecode.pc();
1097
1098 if self.broadcast.is_some() {
1099 self.record_gas_limit_opcode(interpreter);
1100 }
1101
1102 if self.gas_metering.paused {
1104 self.meter_gas(interpreter);
1105 }
1106
1107 if self.gas_metering.reset {
1109 self.meter_gas_reset(interpreter);
1110 }
1111
1112 if self.recording_accesses {
1114 self.record_accesses(interpreter);
1115 }
1116
1117 if self.recorded_account_diffs_stack.is_some() {
1119 self.record_state_diffs(interpreter, ecx);
1120 }
1121
1122 if !self.allowed_mem_writes.is_empty() {
1124 self.check_mem_opcodes(
1125 interpreter,
1126 ecx.journaled_state.depth().try_into().expect("journaled state depth exceeds u64"),
1127 );
1128 }
1129
1130 if let Some(mapping_slots) = &mut self.mapping_slots {
1132 mapping_step(mapping_slots, interpreter);
1133 }
1134
1135 if self.gas_metering.recording {
1137 self.meter_gas_record(interpreter, ecx);
1138 }
1139 }
1140
1141 fn step_end(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1142 if self.broadcast.is_some() {
1143 self.set_gas_limit_type(interpreter);
1144 }
1145
1146 if self.gas_metering.paused {
1147 self.meter_gas_end(interpreter);
1148 }
1149
1150 if self.gas_metering.touched {
1151 self.meter_gas_check(interpreter);
1152 }
1153
1154 if self.arbitrary_storage.is_some() {
1156 self.arbitrary_storage_end(interpreter, ecx);
1157 }
1158 }
1159
1160 fn log(&mut self, interpreter: &mut Interpreter, _ecx: Ecx, log: Log) {
1161 if !self.expected_emits.is_empty() {
1162 expect::handle_expect_emit(self, &log, interpreter);
1163 }
1164
1165 if let Some(storage_recorded_logs) = &mut self.recorded_logs {
1167 storage_recorded_logs.push(Vm::Log {
1168 topics: log.data.topics().to_vec(),
1169 data: log.data.data.clone(),
1170 emitter: log.address,
1171 });
1172 }
1173 }
1174
1175 fn call(&mut self, ecx: Ecx, inputs: &mut CallInputs) -> Option<CallOutcome> {
1176 Self::call_with_executor(self, ecx, inputs, &mut TransparentCheatcodesExecutor)
1177 }
1178
1179 fn call_end(&mut self, ecx: Ecx, call: &CallInputs, outcome: &mut CallOutcome) {
1180 let cheatcode_call = call.target_address == CHEATCODE_ADDRESS
1181 || call.target_address == HARDHAT_CONSOLE_ADDRESS;
1182
1183 if !cheatcode_call {
1187 let curr_depth = ecx.journaled_state.depth();
1189 if let Some(prank) = &self.get_prank(curr_depth)
1190 && curr_depth == prank.depth
1191 {
1192 ecx.tx.caller = prank.prank_origin;
1193
1194 if prank.single_call {
1196 self.pranks.remove(&curr_depth);
1197 }
1198 }
1199
1200 if let Some(broadcast) = &self.broadcast
1202 && curr_depth == broadcast.depth
1203 {
1204 ecx.tx.caller = broadcast.original_origin;
1205
1206 if broadcast.single_call {
1208 let _ = self.broadcast.take();
1209 }
1210 }
1211 }
1212
1213 if let Some(assume_no_revert) = &mut self.assume_no_revert {
1215 if outcome.result.is_revert() && assume_no_revert.reverted_by.is_none() {
1218 assume_no_revert.reverted_by = Some(call.target_address);
1219 }
1220
1221 let curr_depth = ecx.journaled_state.depth();
1223 if curr_depth <= assume_no_revert.depth && !cheatcode_call {
1224 if outcome.result.is_revert() {
1227 let assume_no_revert = std::mem::take(&mut self.assume_no_revert).unwrap();
1228 return match revert_handlers::handle_assume_no_revert(
1229 &assume_no_revert,
1230 outcome.result.result,
1231 &outcome.result.output,
1232 &self.config.available_artifacts,
1233 ) {
1234 Ok(_) => {
1237 outcome.result.output = Error::from(MAGIC_ASSUME).abi_encode().into();
1238 }
1239 Err(error) => {
1242 trace!(expected=?assume_no_revert, ?error, status=?outcome.result.result, "Expected revert mismatch");
1243 outcome.result.result = InstructionResult::Revert;
1244 outcome.result.output = error.abi_encode().into();
1245 }
1246 };
1247 } else {
1248 self.assume_no_revert = None;
1250 }
1251 }
1252 }
1253
1254 if let Some(expected_revert) = &mut self.expected_revert {
1256 if outcome.result.is_revert() {
1259 if expected_revert.reverter.is_some()
1263 && (expected_revert.reverted_by.is_none() || expected_revert.count > 1)
1264 {
1265 expected_revert.reverted_by = Some(call.target_address);
1266 }
1267 }
1268
1269 let curr_depth = ecx.journaled_state.depth();
1270 if curr_depth <= expected_revert.depth {
1271 let needs_processing = match expected_revert.kind {
1272 ExpectedRevertKind::Default => !cheatcode_call,
1273 ExpectedRevertKind::Cheatcode { pending_processing } => {
1276 cheatcode_call && !pending_processing
1277 }
1278 };
1279
1280 if needs_processing {
1281 let mut expected_revert = std::mem::take(&mut self.expected_revert).unwrap();
1282 return match revert_handlers::handle_expect_revert(
1283 cheatcode_call,
1284 false,
1285 self.config.internal_expect_revert,
1286 &expected_revert,
1287 outcome.result.result,
1288 outcome.result.output.clone(),
1289 &self.config.available_artifacts,
1290 ) {
1291 Err(error) => {
1292 trace!(expected=?expected_revert, ?error, status=?outcome.result.result, "Expected revert mismatch");
1293 outcome.result.result = InstructionResult::Revert;
1294 outcome.result.output = error.abi_encode().into();
1295 }
1296 Ok((_, retdata)) => {
1297 expected_revert.actual_count += 1;
1298 if expected_revert.actual_count < expected_revert.count {
1299 self.expected_revert = Some(expected_revert.clone());
1300 }
1301 outcome.result.result = InstructionResult::Return;
1302 outcome.result.output = retdata;
1303 }
1304 };
1305 }
1306
1307 if let ExpectedRevertKind::Cheatcode { pending_processing } =
1310 &mut self.expected_revert.as_mut().unwrap().kind
1311 {
1312 *pending_processing = false;
1313 }
1314 }
1315 }
1316
1317 if cheatcode_call {
1320 return;
1321 }
1322
1323 let gas = outcome.result.gas;
1326 self.gas_metering.last_call_gas = Some(crate::Vm::Gas {
1327 gasLimit: gas.limit(),
1328 gasTotalUsed: gas.spent(),
1329 gasMemoryUsed: 0,
1330 gasRefunded: gas.refunded(),
1331 gasRemaining: gas.remaining(),
1332 });
1333
1334 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1337 if ecx.journaled_state.depth() > 0
1339 && let Some(last_recorded_depth) = &mut recorded_account_diffs_stack.pop()
1340 {
1341 if outcome.result.is_revert() {
1344 last_recorded_depth.iter_mut().for_each(|element| {
1345 element.reverted = true;
1346 element
1347 .storageAccesses
1348 .iter_mut()
1349 .for_each(|storage_access| storage_access.reverted = true);
1350 })
1351 }
1352
1353 if let Some(call_access) = last_recorded_depth.first_mut() {
1354 let curr_depth = ecx.journaled_state.depth();
1359 if call_access.depth == curr_depth as u64
1360 && let Ok(acc) = ecx.journaled_state.load_account(call.target_address)
1361 {
1362 debug_assert!(access_is_call(call_access.kind));
1363 call_access.newBalance = acc.info.balance;
1364 call_access.newNonce = acc.info.nonce;
1365 }
1366 if let Some(last) = recorded_account_diffs_stack.last_mut() {
1371 last.append(last_recorded_depth);
1372 } else {
1373 recorded_account_diffs_stack.push(last_recorded_depth.clone());
1374 }
1375 }
1376 }
1377 }
1378
1379 let should_check_emits = self
1391 .expected_emits
1392 .iter()
1393 .any(|(expected, _)| {
1394 let curr_depth = ecx.journaled_state.depth();
1395 expected.depth == curr_depth
1396 }) &&
1397 !call.is_static;
1399 if should_check_emits {
1400 let expected_counts = self
1401 .expected_emits
1402 .iter()
1403 .filter_map(|(expected, count_map)| {
1404 let count = match expected.address {
1405 Some(emitter) => match count_map.get(&emitter) {
1406 Some(log_count) => expected
1407 .log
1408 .as_ref()
1409 .map(|l| log_count.count(l))
1410 .unwrap_or_else(|| log_count.count_unchecked()),
1411 None => 0,
1412 },
1413 None => match &expected.log {
1414 Some(log) => count_map.values().map(|logs| logs.count(log)).sum(),
1415 None => count_map.values().map(|logs| logs.count_unchecked()).sum(),
1416 },
1417 };
1418
1419 if count != expected.count { Some((expected, count)) } else { None }
1420 })
1421 .collect::<Vec<_>>();
1422
1423 if let Some((expected, _)) = self
1425 .expected_emits
1426 .iter()
1427 .find(|(expected, _)| !expected.found && expected.count > 0)
1428 {
1429 outcome.result.result = InstructionResult::Revert;
1430 let error_msg = expected.mismatch_error.as_deref().unwrap_or("log != expected log");
1431 outcome.result.output = error_msg.abi_encode().into();
1432 return;
1433 }
1434
1435 if !expected_counts.is_empty() {
1436 let msg = if outcome.result.is_ok() {
1437 let (expected, count) = expected_counts.first().unwrap();
1438 format!("log emitted {count} times, expected {}", expected.count)
1439 } else {
1440 "expected an emit, but the call reverted instead. \
1441 ensure you're testing the happy path when using `expectEmit`"
1442 .to_string()
1443 };
1444
1445 outcome.result.result = InstructionResult::Revert;
1446 outcome.result.output = Error::encode(msg);
1447 return;
1448 }
1449
1450 self.expected_emits.clear()
1454 }
1455
1456 let diag = self.fork_revert_diagnostic.take();
1459
1460 if outcome.result.is_revert()
1463 && let Some(err) = diag
1464 {
1465 outcome.result.output = Error::encode(err.to_error_msg(&self.labels));
1466 return;
1467 }
1468
1469 if let TxKind::Call(test_contract) = ecx.tx.kind {
1472 if ecx.journaled_state.db().is_forked_mode()
1475 && outcome.result.result == InstructionResult::Stop
1476 && call.target_address != test_contract
1477 {
1478 let journaled_state = ecx.journaled_state.clone();
1479 self.fork_revert_diagnostic =
1480 ecx.journaled_state.db().diagnose_revert(call.target_address, &journaled_state);
1481 }
1482 }
1483
1484 if ecx.journaled_state.depth() == 0 {
1486 if outcome.result.is_revert() {
1490 return;
1491 }
1492
1493 for (address, calldatas) in &self.expected_calls {
1498 for (calldata, (expected, actual_count)) in calldatas {
1500 let ExpectedCallData { gas, min_gas, value, count, call_type } = expected;
1502
1503 let failed = match call_type {
1504 ExpectedCallType::Count => *count != *actual_count,
1508 ExpectedCallType::NonCount => *count > *actual_count,
1513 };
1514 if failed {
1515 let expected_values = [
1516 Some(format!("data {}", hex::encode_prefixed(calldata))),
1517 value.as_ref().map(|v| format!("value {v}")),
1518 gas.map(|g| format!("gas {g}")),
1519 min_gas.map(|g| format!("minimum gas {g}")),
1520 ]
1521 .into_iter()
1522 .flatten()
1523 .join(", ");
1524 let but = if outcome.result.is_ok() {
1525 let s = if *actual_count == 1 { "" } else { "s" };
1526 format!("was called {actual_count} time{s}")
1527 } else {
1528 "the call reverted instead; \
1529 ensure you're testing the happy path when using `expectCall`"
1530 .to_string()
1531 };
1532 let s = if *count == 1 { "" } else { "s" };
1533 let msg = format!(
1534 "expected call to {address} with {expected_values} \
1535 to be called {count} time{s}, but {but}"
1536 );
1537 outcome.result.result = InstructionResult::Revert;
1538 outcome.result.output = Error::encode(msg);
1539
1540 return;
1541 }
1542 }
1543 }
1544
1545 for (expected, _) in &mut self.expected_emits {
1549 if expected.count == 0 && !expected.found {
1550 expected.found = true;
1551 }
1552 }
1553 self.expected_emits.retain(|(expected, _)| !expected.found);
1554 if !self.expected_emits.is_empty() {
1556 let msg = if outcome.result.is_ok() {
1557 "expected an emit, but no logs were emitted afterwards. \
1558 you might have mismatched events or not enough events were emitted"
1559 } else {
1560 "expected an emit, but the call reverted instead. \
1561 ensure you're testing the happy path when using `expectEmit`"
1562 };
1563 outcome.result.result = InstructionResult::Revert;
1564 outcome.result.output = Error::encode(msg);
1565 return;
1566 }
1567
1568 if let Some(expected_create) = self.expected_creates.first() {
1570 let msg = format!(
1571 "expected {} call by address {} for bytecode {} but not found",
1572 expected_create.create_scheme,
1573 hex::encode_prefixed(expected_create.deployer),
1574 hex::encode_prefixed(&expected_create.bytecode),
1575 );
1576 outcome.result.result = InstructionResult::Revert;
1577 outcome.result.output = Error::encode(msg);
1578 }
1579 }
1580 }
1581
1582 fn create(&mut self, ecx: Ecx, mut input: &mut CreateInputs) -> Option<CreateOutcome> {
1583 let gas = Gas::new(input.gas_limit());
1584 if self.intercept_next_create_call {
1586 self.intercept_next_create_call = false;
1588
1589 let output = input.init_code();
1591
1592 return Some(CreateOutcome {
1594 result: InterpreterResult { result: InstructionResult::Revert, output, gas },
1595 address: None,
1596 });
1597 }
1598
1599 let curr_depth = ecx.journaled_state.depth();
1600
1601 if let Some(prank) = &self.get_prank(curr_depth)
1603 && curr_depth >= prank.depth
1604 && input.caller() == prank.prank_caller
1605 {
1606 let mut prank_applied = false;
1607
1608 if curr_depth == prank.depth {
1610 input.set_caller(prank.new_caller);
1611 prank_applied = true;
1612 }
1613
1614 if let Some(new_origin) = prank.new_origin {
1616 ecx.tx.caller = new_origin;
1617 prank_applied = true;
1618 }
1619
1620 if prank_applied && let Some(applied_prank) = prank.first_time_applied() {
1622 self.pranks.insert(curr_depth, applied_prank);
1623 }
1624 }
1625
1626 self.apply_accesslist(ecx);
1628
1629 if let Some(broadcast) = &self.broadcast
1631 && curr_depth >= broadcast.depth
1632 && input.caller() == broadcast.original_caller
1633 {
1634 if let Err(err) = ecx.journaled_state.load_account(broadcast.new_origin) {
1635 return Some(CreateOutcome {
1636 result: InterpreterResult {
1637 result: InstructionResult::Revert,
1638 output: Error::encode(err),
1639 gas,
1640 },
1641 address: None,
1642 });
1643 }
1644
1645 ecx.tx.caller = broadcast.new_origin;
1646
1647 if curr_depth == broadcast.depth {
1648 input.set_caller(broadcast.new_origin);
1649
1650 ecx.journaled_state.touch(broadcast.new_origin);
1652
1653 let account = &ecx.journaled_state.inner.state()[&broadcast.new_origin];
1654 self.broadcastable_transactions.push_back(BroadcastableTransaction {
1655 rpc: ecx.journaled_state.database.active_fork_url(),
1656 transaction: TransactionRequest {
1657 from: Some(broadcast.new_origin),
1658 to: None,
1659 value: Some(input.value()),
1660 input: TransactionInput::new(input.init_code()),
1661 nonce: Some(account.info.nonce),
1662 ..Default::default()
1663 }
1664 .into(),
1665 });
1666
1667 input.log_debug(self, &input.scheme().unwrap_or(CreateScheme::Create));
1668 }
1669 }
1670
1671 let address = input.allow_cheatcodes(self, ecx);
1673
1674 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1676 recorded_account_diffs_stack.push(vec![AccountAccess {
1677 chainInfo: crate::Vm::ChainInfo {
1678 forkId: ecx.journaled_state.db().active_fork_id().unwrap_or_default(),
1679 chainId: U256::from(ecx.cfg.chain_id),
1680 },
1681 accessor: input.caller(),
1682 account: address,
1683 kind: crate::Vm::AccountAccessKind::Create,
1684 initialized: true,
1685 oldBalance: U256::ZERO, newBalance: U256::ZERO, oldNonce: 0, newNonce: 1, value: input.value(),
1690 data: input.init_code(),
1691 reverted: false,
1692 deployedCode: Bytes::new(), storageAccesses: vec![], depth: curr_depth as u64,
1695 }]);
1696 }
1697
1698 None
1699 }
1700
1701 fn create_end(&mut self, ecx: Ecx, call: &CreateInputs, outcome: &mut CreateOutcome) {
1702 let call = Some(call);
1703 let curr_depth = ecx.journaled_state.depth();
1704
1705 if let Some(prank) = &self.get_prank(curr_depth)
1707 && curr_depth == prank.depth
1708 {
1709 ecx.tx.caller = prank.prank_origin;
1710
1711 if prank.single_call {
1713 std::mem::take(&mut self.pranks);
1714 }
1715 }
1716
1717 if let Some(broadcast) = &self.broadcast
1719 && curr_depth == broadcast.depth
1720 {
1721 ecx.tx.caller = broadcast.original_origin;
1722
1723 if broadcast.single_call {
1725 std::mem::take(&mut self.broadcast);
1726 }
1727 }
1728
1729 if let Some(expected_revert) = &self.expected_revert
1731 && curr_depth <= expected_revert.depth
1732 && matches!(expected_revert.kind, ExpectedRevertKind::Default)
1733 {
1734 let mut expected_revert = std::mem::take(&mut self.expected_revert).unwrap();
1735 return match revert_handlers::handle_expect_revert(
1736 false,
1737 true,
1738 self.config.internal_expect_revert,
1739 &expected_revert,
1740 outcome.result.result,
1741 outcome.result.output.clone(),
1742 &self.config.available_artifacts,
1743 ) {
1744 Ok((address, retdata)) => {
1745 expected_revert.actual_count += 1;
1746 if expected_revert.actual_count < expected_revert.count {
1747 self.expected_revert = Some(expected_revert.clone());
1748 }
1749
1750 outcome.result.result = InstructionResult::Return;
1751 outcome.result.output = retdata;
1752 outcome.address = address;
1753 }
1754 Err(err) => {
1755 outcome.result.result = InstructionResult::Revert;
1756 outcome.result.output = err.abi_encode().into();
1757 }
1758 };
1759 }
1760
1761 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1764 if curr_depth > 0
1766 && let Some(last_depth) = &mut recorded_account_diffs_stack.pop()
1767 {
1768 if outcome.result.is_revert() {
1771 last_depth.iter_mut().for_each(|element| {
1772 element.reverted = true;
1773 element
1774 .storageAccesses
1775 .iter_mut()
1776 .for_each(|storage_access| storage_access.reverted = true);
1777 })
1778 }
1779
1780 if let Some(create_access) = last_depth.first_mut() {
1781 let depth = ecx.journaled_state.depth();
1786 if create_access.depth == depth as u64 {
1787 debug_assert_eq!(
1788 create_access.kind as u8,
1789 crate::Vm::AccountAccessKind::Create as u8
1790 );
1791 if let Some(address) = outcome.address
1792 && let Ok(created_acc) = ecx.journaled_state.load_account(address)
1793 {
1794 create_access.newBalance = created_acc.info.balance;
1795 create_access.newNonce = created_acc.info.nonce;
1796 create_access.deployedCode =
1797 created_acc.info.code.clone().unwrap_or_default().original_bytes();
1798 }
1799 }
1800 if let Some(last) = recorded_account_diffs_stack.last_mut() {
1805 last.append(last_depth);
1806 } else {
1807 recorded_account_diffs_stack.push(last_depth.clone());
1808 }
1809 }
1810 }
1811 }
1812
1813 if !self.expected_creates.is_empty()
1815 && let (Some(address), Some(call)) = (outcome.address, call)
1816 && let Ok(created_acc) = ecx.journaled_state.load_account(address)
1817 {
1818 let bytecode = created_acc.info.code.clone().unwrap_or_default().original_bytes();
1819 if let Some((index, _)) =
1820 self.expected_creates.iter().find_position(|expected_create| {
1821 expected_create.deployer == call.caller
1822 && expected_create.create_scheme.eq(call.scheme.into())
1823 && expected_create.bytecode == bytecode
1824 })
1825 {
1826 self.expected_creates.swap_remove(index);
1827 }
1828 }
1829 }
1830}
1831
1832impl InspectorExt for Cheatcodes {
1833 fn should_use_create2_factory(&mut self, ecx: Ecx, inputs: &CreateInputs) -> bool {
1834 if let CreateScheme::Create2 { .. } = inputs.scheme {
1835 let depth = ecx.journaled_state.depth();
1836 let target_depth = if let Some(prank) = &self.get_prank(depth) {
1837 prank.depth
1838 } else if let Some(broadcast) = &self.broadcast {
1839 broadcast.depth
1840 } else {
1841 1
1842 };
1843
1844 depth == target_depth
1845 && (self.broadcast.is_some() || self.config.always_use_create_2_factory)
1846 } else {
1847 false
1848 }
1849 }
1850
1851 fn create2_deployer(&self) -> Address {
1852 self.config.evm_opts.create2_deployer
1853 }
1854}
1855
1856impl Cheatcodes {
1857 #[cold]
1858 fn meter_gas(&mut self, interpreter: &mut Interpreter) {
1859 if let Some(paused_gas) = self.gas_metering.paused_frames.last() {
1860 let memory = *interpreter.gas.memory();
1863 interpreter.gas = *paused_gas;
1864 interpreter.gas.memory_mut().words_num = memory.words_num;
1865 interpreter.gas.memory_mut().expansion_cost = memory.expansion_cost;
1866 } else {
1867 self.gas_metering.paused_frames.push(interpreter.gas);
1869 }
1870 }
1871
1872 #[cold]
1873 fn meter_gas_record(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1874 if interpreter.bytecode.action.as_ref().and_then(|i| i.instruction_result()).is_none() {
1875 self.gas_metering.gas_records.iter_mut().for_each(|record| {
1876 let curr_depth = ecx.journaled_state.depth();
1877 if curr_depth == record.depth {
1878 if self.gas_metering.last_gas_used != 0 {
1881 let gas_diff =
1882 interpreter.gas.spent().saturating_sub(self.gas_metering.last_gas_used);
1883 record.gas_used = record.gas_used.saturating_add(gas_diff);
1884 }
1885
1886 self.gas_metering.last_gas_used = interpreter.gas.spent();
1889 }
1890 });
1891 }
1892 }
1893
1894 #[cold]
1895 fn meter_gas_end(&mut self, interpreter: &mut Interpreter) {
1896 if let Some(interpreter_action) = interpreter.bytecode.action.as_ref()
1898 && will_exit(interpreter_action)
1899 {
1900 self.gas_metering.paused_frames.pop();
1901 }
1902 }
1903
1904 #[cold]
1905 fn meter_gas_reset(&mut self, interpreter: &mut Interpreter) {
1906 interpreter.gas = Gas::new(interpreter.gas.limit());
1907 self.gas_metering.reset = false;
1908 }
1909
1910 #[cold]
1911 fn meter_gas_check(&mut self, interpreter: &mut Interpreter) {
1912 if let Some(interpreter_action) = interpreter.bytecode.action.as_ref()
1913 && will_exit(interpreter_action)
1914 {
1915 if interpreter.gas.spent()
1919 < u64::try_from(interpreter.gas.refunded()).unwrap_or_default()
1920 {
1921 interpreter.gas = Gas::new(interpreter.gas.limit());
1922 }
1923 }
1924 }
1925
1926 #[cold]
1934 fn arbitrary_storage_end(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1935 let (key, target_address) = if interpreter.bytecode.opcode() == op::SLOAD {
1936 (try_or_return!(interpreter.stack.peek(0)), interpreter.input.target_address)
1937 } else {
1938 return;
1939 };
1940
1941 let Some(value) = ecx.sload(target_address, key) else {
1942 return;
1943 };
1944
1945 if (value.is_cold && value.data.is_zero())
1946 || self.should_overwrite_arbitrary_storage(&target_address, key)
1947 {
1948 if self.has_arbitrary_storage(&target_address) {
1949 let arbitrary_value = self.rng().random();
1950 self.arbitrary_storage.as_mut().unwrap().save(
1951 ecx,
1952 target_address,
1953 key,
1954 arbitrary_value,
1955 );
1956 } else if self.is_arbitrary_storage_copy(&target_address) {
1957 let arbitrary_value = self.rng().random();
1958 self.arbitrary_storage.as_mut().unwrap().copy(
1959 ecx,
1960 target_address,
1961 key,
1962 arbitrary_value,
1963 );
1964 }
1965 }
1966 }
1967
1968 #[cold]
1970 fn record_accesses(&mut self, interpreter: &mut Interpreter) {
1971 let access = &mut self.accesses;
1972 match interpreter.bytecode.opcode() {
1973 op::SLOAD => {
1974 let key = try_or_return!(interpreter.stack.peek(0));
1975 access.record_read(interpreter.input.target_address, key);
1976 }
1977 op::SSTORE => {
1978 let key = try_or_return!(interpreter.stack.peek(0));
1979 access.record_write(interpreter.input.target_address, key);
1980 }
1981 _ => {}
1982 }
1983 }
1984
1985 #[cold]
1986 fn record_state_diffs(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1987 let Some(account_accesses) = &mut self.recorded_account_diffs_stack else { return };
1988 match interpreter.bytecode.opcode() {
1989 op::SELFDESTRUCT => {
1990 let Some(last) = account_accesses.last_mut() else { return };
1992
1993 let target = try_or_return!(interpreter.stack.peek(0));
1995 let target = Address::from_word(B256::from(target));
1996 let (initialized, old_balance, old_nonce) = ecx
1997 .journaled_state
1998 .load_account(target)
1999 .map(|account| {
2000 (account.info.exists(), account.info.balance, account.info.nonce)
2001 })
2002 .unwrap_or_default();
2003
2004 let value = ecx
2006 .balance(interpreter.input.target_address)
2007 .map(|b| b.data)
2008 .unwrap_or(U256::ZERO);
2009
2010 last.push(crate::Vm::AccountAccess {
2012 chainInfo: crate::Vm::ChainInfo {
2013 forkId: ecx.journaled_state.database.active_fork_id().unwrap_or_default(),
2014 chainId: U256::from(ecx.cfg.chain_id),
2015 },
2016 accessor: interpreter.input.target_address,
2017 account: target,
2018 kind: crate::Vm::AccountAccessKind::SelfDestruct,
2019 initialized,
2020 oldBalance: old_balance,
2021 newBalance: old_balance + value,
2022 oldNonce: old_nonce,
2023 newNonce: old_nonce, value,
2025 data: Bytes::new(),
2026 reverted: false,
2027 deployedCode: Bytes::new(),
2028 storageAccesses: vec![],
2029 depth: ecx
2030 .journaled_state
2031 .depth()
2032 .try_into()
2033 .expect("journaled state depth exceeds u64"),
2034 });
2035 }
2036
2037 op::SLOAD => {
2038 let Some(last) = account_accesses.last_mut() else { return };
2039
2040 let key = try_or_return!(interpreter.stack.peek(0));
2041 let address = interpreter.input.target_address;
2042
2043 let mut present_value = U256::ZERO;
2046 if ecx.journaled_state.load_account(address).is_ok()
2048 && let Some(previous) = ecx.sload(address, key)
2049 {
2050 present_value = previous.data;
2051 }
2052 let access = crate::Vm::StorageAccess {
2053 account: interpreter.input.target_address,
2054 slot: key.into(),
2055 isWrite: false,
2056 previousValue: present_value.into(),
2057 newValue: present_value.into(),
2058 reverted: false,
2059 };
2060 let curr_depth = ecx
2061 .journaled_state
2062 .depth()
2063 .try_into()
2064 .expect("journaled state depth exceeds u64");
2065 append_storage_access(last, access, curr_depth);
2066 }
2067 op::SSTORE => {
2068 let Some(last) = account_accesses.last_mut() else { return };
2069
2070 let key = try_or_return!(interpreter.stack.peek(0));
2071 let value = try_or_return!(interpreter.stack.peek(1));
2072 let address = interpreter.input.target_address;
2073 let mut previous_value = U256::ZERO;
2076 if ecx.journaled_state.load_account(address).is_ok()
2077 && let Some(previous) = ecx.sload(address, key)
2078 {
2079 previous_value = previous.data;
2080 }
2081
2082 let access = crate::Vm::StorageAccess {
2083 account: address,
2084 slot: key.into(),
2085 isWrite: true,
2086 previousValue: previous_value.into(),
2087 newValue: value.into(),
2088 reverted: false,
2089 };
2090 let curr_depth = ecx
2091 .journaled_state
2092 .depth()
2093 .try_into()
2094 .expect("journaled state depth exceeds u64");
2095 append_storage_access(last, access, curr_depth);
2096 }
2097
2098 op::EXTCODECOPY | op::EXTCODESIZE | op::EXTCODEHASH | op::BALANCE => {
2100 let kind = match interpreter.bytecode.opcode() {
2101 op::EXTCODECOPY => crate::Vm::AccountAccessKind::Extcodecopy,
2102 op::EXTCODESIZE => crate::Vm::AccountAccessKind::Extcodesize,
2103 op::EXTCODEHASH => crate::Vm::AccountAccessKind::Extcodehash,
2104 op::BALANCE => crate::Vm::AccountAccessKind::Balance,
2105 _ => unreachable!(),
2106 };
2107 let address =
2108 Address::from_word(B256::from(try_or_return!(interpreter.stack.peek(0))));
2109 let initialized;
2110 let balance;
2111 let nonce;
2112 if let Ok(acc) = ecx.journaled_state.load_account(address) {
2113 initialized = acc.info.exists();
2114 balance = acc.info.balance;
2115 nonce = acc.info.nonce;
2116 } else {
2117 initialized = false;
2118 balance = U256::ZERO;
2119 nonce = 0;
2120 }
2121 let curr_depth = ecx
2122 .journaled_state
2123 .depth()
2124 .try_into()
2125 .expect("journaled state depth exceeds u64");
2126 let account_access = crate::Vm::AccountAccess {
2127 chainInfo: crate::Vm::ChainInfo {
2128 forkId: ecx.journaled_state.database.active_fork_id().unwrap_or_default(),
2129 chainId: U256::from(ecx.cfg.chain_id),
2130 },
2131 accessor: interpreter.input.target_address,
2132 account: address,
2133 kind,
2134 initialized,
2135 oldBalance: balance,
2136 newBalance: balance,
2137 oldNonce: nonce,
2138 newNonce: nonce, value: U256::ZERO,
2140 data: Bytes::new(),
2141 reverted: false,
2142 deployedCode: Bytes::new(),
2143 storageAccesses: vec![],
2144 depth: curr_depth,
2145 };
2146 if let Some(last) = account_accesses.last_mut() {
2149 last.push(account_access);
2150 } else {
2151 account_accesses.push(vec![account_access]);
2152 }
2153 }
2154 _ => {}
2155 }
2156 }
2157
2158 #[cold]
2163 fn check_mem_opcodes(&self, interpreter: &mut Interpreter, depth: u64) {
2164 let Some(ranges) = self.allowed_mem_writes.get(&depth) else {
2165 return;
2166 };
2167
2168 macro_rules! mem_opcode_match {
2177 ($(($opcode:ident, $offset_depth:expr, $size_depth:expr, $writes:expr)),* $(,)?) => {
2178 match interpreter.bytecode.opcode() {
2179 op::MSTORE => {
2184 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2186
2187 if !ranges.iter().any(|range| {
2190 range.contains(&offset) && range.contains(&(offset + 31))
2191 }) {
2192 let value = try_or_return!(interpreter.stack.peek(1)).to_be_bytes::<32>();
2197 if value[..SELECTOR_LEN] == stopExpectSafeMemoryCall::SELECTOR {
2198 return
2199 }
2200
2201 disallowed_mem_write(offset, 32, interpreter, ranges);
2202 return
2203 }
2204 }
2205 op::MSTORE8 => {
2206 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2208
2209 if !ranges.iter().any(|range| range.contains(&offset)) {
2212 disallowed_mem_write(offset, 1, interpreter, ranges);
2213 return
2214 }
2215 }
2216
2217 op::MLOAD => {
2222 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2224
2225 if offset >= interpreter.memory.size() as u64 && !ranges.iter().any(|range| {
2229 range.contains(&offset) && range.contains(&(offset + 31))
2230 }) {
2231 disallowed_mem_write(offset, 32, interpreter, ranges);
2232 return
2233 }
2234 }
2235
2236 op::CALL => {
2241 let dest_offset = try_or_return!(interpreter.stack.peek(5)).saturating_to::<u64>();
2243
2244 let size = try_or_return!(interpreter.stack.peek(6)).saturating_to::<u64>();
2246
2247 let fail_cond = !ranges.iter().any(|range| {
2251 range.contains(&dest_offset) &&
2252 range.contains(&(dest_offset + size.saturating_sub(1)))
2253 });
2254
2255 if fail_cond {
2258 let to = Address::from_word(try_or_return!(interpreter.stack.peek(1)).to_be_bytes::<32>().into());
2262 if to == CHEATCODE_ADDRESS {
2263 let args_offset = try_or_return!(interpreter.stack.peek(3)).saturating_to::<usize>();
2264 let args_size = try_or_return!(interpreter.stack.peek(4)).saturating_to::<usize>();
2265 let memory_word = interpreter.memory.slice_len(args_offset, args_size);
2266 if memory_word[..SELECTOR_LEN] == stopExpectSafeMemoryCall::SELECTOR {
2267 return
2268 }
2269 }
2270
2271 disallowed_mem_write(dest_offset, size, interpreter, ranges);
2272 return
2273 }
2274 }
2275
2276 $(op::$opcode => {
2277 let dest_offset = try_or_return!(interpreter.stack.peek($offset_depth)).saturating_to::<u64>();
2279
2280 let size = try_or_return!(interpreter.stack.peek($size_depth)).saturating_to::<u64>();
2282
2283 let fail_cond = !ranges.iter().any(|range| {
2287 range.contains(&dest_offset) &&
2288 range.contains(&(dest_offset + size.saturating_sub(1)))
2289 }) && ($writes ||
2290 [dest_offset, (dest_offset + size).saturating_sub(1)].into_iter().any(|offset| {
2291 offset >= interpreter.memory.size() as u64
2292 })
2293 );
2294
2295 if fail_cond {
2298 disallowed_mem_write(dest_offset, size, interpreter, ranges);
2299 return
2300 }
2301 })*
2302
2303 _ => {}
2304 }
2305 }
2306 }
2307
2308 mem_opcode_match!(
2311 (CALLDATACOPY, 0, 2, true),
2312 (CODECOPY, 0, 2, true),
2313 (RETURNDATACOPY, 0, 2, true),
2314 (EXTCODECOPY, 1, 3, true),
2315 (CALLCODE, 5, 6, true),
2316 (STATICCALL, 4, 5, true),
2317 (DELEGATECALL, 4, 5, true),
2318 (KECCAK256, 0, 1, false),
2319 (LOG0, 0, 1, false),
2320 (LOG1, 0, 1, false),
2321 (LOG2, 0, 1, false),
2322 (LOG3, 0, 1, false),
2323 (LOG4, 0, 1, false),
2324 (CREATE, 1, 2, false),
2325 (CREATE2, 1, 2, false),
2326 (RETURN, 0, 1, false),
2327 (REVERT, 0, 1, false),
2328 );
2329 }
2330
2331 #[cold]
2332 fn record_gas_limit_opcode(&mut self, interpreter: &mut Interpreter) {
2333 match interpreter.bytecode.opcode() {
2334 op::CREATE2 => self.dynamic_gas_limit_sequence = Some((true, true)),
2336 op::GAS => {
2337 if self.dynamic_gas_limit_sequence.is_none() {
2338 self.dynamic_gas_limit_sequence = Some((true, false));
2340 }
2341 }
2342 _ => {}
2343 }
2344 }
2345
2346 #[cold]
2347 fn set_gas_limit_type(&mut self, interpreter: &mut Interpreter) {
2348 if matches!(self.dynamic_gas_limit_sequence, Some((true, true))) {
2350 return;
2351 }
2352
2353 if matches!(self.dynamic_gas_limit_sequence, Some((true, false)))
2355 && interpreter.bytecode.opcode() == op::CALL
2356 {
2357 self.dynamic_gas_limit_sequence = Some((true, true));
2358 return;
2359 }
2360
2361 self.dynamic_gas_limit_sequence = None;
2363 }
2364}
2365
2366fn disallowed_mem_write(
2372 dest_offset: u64,
2373 size: u64,
2374 interpreter: &mut Interpreter,
2375 ranges: &[Range<u64>],
2376) {
2377 let revert_string = format!(
2378 "memory write at offset 0x{:02X} of size 0x{:02X} not allowed; safe range: {}",
2379 dest_offset,
2380 size,
2381 ranges.iter().map(|r| format!("(0x{:02X}, 0x{:02X}]", r.start, r.end)).join(" U ")
2382 );
2383
2384 interpreter.bytecode.set_action(InterpreterAction::new_return(
2385 InstructionResult::Revert,
2386 Bytes::from(revert_string.into_bytes()),
2387 interpreter.gas,
2388 ));
2389}
2390
2391fn access_is_call(kind: crate::Vm::AccountAccessKind) -> bool {
2393 matches!(
2394 kind,
2395 crate::Vm::AccountAccessKind::Call
2396 | crate::Vm::AccountAccessKind::StaticCall
2397 | crate::Vm::AccountAccessKind::CallCode
2398 | crate::Vm::AccountAccessKind::DelegateCall
2399 )
2400}
2401
2402fn append_storage_access(
2404 last: &mut Vec<AccountAccess>,
2405 storage_access: crate::Vm::StorageAccess,
2406 storage_depth: u64,
2407) {
2408 if !last.is_empty() && last.first().unwrap().depth < storage_depth {
2410 if last.len() == 1 {
2416 last.first_mut().unwrap().storageAccesses.push(storage_access);
2417 } else {
2418 let last_record = last.last_mut().unwrap();
2419 if last_record.kind as u8 == crate::Vm::AccountAccessKind::Resume as u8 {
2420 last_record.storageAccesses.push(storage_access);
2421 } else {
2422 let entry = last.first().unwrap();
2423 let resume_record = crate::Vm::AccountAccess {
2424 chainInfo: crate::Vm::ChainInfo {
2425 forkId: entry.chainInfo.forkId,
2426 chainId: entry.chainInfo.chainId,
2427 },
2428 accessor: entry.accessor,
2429 account: entry.account,
2430 kind: crate::Vm::AccountAccessKind::Resume,
2431 initialized: entry.initialized,
2432 storageAccesses: vec![storage_access],
2433 reverted: entry.reverted,
2434 oldBalance: U256::ZERO,
2436 newBalance: U256::ZERO,
2437 oldNonce: 0,
2438 newNonce: 0,
2439 value: U256::ZERO,
2440 data: Bytes::new(),
2441 deployedCode: Bytes::new(),
2442 depth: entry.depth,
2443 };
2444 last.push(resume_record);
2445 }
2446 }
2447 }
2448}
2449
2450fn apply_dispatch(
2452 calls: &Vm::VmCalls,
2453 ccx: &mut CheatsCtxt,
2454 executor: &mut dyn CheatcodesExecutor,
2455) -> Result {
2456 let cheat = calls_as_dyn_cheatcode(calls);
2457
2458 let _guard = debug_span!(target: "cheatcodes", "apply", id = %cheat.id()).entered();
2459 trace!(target: "cheatcodes", ?cheat, "applying");
2460
2461 if let spec::Status::Deprecated(replacement) = *cheat.status() {
2462 ccx.state.deprecated.insert(cheat.signature(), replacement);
2463 }
2464
2465 let mut result = cheat.dyn_apply(ccx, executor);
2467
2468 if let Err(e) = &mut result
2470 && e.is_str()
2471 {
2472 let name = cheat.name();
2473 if !name.contains("assert") && name != "rpcUrl" {
2477 *e = fmt_err!("vm.{name}: {e}");
2478 }
2479 }
2480
2481 trace!(
2482 target: "cheatcodes",
2483 return = %match &result {
2484 Ok(b) => hex::encode(b),
2485 Err(e) => e.to_string(),
2486 }
2487 );
2488
2489 result
2490}
2491
2492fn calls_as_dyn_cheatcode(calls: &Vm::VmCalls) -> &dyn DynCheatcode {
2493 macro_rules! as_dyn {
2494 ($($variant:ident),*) => {
2495 match calls {
2496 $(Vm::VmCalls::$variant(cheat) => cheat,)*
2497 }
2498 };
2499 }
2500 vm_calls!(as_dyn)
2501}
2502
2503fn will_exit(action: &InterpreterAction) -> bool {
2505 match action {
2506 InterpreterAction::Return(result) => {
2507 result.result.is_ok_or_revert() || result.result.is_error()
2508 }
2509 _ => false,
2510 }
2511}