1use crate::{
4 CheatsConfig, CheatsCtxt, DynCheatcode, Error, Result,
5 Vm::{self, AccountAccess},
6 evm::{
7 DealRecord, GasRecord, RecordAccess,
8 mapping::{self, MappingSlots},
9 mock::{MockCallDataContext, MockCallReturnData},
10 prank::Prank,
11 },
12 inspector::utils::CommonCreateInput,
13 script::{Broadcast, Wallets},
14 test::{
15 assume::AssumeNoRevert,
16 expect::{
17 self, ExpectedCallData, ExpectedCallTracker, ExpectedCallType, ExpectedCreate,
18 ExpectedEmitTracker, ExpectedRevert, ExpectedRevertKind,
19 },
20 revert_handlers,
21 },
22 utils::IgnoredTraces,
23};
24use alloy_consensus::BlobTransactionSidecar;
25use alloy_evm::eth::EthEvmContext;
26use alloy_network::TransactionBuilder4844;
27use alloy_primitives::{
28 Address, B256, Bytes, Log, TxKind, U256, hex,
29 map::{AddressHashMap, HashMap, HashSet},
30};
31use alloy_rpc_types::{
32 AccessList,
33 request::{TransactionInput, TransactionRequest},
34};
35use alloy_sol_types::{SolCall, SolInterface, SolValue};
36use foundry_common::{SELECTOR_LEN, TransactionMaybeSigned, evm::Breakpoints};
37use foundry_evm_core::{
38 InspectorExt,
39 abi::Vm::stopExpectSafeMemoryCall,
40 backend::{DatabaseError, DatabaseExt, RevertDiagnostic},
41 constants::{CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS, MAGIC_ASSUME},
42 evm::{FoundryEvm, new_evm_with_existing_context},
43};
44use foundry_evm_traces::{TracingInspector, TracingInspectorConfig};
45use foundry_wallets::multi_wallet::MultiWallet;
46use itertools::Itertools;
47use proptest::test_runner::{RngAlgorithm, TestRng, TestRunner};
48use rand::Rng;
49use revm::{
50 Inspector, Journal,
51 bytecode::opcode as op,
52 context::{BlockEnv, JournalTr, LocalContext, TransactionType, result::EVMError},
53 context_interface::{CreateScheme, transaction::SignedAuthorization},
54 handler::FrameResult,
55 interpreter::{
56 CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, FrameInput, Gas, Host,
57 InstructionResult, Interpreter, InterpreterAction, InterpreterResult,
58 interpreter_types::{Jumps, LoopControl, MemoryTr},
59 },
60 state::EvmStorageSlot,
61};
62use serde_json::Value;
63use std::{
64 cmp::max,
65 collections::{BTreeMap, VecDeque},
66 fs::File,
67 io::BufReader,
68 ops::Range,
69 path::PathBuf,
70 sync::Arc,
71};
72
73mod utils;
74
75pub type Ecx<'a, 'b, 'c> = &'a mut EthEvmContext<&'b mut (dyn DatabaseExt + 'c)>;
76
77pub trait CheatcodesExecutor {
83 fn get_inspector<'a>(&'a mut self, cheats: &'a mut Cheatcodes) -> Box<dyn InspectorExt + 'a>;
86
87 fn exec_create(
89 &mut self,
90 inputs: CreateInputs,
91 ccx: &mut CheatsCtxt,
92 ) -> Result<CreateOutcome, EVMError<DatabaseError>> {
93 with_evm(self, ccx, |evm| {
94 evm.inner.ctx.journaled_state.depth += 1;
95
96 let frame = FrameInput::Create(Box::new(inputs));
97
98 let outcome = match evm.run_execution(frame)? {
99 FrameResult::Call(_) => unreachable!(),
100 FrameResult::Create(create) => create,
101 };
102
103 evm.inner.ctx.journaled_state.depth -= 1;
104
105 Ok(outcome)
106 })
107 }
108
109 fn console_log(&mut self, ccx: &mut CheatsCtxt, msg: &str) {
110 self.get_inspector(ccx.state).console_log(msg);
111 }
112
113 fn tracing_inspector(&mut self) -> Option<&mut Option<TracingInspector>> {
115 None
116 }
117}
118
119fn with_evm<E, F, O>(
121 executor: &mut E,
122 ccx: &mut CheatsCtxt,
123 f: F,
124) -> Result<O, EVMError<DatabaseError>>
125where
126 E: CheatcodesExecutor + ?Sized,
127 F: for<'a, 'b> FnOnce(
128 &mut FoundryEvm<'a, &'b mut dyn InspectorExt>,
129 ) -> Result<O, EVMError<DatabaseError>>,
130{
131 let mut inspector = executor.get_inspector(ccx.state);
132 let error = std::mem::replace(&mut ccx.ecx.error, Ok(()));
133
134 let ctx = EthEvmContext {
135 block: ccx.ecx.block.clone(),
136 cfg: ccx.ecx.cfg.clone(),
137 tx: ccx.ecx.tx.clone(),
138 journaled_state: Journal {
139 inner: ccx.ecx.journaled_state.inner.clone(),
140 database: &mut *ccx.ecx.journaled_state.database as &mut dyn DatabaseExt,
141 },
142 local: LocalContext::default(),
143 chain: (),
144 error,
145 };
146
147 let mut evm = new_evm_with_existing_context(ctx, &mut *inspector);
148
149 let res = f(&mut evm)?;
150
151 ccx.ecx.journaled_state.inner = evm.inner.ctx.journaled_state.inner;
152 ccx.ecx.block = evm.inner.ctx.block;
153 ccx.ecx.tx = evm.inner.ctx.tx;
154 ccx.ecx.cfg = evm.inner.ctx.cfg;
155 ccx.ecx.error = evm.inner.ctx.error;
156
157 Ok(res)
158}
159
160#[derive(Debug, Default, Clone, Copy)]
163struct TransparentCheatcodesExecutor;
164
165impl CheatcodesExecutor for TransparentCheatcodesExecutor {
166 fn get_inspector<'a>(&'a mut self, cheats: &'a mut Cheatcodes) -> Box<dyn InspectorExt + 'a> {
167 Box::new(cheats)
168 }
169}
170
171macro_rules! try_or_return {
172 ($e:expr) => {
173 match $e {
174 Ok(v) => v,
175 Err(_) => return,
176 }
177 };
178}
179
180#[derive(Debug, Default)]
182pub struct TestContext {
183 pub opened_read_files: HashMap<PathBuf, BufReader<File>>,
185}
186
187impl Clone for TestContext {
189 fn clone(&self) -> Self {
190 Default::default()
191 }
192}
193
194impl TestContext {
195 #[inline]
197 pub fn clear(&mut self) {
198 self.opened_read_files.clear();
199 }
200}
201
202#[derive(Clone, Debug)]
204pub struct BroadcastableTransaction {
205 pub rpc: Option<String>,
207 pub transaction: TransactionMaybeSigned,
209}
210
211#[derive(Clone, Debug, Copy)]
212pub struct RecordDebugStepInfo {
213 pub start_node_idx: usize,
215 pub original_tracer_config: TracingInspectorConfig,
217}
218
219#[derive(Clone, Debug, Default)]
221pub struct GasMetering {
222 pub paused: bool,
224 pub touched: bool,
227 pub reset: bool,
229 pub paused_frames: Vec<Gas>,
231
232 pub active_gas_snapshot: Option<(String, String)>,
234
235 pub last_call_gas: Option<crate::Vm::Gas>,
238
239 pub recording: bool,
241 pub last_gas_used: u64,
243 pub gas_records: Vec<GasRecord>,
245}
246
247impl GasMetering {
248 pub fn start(&mut self) {
250 self.recording = true;
251 }
252
253 pub fn stop(&mut self) {
255 self.recording = false;
256 }
257
258 pub fn resume(&mut self) {
260 if self.paused {
261 self.paused = false;
262 self.touched = true;
263 }
264 self.paused_frames.clear();
265 }
266
267 pub fn reset(&mut self) {
269 self.paused = false;
270 self.touched = true;
271 self.reset = true;
272 self.paused_frames.clear();
273 }
274}
275
276#[derive(Clone, Debug, Default)]
278pub struct ArbitraryStorage {
279 pub values: HashMap<Address, HashMap<U256, U256>>,
283 pub copies: HashMap<Address, Address>,
285 pub overwrites: HashSet<Address>,
287}
288
289impl ArbitraryStorage {
290 pub fn mark_arbitrary(&mut self, address: &Address, overwrite: bool) {
292 self.values.insert(*address, HashMap::default());
293 if overwrite {
294 self.overwrites.insert(*address);
295 } else {
296 self.overwrites.remove(address);
297 }
298 }
299
300 pub fn mark_copy(&mut self, from: &Address, to: &Address) {
302 if self.values.contains_key(from) {
303 self.copies.insert(*to, *from);
304 }
305 }
306
307 pub fn save(&mut self, ecx: Ecx, address: Address, slot: U256, data: U256) {
311 self.values.get_mut(&address).expect("missing arbitrary address entry").insert(slot, data);
312 if let Ok(mut account) = ecx.journaled_state.load_account(address) {
313 account.storage.insert(slot, EvmStorageSlot::new(data, 0));
314 }
315 }
316
317 pub fn copy(&mut self, ecx: Ecx, target: Address, slot: U256, new_value: U256) -> U256 {
323 let source = self.copies.get(&target).expect("missing arbitrary copy target entry");
324 let storage_cache = self.values.get_mut(source).expect("missing arbitrary source storage");
325 let value = match storage_cache.get(&slot) {
326 Some(value) => *value,
327 None => {
328 storage_cache.insert(slot, new_value);
329 if let Ok(mut source_account) = ecx.journaled_state.load_account(*source) {
331 source_account.storage.insert(slot, EvmStorageSlot::new(new_value, 0));
332 }
333 new_value
334 }
335 };
336 if let Ok(mut target_account) = ecx.journaled_state.load_account(target) {
338 target_account.storage.insert(slot, EvmStorageSlot::new(value, 0));
339 }
340 value
341 }
342}
343
344pub type BroadcastableTransactions = VecDeque<BroadcastableTransaction>;
346
347#[derive(Clone, Debug)]
365pub struct Cheatcodes {
366 pub block: Option<BlockEnv>,
371
372 pub active_delegations: Vec<SignedAuthorization>,
376
377 pub active_blob_sidecar: Option<BlobTransactionSidecar>,
379
380 pub gas_price: Option<u128>,
385
386 pub labels: AddressHashMap<String>,
388
389 pub pranks: BTreeMap<usize, Prank>,
391
392 pub expected_revert: Option<ExpectedRevert>,
394
395 pub assume_no_revert: Option<AssumeNoRevert>,
397
398 pub fork_revert_diagnostic: Option<RevertDiagnostic>,
400
401 pub accesses: RecordAccess,
403
404 pub recording_accesses: bool,
406
407 pub recorded_account_diffs_stack: Option<Vec<Vec<AccountAccess>>>,
413
414 pub record_debug_steps_info: Option<RecordDebugStepInfo>,
416
417 pub recorded_logs: Option<Vec<crate::Vm::Log>>,
419
420 pub mocked_calls: HashMap<Address, BTreeMap<MockCallDataContext, VecDeque<MockCallReturnData>>>,
423
424 pub mocked_functions: HashMap<Address, HashMap<Bytes, Address>>,
426
427 pub expected_calls: ExpectedCallTracker,
429 pub expected_emits: ExpectedEmitTracker,
431 pub expected_creates: Vec<ExpectedCreate>,
433
434 pub allowed_mem_writes: HashMap<u64, Vec<Range<u64>>>,
436
437 pub broadcast: Option<Broadcast>,
439
440 pub broadcastable_transactions: BroadcastableTransactions,
442
443 pub access_list: Option<AccessList>,
445
446 pub config: Arc<CheatsConfig>,
448
449 pub test_context: TestContext,
451
452 pub fs_commit: bool,
455
456 pub serialized_jsons: BTreeMap<String, BTreeMap<String, Value>>,
459
460 pub eth_deals: Vec<DealRecord>,
462
463 pub gas_metering: GasMetering,
465
466 pub gas_snapshots: BTreeMap<String, BTreeMap<String, String>>,
469
470 pub mapping_slots: Option<AddressHashMap<MappingSlots>>,
472
473 pub pc: usize,
475 pub breakpoints: Breakpoints,
478
479 pub intercept_next_create_call: bool,
481
482 test_runner: Option<TestRunner>,
485
486 pub ignored_traces: IgnoredTraces,
488
489 pub arbitrary_storage: Option<ArbitraryStorage>,
491
492 pub deprecated: HashMap<&'static str, Option<&'static str>>,
494 pub wallets: Option<Wallets>,
496}
497
498impl Default for Cheatcodes {
502 fn default() -> Self {
503 Self::new(Arc::default())
504 }
505}
506
507impl Cheatcodes {
508 pub fn new(config: Arc<CheatsConfig>) -> Self {
510 Self {
511 fs_commit: true,
512 labels: config.labels.clone(),
513 config,
514 block: Default::default(),
515 active_delegations: Default::default(),
516 active_blob_sidecar: Default::default(),
517 gas_price: Default::default(),
518 pranks: Default::default(),
519 expected_revert: Default::default(),
520 assume_no_revert: Default::default(),
521 fork_revert_diagnostic: Default::default(),
522 accesses: Default::default(),
523 recording_accesses: Default::default(),
524 recorded_account_diffs_stack: Default::default(),
525 recorded_logs: Default::default(),
526 record_debug_steps_info: Default::default(),
527 mocked_calls: Default::default(),
528 mocked_functions: Default::default(),
529 expected_calls: Default::default(),
530 expected_emits: Default::default(),
531 expected_creates: Default::default(),
532 allowed_mem_writes: Default::default(),
533 broadcast: Default::default(),
534 broadcastable_transactions: Default::default(),
535 access_list: Default::default(),
536 test_context: Default::default(),
537 serialized_jsons: Default::default(),
538 eth_deals: Default::default(),
539 gas_metering: Default::default(),
540 gas_snapshots: Default::default(),
541 mapping_slots: Default::default(),
542 pc: Default::default(),
543 breakpoints: Default::default(),
544 intercept_next_create_call: Default::default(),
545 test_runner: Default::default(),
546 ignored_traces: Default::default(),
547 arbitrary_storage: Default::default(),
548 deprecated: Default::default(),
549 wallets: Default::default(),
550 }
551 }
552
553 pub fn get_prank(&self, depth: usize) -> Option<&Prank> {
557 self.pranks.range(..=depth).last().map(|(_, prank)| prank)
558 }
559
560 pub fn wallets(&mut self) -> &Wallets {
562 self.wallets.get_or_insert_with(|| Wallets::new(MultiWallet::default(), None))
563 }
564
565 pub fn set_wallets(&mut self, wallets: Wallets) {
567 self.wallets = Some(wallets);
568 }
569
570 pub fn add_delegation(&mut self, authorization: SignedAuthorization) {
572 self.active_delegations.push(authorization);
573 }
574
575 fn apply_cheatcode(
577 &mut self,
578 ecx: Ecx,
579 call: &CallInputs,
580 executor: &mut dyn CheatcodesExecutor,
581 ) -> Result {
582 let decoded = Vm::VmCalls::abi_decode(&call.input.bytes(ecx)).map_err(|e| {
584 if let alloy_sol_types::Error::UnknownSelector { name: _, selector } = e {
585 let msg = format!(
586 "unknown cheatcode with selector {selector}; \
587 you may have a mismatch between the `Vm` interface (likely in `forge-std`) \
588 and the `forge` version"
589 );
590 return alloy_sol_types::Error::Other(std::borrow::Cow::Owned(msg));
591 }
592 e
593 })?;
594
595 let caller = call.caller;
596
597 ecx.journaled_state.database.ensure_cheatcode_access_forking_mode(&caller)?;
600
601 apply_dispatch(
602 &decoded,
603 &mut CheatsCtxt { state: self, ecx, gas_limit: call.gas_limit, caller },
604 executor,
605 )
606 }
607
608 fn allow_cheatcodes_on_create(&self, ecx: Ecx, caller: Address, created_address: Address) {
614 if ecx.journaled_state.depth <= 1
615 || ecx.journaled_state.database.has_cheatcode_access(&caller)
616 {
617 ecx.journaled_state.database.allow_cheatcode_access(created_address);
618 }
619 }
620
621 fn apply_accesslist(&mut self, ecx: Ecx) {
627 if let Some(access_list) = &self.access_list {
628 ecx.tx.access_list = access_list.clone();
629
630 if ecx.tx.tx_type == TransactionType::Legacy as u8 {
631 ecx.tx.tx_type = TransactionType::Eip2930 as u8;
632 }
633 }
634 }
635
636 pub fn on_revert(&mut self, ecx: Ecx) {
641 trace!(deals=?self.eth_deals.len(), "rolling back deals");
642
643 if self.expected_revert.is_some() {
645 return;
646 }
647
648 if ecx.journaled_state.depth() > 0 {
650 return;
651 }
652
653 while let Some(record) = self.eth_deals.pop() {
657 if let Some(acc) = ecx.journaled_state.state.get_mut(&record.address) {
658 acc.info.balance = record.old_balance;
659 }
660 }
661 }
662
663 pub fn call_with_executor(
664 &mut self,
665 ecx: Ecx,
666 call: &mut CallInputs,
667 executor: &mut impl CheatcodesExecutor,
668 ) -> Option<CallOutcome> {
669 let gas = Gas::new(call.gas_limit);
670 let curr_depth = ecx.journaled_state.depth();
671
672 if curr_depth == 0 {
676 let sender = ecx.tx.caller;
677 let account = match super::evm::journaled_account(ecx, sender) {
678 Ok(account) => account,
679 Err(err) => {
680 return Some(CallOutcome {
681 result: InterpreterResult {
682 result: InstructionResult::Revert,
683 output: err.abi_encode().into(),
684 gas,
685 },
686 memory_offset: call.return_memory_offset.clone(),
687 });
688 }
689 };
690 let prev = account.info.nonce;
691 account.info.nonce = prev.saturating_sub(1);
692
693 trace!(target: "cheatcodes", %sender, nonce=account.info.nonce, prev, "corrected nonce");
694 }
695
696 if call.target_address == CHEATCODE_ADDRESS {
697 return match self.apply_cheatcode(ecx, call, executor) {
698 Ok(retdata) => Some(CallOutcome {
699 result: InterpreterResult {
700 result: InstructionResult::Return,
701 output: retdata.into(),
702 gas,
703 },
704 memory_offset: call.return_memory_offset.clone(),
705 }),
706 Err(err) => Some(CallOutcome {
707 result: InterpreterResult {
708 result: InstructionResult::Revert,
709 output: err.abi_encode().into(),
710 gas,
711 },
712 memory_offset: call.return_memory_offset.clone(),
713 }),
714 };
715 }
716
717 if call.target_address == HARDHAT_CONSOLE_ADDRESS {
718 return None;
719 }
720
721 if let Some(expected_calls_for_target) = self.expected_calls.get_mut(&call.bytecode_address)
725 {
726 for (calldata, (expected, actual_count)) in expected_calls_for_target {
728 if calldata.len() <= call.input.len() &&
731 *calldata == call.input.bytes(ecx)[..calldata.len()] &&
733 expected
735 .value.is_none_or(|value| Some(value) == call.transfer_value()) &&
736 expected.gas.is_none_or(|gas| gas == call.gas_limit) &&
738 expected.min_gas.is_none_or(|min_gas| min_gas <= call.gas_limit)
740 {
741 *actual_count += 1;
742 }
743 }
744 }
745
746 if let Some(mocks) = self.mocked_calls.get_mut(&call.bytecode_address) {
748 let ctx = MockCallDataContext {
749 calldata: call.input.bytes(ecx),
750 value: call.transfer_value(),
751 };
752
753 if let Some(return_data_queue) = match mocks.get_mut(&ctx) {
754 Some(queue) => Some(queue),
755 None => mocks
756 .iter_mut()
757 .find(|(mock, _)| {
758 call.input.bytes(ecx).get(..mock.calldata.len()) == Some(&mock.calldata[..])
759 && mock.value.is_none_or(|value| Some(value) == call.transfer_value())
760 })
761 .map(|(_, v)| v),
762 } && let Some(return_data) = if return_data_queue.len() == 1 {
763 return_data_queue.front().map(|x| x.to_owned())
765 } else {
766 return_data_queue.pop_front()
768 } {
769 return Some(CallOutcome {
770 result: InterpreterResult {
771 result: return_data.ret_type,
772 output: return_data.data,
773 gas,
774 },
775 memory_offset: call.return_memory_offset.clone(),
776 });
777 }
778 }
779
780 if let Some(prank) = &self.get_prank(curr_depth) {
782 if prank.delegate_call
784 && curr_depth == prank.depth
785 && let CallScheme::DelegateCall = call.scheme
786 {
787 call.target_address = prank.new_caller;
788 call.caller = prank.new_caller;
789 if let Some(new_origin) = prank.new_origin {
790 ecx.tx.caller = new_origin;
791 }
792 }
793
794 if curr_depth >= prank.depth && call.caller == prank.prank_caller {
795 let mut prank_applied = false;
796
797 if curr_depth == prank.depth {
799 call.caller = prank.new_caller;
800 prank_applied = true;
801 }
802
803 if let Some(new_origin) = prank.new_origin {
805 ecx.tx.caller = new_origin;
806 prank_applied = true;
807 }
808
809 if prank_applied && let Some(applied_prank) = prank.first_time_applied() {
811 self.pranks.insert(curr_depth, applied_prank);
812 }
813 }
814 }
815
816 self.apply_accesslist(ecx);
818
819 if let Some(broadcast) = &self.broadcast {
821 if curr_depth == broadcast.depth && call.caller == broadcast.original_caller {
826 ecx.tx.caller = broadcast.new_origin;
830
831 call.caller = broadcast.new_origin;
832 if !call.is_static {
837 if let Err(err) = ecx.journaled_state.load_account(broadcast.new_origin) {
838 return Some(CallOutcome {
839 result: InterpreterResult {
840 result: InstructionResult::Revert,
841 output: Error::encode(err),
842 gas,
843 },
844 memory_offset: call.return_memory_offset.clone(),
845 });
846 }
847
848 let is_fixed_gas_limit = check_if_fixed_gas_limit(&ecx, call.gas_limit);
849
850 let input = TransactionInput::new(call.input.bytes(ecx));
851
852 let account =
853 ecx.journaled_state.inner.state().get_mut(&broadcast.new_origin).unwrap();
854
855 let mut tx_req = TransactionRequest {
856 from: Some(broadcast.new_origin),
857 to: Some(TxKind::from(Some(call.target_address))),
858 value: call.transfer_value(),
859 input,
860 nonce: Some(account.info.nonce),
861 chain_id: Some(ecx.cfg.chain_id),
862 gas: if is_fixed_gas_limit { Some(call.gas_limit) } else { None },
863 ..Default::default()
864 };
865
866 let active_delegations = std::mem::take(&mut self.active_delegations);
867 if let Some(blob_sidecar) = self.active_blob_sidecar.take() {
869 if !active_delegations.is_empty() {
871 let msg = "both delegation and blob are active; `attachBlob` and `attachDelegation` are not compatible";
872 return Some(CallOutcome {
873 result: InterpreterResult {
874 result: InstructionResult::Revert,
875 output: Error::encode(msg),
876 gas,
877 },
878 memory_offset: call.return_memory_offset.clone(),
879 });
880 }
881 tx_req.set_blob_sidecar(blob_sidecar);
882 }
883
884 if !active_delegations.is_empty() {
886 for auth in &active_delegations {
887 let Ok(authority) = auth.recover_authority() else {
888 continue;
889 };
890 if authority == broadcast.new_origin {
891 account.info.nonce += 1;
894 }
895 }
896 tx_req.authorization_list = Some(active_delegations);
897 }
898
899 self.broadcastable_transactions.push_back(BroadcastableTransaction {
900 rpc: ecx.journaled_state.database.active_fork_url(),
901 transaction: tx_req.into(),
902 });
903 debug!(target: "cheatcodes", tx=?self.broadcastable_transactions.back().unwrap(), "broadcastable call");
904
905 if !self.config.evm_opts.isolate {
907 let prev = account.info.nonce;
908 account.info.nonce += 1;
909 debug!(target: "cheatcodes", address=%broadcast.new_origin, nonce=prev+1, prev, "incremented nonce");
910 }
911 } else if broadcast.single_call {
912 let msg = "`staticcall`s are not allowed after `broadcast`; use `startBroadcast` instead";
913 return Some(CallOutcome {
914 result: InterpreterResult {
915 result: InstructionResult::Revert,
916 output: Error::encode(msg),
917 gas,
918 },
919 memory_offset: call.return_memory_offset.clone(),
920 });
921 }
922 }
923 }
924
925 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
927 let initialized;
930 let old_balance;
931 if let Ok(acc) = ecx.journaled_state.load_account(call.target_address) {
932 initialized = acc.info.exists();
933 old_balance = acc.info.balance;
934 } else {
935 initialized = false;
936 old_balance = U256::ZERO;
937 }
938 let kind = match call.scheme {
939 CallScheme::Call => crate::Vm::AccountAccessKind::Call,
940 CallScheme::CallCode => crate::Vm::AccountAccessKind::CallCode,
941 CallScheme::DelegateCall => crate::Vm::AccountAccessKind::DelegateCall,
942 CallScheme::StaticCall => crate::Vm::AccountAccessKind::StaticCall,
943 };
944 recorded_account_diffs_stack.push(vec![AccountAccess {
950 chainInfo: crate::Vm::ChainInfo {
951 forkId: ecx.journaled_state.db().active_fork_id().unwrap_or_default(),
952 chainId: U256::from(ecx.cfg.chain_id),
953 },
954 accessor: call.caller,
955 account: call.bytecode_address,
956 kind,
957 initialized,
958 oldBalance: old_balance,
959 newBalance: U256::ZERO, value: call.call_value(),
961 data: call.input.bytes(ecx),
962 reverted: false,
963 deployedCode: Bytes::new(),
964 storageAccesses: vec![], depth: ecx
966 .journaled_state
967 .depth()
968 .try_into()
969 .expect("journaled state depth exceeds u64"),
970 }]);
971 }
972
973 None
974 }
975
976 pub fn rng(&mut self) -> &mut impl Rng {
977 self.test_runner().rng()
978 }
979
980 pub fn test_runner(&mut self) -> &mut TestRunner {
981 self.test_runner.get_or_insert_with(|| match self.config.seed {
982 Some(seed) => TestRunner::new_with_rng(
983 proptest::test_runner::Config::default(),
984 TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>()),
985 ),
986 None => TestRunner::new(proptest::test_runner::Config::default()),
987 })
988 }
989
990 pub fn set_seed(&mut self, seed: U256) {
991 self.test_runner = Some(TestRunner::new_with_rng(
992 proptest::test_runner::Config::default(),
993 TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>()),
994 ));
995 }
996
997 pub fn arbitrary_storage(&mut self) -> &mut ArbitraryStorage {
1000 self.arbitrary_storage.get_or_insert_with(ArbitraryStorage::default)
1001 }
1002
1003 pub fn has_arbitrary_storage(&self, address: &Address) -> bool {
1005 match &self.arbitrary_storage {
1006 Some(storage) => storage.values.contains_key(address),
1007 None => false,
1008 }
1009 }
1010
1011 pub fn should_overwrite_arbitrary_storage(
1015 &self,
1016 address: &Address,
1017 storage_slot: U256,
1018 ) -> bool {
1019 match &self.arbitrary_storage {
1020 Some(storage) => {
1021 storage.overwrites.contains(address)
1022 && storage
1023 .values
1024 .get(address)
1025 .and_then(|arbitrary_values| arbitrary_values.get(&storage_slot))
1026 .is_none()
1027 }
1028 None => false,
1029 }
1030 }
1031
1032 pub fn is_arbitrary_storage_copy(&self, address: &Address) -> bool {
1034 match &self.arbitrary_storage {
1035 Some(storage) => storage.copies.contains_key(address),
1036 None => false,
1037 }
1038 }
1039}
1040
1041impl Inspector<EthEvmContext<&mut dyn DatabaseExt>> for Cheatcodes {
1042 #[inline]
1043 fn initialize_interp(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1044 if let Some(block) = self.block.take() {
1047 ecx.block = block;
1048 }
1049 if let Some(gas_price) = self.gas_price.take() {
1050 ecx.tx.gas_price = gas_price;
1051 }
1052
1053 if self.gas_metering.paused {
1055 self.gas_metering.paused_frames.push(interpreter.gas);
1056 }
1057
1058 if let Some(expected) = &mut self.expected_revert {
1060 expected.max_depth = max(ecx.journaled_state.depth(), expected.max_depth);
1061 }
1062 }
1063
1064 #[inline]
1065 fn step(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1066 self.pc = interpreter.bytecode.pc();
1067
1068 if self.gas_metering.paused {
1070 self.meter_gas(interpreter);
1071 }
1072
1073 if self.gas_metering.reset {
1075 self.meter_gas_reset(interpreter);
1076 }
1077
1078 if self.recording_accesses {
1080 self.record_accesses(interpreter);
1081 }
1082
1083 if self.recorded_account_diffs_stack.is_some() {
1085 self.record_state_diffs(interpreter, ecx);
1086 }
1087
1088 if !self.allowed_mem_writes.is_empty() {
1090 self.check_mem_opcodes(
1091 interpreter,
1092 ecx.journaled_state.depth().try_into().expect("journaled state depth exceeds u64"),
1093 );
1094 }
1095
1096 if let Some(mapping_slots) = &mut self.mapping_slots {
1098 mapping::step(mapping_slots, interpreter);
1099 }
1100
1101 if self.gas_metering.recording {
1103 self.meter_gas_record(interpreter, ecx);
1104 }
1105 }
1106
1107 #[inline]
1108 fn step_end(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1109 if self.gas_metering.paused {
1110 self.meter_gas_end(interpreter);
1111 }
1112
1113 if self.gas_metering.touched {
1114 self.meter_gas_check(interpreter);
1115 }
1116
1117 if self.arbitrary_storage.is_some() {
1119 self.arbitrary_storage_end(interpreter, ecx);
1120 }
1121 }
1122
1123 fn log(&mut self, interpreter: &mut Interpreter, _ecx: Ecx, log: Log) {
1124 if !self.expected_emits.is_empty() {
1125 expect::handle_expect_emit(self, &log, interpreter);
1126 }
1127
1128 if let Some(storage_recorded_logs) = &mut self.recorded_logs {
1130 storage_recorded_logs.push(Vm::Log {
1131 topics: log.data.topics().to_vec(),
1132 data: log.data.data.clone(),
1133 emitter: log.address,
1134 });
1135 }
1136 }
1137
1138 fn call(&mut self, ecx: Ecx, inputs: &mut CallInputs) -> Option<CallOutcome> {
1139 Self::call_with_executor(self, ecx, inputs, &mut TransparentCheatcodesExecutor)
1140 }
1141
1142 fn call_end(&mut self, ecx: Ecx, call: &CallInputs, outcome: &mut CallOutcome) {
1143 let cheatcode_call = call.target_address == CHEATCODE_ADDRESS
1144 || call.target_address == HARDHAT_CONSOLE_ADDRESS;
1145
1146 if !cheatcode_call {
1150 let curr_depth = ecx.journaled_state.depth();
1152 if let Some(prank) = &self.get_prank(curr_depth)
1153 && curr_depth == prank.depth
1154 {
1155 ecx.tx.caller = prank.prank_origin;
1156
1157 if prank.single_call {
1159 self.pranks.remove(&curr_depth);
1160 }
1161 }
1162
1163 if let Some(broadcast) = &self.broadcast
1165 && curr_depth == broadcast.depth
1166 {
1167 ecx.tx.caller = broadcast.original_origin;
1168
1169 if broadcast.single_call {
1171 let _ = self.broadcast.take();
1172 }
1173 }
1174 }
1175
1176 if let Some(assume_no_revert) = &mut self.assume_no_revert {
1178 if outcome.result.is_revert() && assume_no_revert.reverted_by.is_none() {
1181 assume_no_revert.reverted_by = Some(call.target_address);
1182 }
1183
1184 let curr_depth = ecx.journaled_state.depth();
1186 if curr_depth <= assume_no_revert.depth && !cheatcode_call {
1187 if outcome.result.is_revert() {
1190 let assume_no_revert = std::mem::take(&mut self.assume_no_revert).unwrap();
1191 return match revert_handlers::handle_assume_no_revert(
1192 &assume_no_revert,
1193 outcome.result.result,
1194 &outcome.result.output,
1195 &self.config.available_artifacts,
1196 ) {
1197 Ok(_) => {
1200 outcome.result.output = Error::from(MAGIC_ASSUME).abi_encode().into();
1201 }
1202 Err(error) => {
1205 trace!(expected=?assume_no_revert, ?error, status=?outcome.result.result, "Expected revert mismatch");
1206 outcome.result.result = InstructionResult::Revert;
1207 outcome.result.output = error.abi_encode().into();
1208 }
1209 };
1210 } else {
1211 self.assume_no_revert = None;
1213 }
1214 }
1215 }
1216
1217 if let Some(expected_revert) = &mut self.expected_revert {
1219 if outcome.result.is_revert() {
1222 if expected_revert.reverter.is_some()
1226 && (expected_revert.reverted_by.is_none() || expected_revert.count > 1)
1227 {
1228 expected_revert.reverted_by = Some(call.target_address);
1229 }
1230 }
1231
1232 let curr_depth = ecx.journaled_state.depth();
1233 if curr_depth <= expected_revert.depth {
1234 let needs_processing = match expected_revert.kind {
1235 ExpectedRevertKind::Default => !cheatcode_call,
1236 ExpectedRevertKind::Cheatcode { pending_processing } => {
1239 cheatcode_call && !pending_processing
1240 }
1241 };
1242
1243 if needs_processing {
1244 let mut expected_revert = std::mem::take(&mut self.expected_revert).unwrap();
1245 return match revert_handlers::handle_expect_revert(
1246 cheatcode_call,
1247 false,
1248 self.config.internal_expect_revert,
1249 &expected_revert,
1250 outcome.result.result,
1251 outcome.result.output.clone(),
1252 &self.config.available_artifacts,
1253 ) {
1254 Err(error) => {
1255 trace!(expected=?expected_revert, ?error, status=?outcome.result.result, "Expected revert mismatch");
1256 outcome.result.result = InstructionResult::Revert;
1257 outcome.result.output = error.abi_encode().into();
1258 }
1259 Ok((_, retdata)) => {
1260 expected_revert.actual_count += 1;
1261 if expected_revert.actual_count < expected_revert.count {
1262 self.expected_revert = Some(expected_revert.clone());
1263 }
1264 outcome.result.result = InstructionResult::Return;
1265 outcome.result.output = retdata;
1266 }
1267 };
1268 }
1269
1270 if let ExpectedRevertKind::Cheatcode { pending_processing } =
1273 &mut self.expected_revert.as_mut().unwrap().kind
1274 {
1275 *pending_processing = false;
1276 }
1277 }
1278 }
1279
1280 if cheatcode_call {
1283 return;
1284 }
1285
1286 let gas = outcome.result.gas;
1289 self.gas_metering.last_call_gas = Some(crate::Vm::Gas {
1290 gasLimit: gas.limit(),
1291 gasTotalUsed: gas.spent(),
1292 gasMemoryUsed: 0,
1293 gasRefunded: gas.refunded(),
1294 gasRemaining: gas.remaining(),
1295 });
1296
1297 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1300 if ecx.journaled_state.depth() > 0
1302 && let Some(last_recorded_depth) = &mut recorded_account_diffs_stack.pop()
1303 {
1304 if outcome.result.is_revert() {
1307 last_recorded_depth.iter_mut().for_each(|element| {
1308 element.reverted = true;
1309 element
1310 .storageAccesses
1311 .iter_mut()
1312 .for_each(|storage_access| storage_access.reverted = true);
1313 })
1314 }
1315
1316 if let Some(call_access) = last_recorded_depth.first_mut() {
1317 let curr_depth = ecx.journaled_state.depth();
1322 if call_access.depth == curr_depth as u64
1323 && let Ok(acc) = ecx.journaled_state.load_account(call.target_address)
1324 {
1325 debug_assert!(access_is_call(call_access.kind));
1326 call_access.newBalance = acc.info.balance;
1327 }
1328 if let Some(last) = recorded_account_diffs_stack.last_mut() {
1333 last.append(last_recorded_depth);
1334 } else {
1335 recorded_account_diffs_stack.push(last_recorded_depth.clone());
1336 }
1337 }
1338 }
1339 }
1340
1341 let should_check_emits = self
1353 .expected_emits
1354 .iter()
1355 .any(|(expected, _)| {
1356 let curr_depth = ecx.journaled_state.depth();
1357 expected.depth == curr_depth
1358 }) &&
1359 !call.is_static;
1361 if should_check_emits {
1362 let expected_counts = self
1363 .expected_emits
1364 .iter()
1365 .filter_map(|(expected, count_map)| {
1366 let count = match expected.address {
1367 Some(emitter) => match count_map.get(&emitter) {
1368 Some(log_count) => expected
1369 .log
1370 .as_ref()
1371 .map(|l| log_count.count(l))
1372 .unwrap_or_else(|| log_count.count_unchecked()),
1373 None => 0,
1374 },
1375 None => match &expected.log {
1376 Some(log) => count_map.values().map(|logs| logs.count(log)).sum(),
1377 None => count_map.values().map(|logs| logs.count_unchecked()).sum(),
1378 },
1379 };
1380
1381 if count != expected.count { Some((expected, count)) } else { None }
1382 })
1383 .collect::<Vec<_>>();
1384
1385 if self.expected_emits.iter().any(|(expected, _)| !expected.found && expected.count > 0)
1387 {
1388 outcome.result.result = InstructionResult::Revert;
1389 outcome.result.output = "log != expected log".abi_encode().into();
1390 return;
1391 }
1392
1393 if !expected_counts.is_empty() {
1394 let msg = if outcome.result.is_ok() {
1395 let (expected, count) = expected_counts.first().unwrap();
1396 format!("log emitted {count} times, expected {}", expected.count)
1397 } else {
1398 "expected an emit, but the call reverted instead. \
1399 ensure you're testing the happy path when using `expectEmit`"
1400 .to_string()
1401 };
1402
1403 outcome.result.result = InstructionResult::Revert;
1404 outcome.result.output = Error::encode(msg);
1405 return;
1406 }
1407
1408 self.expected_emits.clear()
1412 }
1413
1414 let diag = self.fork_revert_diagnostic.take();
1417
1418 if outcome.result.is_revert()
1421 && let Some(err) = diag
1422 {
1423 outcome.result.output = Error::encode(err.to_error_msg(&self.labels));
1424 return;
1425 }
1426
1427 if let TxKind::Call(test_contract) = ecx.tx.kind {
1430 if ecx.journaled_state.db().is_forked_mode()
1433 && outcome.result.result == InstructionResult::Stop
1434 && call.target_address != test_contract
1435 {
1436 let journaled_state = ecx.journaled_state.clone();
1437 self.fork_revert_diagnostic =
1438 ecx.journaled_state.db().diagnose_revert(call.target_address, &journaled_state);
1439 }
1440 }
1441
1442 if ecx.journaled_state.depth() == 0 {
1444 if outcome.result.is_revert() {
1448 return;
1449 }
1450
1451 for (address, calldatas) in &self.expected_calls {
1456 for (calldata, (expected, actual_count)) in calldatas {
1458 let ExpectedCallData { gas, min_gas, value, count, call_type } = expected;
1460
1461 let failed = match call_type {
1462 ExpectedCallType::Count => *count != *actual_count,
1466 ExpectedCallType::NonCount => *count > *actual_count,
1471 };
1472 if failed {
1473 let expected_values = [
1474 Some(format!("data {}", hex::encode_prefixed(calldata))),
1475 value.as_ref().map(|v| format!("value {v}")),
1476 gas.map(|g| format!("gas {g}")),
1477 min_gas.map(|g| format!("minimum gas {g}")),
1478 ]
1479 .into_iter()
1480 .flatten()
1481 .join(", ");
1482 let but = if outcome.result.is_ok() {
1483 let s = if *actual_count == 1 { "" } else { "s" };
1484 format!("was called {actual_count} time{s}")
1485 } else {
1486 "the call reverted instead; \
1487 ensure you're testing the happy path when using `expectCall`"
1488 .to_string()
1489 };
1490 let s = if *count == 1 { "" } else { "s" };
1491 let msg = format!(
1492 "expected call to {address} with {expected_values} \
1493 to be called {count} time{s}, but {but}"
1494 );
1495 outcome.result.result = InstructionResult::Revert;
1496 outcome.result.output = Error::encode(msg);
1497
1498 return;
1499 }
1500 }
1501 }
1502
1503 self.expected_emits.retain(|(expected, _)| expected.count > 0 && !expected.found);
1506 if !self.expected_emits.is_empty() {
1508 let msg = if outcome.result.is_ok() {
1509 "expected an emit, but no logs were emitted afterwards. \
1510 you might have mismatched events or not enough events were emitted"
1511 } else {
1512 "expected an emit, but the call reverted instead. \
1513 ensure you're testing the happy path when using `expectEmit`"
1514 };
1515 outcome.result.result = InstructionResult::Revert;
1516 outcome.result.output = Error::encode(msg);
1517 return;
1518 }
1519
1520 if let Some(expected_create) = self.expected_creates.first() {
1522 let msg = format!(
1523 "expected {} call by address {} for bytecode {} but not found",
1524 expected_create.create_scheme,
1525 hex::encode_prefixed(expected_create.deployer),
1526 hex::encode_prefixed(&expected_create.bytecode),
1527 );
1528 outcome.result.result = InstructionResult::Revert;
1529 outcome.result.output = Error::encode(msg);
1530 }
1531 }
1532 }
1533
1534 fn create(&mut self, ecx: Ecx, mut input: &mut CreateInputs) -> Option<CreateOutcome> {
1535 let gas = Gas::new(input.gas_limit());
1536 if self.intercept_next_create_call {
1538 self.intercept_next_create_call = false;
1540
1541 let output = input.init_code();
1543
1544 return Some(CreateOutcome {
1546 result: InterpreterResult { result: InstructionResult::Revert, output, gas },
1547 address: None,
1548 });
1549 }
1550
1551 let curr_depth = ecx.journaled_state.depth();
1552
1553 if let Some(prank) = &self.get_prank(curr_depth)
1555 && curr_depth >= prank.depth
1556 && input.caller() == prank.prank_caller
1557 {
1558 let mut prank_applied = false;
1559
1560 if curr_depth == prank.depth {
1562 if let Err(err) = ecx.journaled_state.load_account(prank.new_caller) {
1565 return Some(CreateOutcome {
1567 result: InterpreterResult {
1568 result: InstructionResult::Revert,
1569 output: Error::encode(err),
1570 gas,
1571 },
1572 address: None,
1573 });
1574 }
1575
1576 input.set_caller(prank.new_caller);
1577 prank_applied = true;
1578
1579 ecx.journaled_state.touch(prank.new_caller);
1582 }
1583
1584 if let Some(new_origin) = prank.new_origin {
1586 ecx.tx.caller = new_origin;
1587 prank_applied = true;
1588 }
1589
1590 if prank_applied && let Some(applied_prank) = prank.first_time_applied() {
1592 self.pranks.insert(curr_depth, applied_prank);
1593 }
1594 }
1595
1596 self.apply_accesslist(ecx);
1598
1599 if let Some(broadcast) = &self.broadcast
1601 && curr_depth >= broadcast.depth
1602 && input.caller() == broadcast.original_caller
1603 {
1604 if let Err(err) = ecx.journaled_state.load_account(broadcast.new_origin) {
1605 return Some(CreateOutcome {
1606 result: InterpreterResult {
1607 result: InstructionResult::Revert,
1608 output: Error::encode(err),
1609 gas,
1610 },
1611 address: None,
1612 });
1613 }
1614
1615 ecx.tx.caller = broadcast.new_origin;
1616
1617 if curr_depth == broadcast.depth {
1618 input.set_caller(broadcast.new_origin);
1619 let is_fixed_gas_limit = check_if_fixed_gas_limit(&ecx, input.gas_limit());
1620
1621 let account = &ecx.journaled_state.inner.state()[&broadcast.new_origin];
1622 self.broadcastable_transactions.push_back(BroadcastableTransaction {
1623 rpc: ecx.journaled_state.database.active_fork_url(),
1624 transaction: TransactionRequest {
1625 from: Some(broadcast.new_origin),
1626 to: None,
1627 value: Some(input.value()),
1628 input: TransactionInput::new(input.init_code()),
1629 nonce: Some(account.info.nonce),
1630 gas: if is_fixed_gas_limit { Some(input.gas_limit()) } else { None },
1631 ..Default::default()
1632 }
1633 .into(),
1634 });
1635
1636 input.log_debug(self, &input.scheme().unwrap_or(CreateScheme::Create));
1637 }
1638 }
1639
1640 let address = input.allow_cheatcodes(self, ecx);
1642
1643 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1645 recorded_account_diffs_stack.push(vec![AccountAccess {
1646 chainInfo: crate::Vm::ChainInfo {
1647 forkId: ecx.journaled_state.db().active_fork_id().unwrap_or_default(),
1648 chainId: U256::from(ecx.cfg.chain_id),
1649 },
1650 accessor: input.caller(),
1651 account: address,
1652 kind: crate::Vm::AccountAccessKind::Create,
1653 initialized: true,
1654 oldBalance: U256::ZERO, newBalance: U256::ZERO, value: input.value(),
1657 data: input.init_code(),
1658 reverted: false,
1659 deployedCode: Bytes::new(), storageAccesses: vec![], depth: curr_depth as u64,
1662 }]);
1663 }
1664
1665 None
1666 }
1667
1668 fn create_end(&mut self, ecx: Ecx, call: &CreateInputs, outcome: &mut CreateOutcome) {
1669 let call = Some(call);
1670 let curr_depth = ecx.journaled_state.depth();
1671
1672 if let Some(prank) = &self.get_prank(curr_depth)
1674 && curr_depth == prank.depth
1675 {
1676 ecx.tx.caller = prank.prank_origin;
1677
1678 if prank.single_call {
1680 std::mem::take(&mut self.pranks);
1681 }
1682 }
1683
1684 if let Some(broadcast) = &self.broadcast
1686 && curr_depth == broadcast.depth
1687 {
1688 ecx.tx.caller = broadcast.original_origin;
1689
1690 if broadcast.single_call {
1692 std::mem::take(&mut self.broadcast);
1693 }
1694 }
1695
1696 if let Some(expected_revert) = &self.expected_revert
1698 && curr_depth <= expected_revert.depth
1699 && matches!(expected_revert.kind, ExpectedRevertKind::Default)
1700 {
1701 let mut expected_revert = std::mem::take(&mut self.expected_revert).unwrap();
1702 return match revert_handlers::handle_expect_revert(
1703 false,
1704 true,
1705 self.config.internal_expect_revert,
1706 &expected_revert,
1707 outcome.result.result,
1708 outcome.result.output.clone(),
1709 &self.config.available_artifacts,
1710 ) {
1711 Ok((address, retdata)) => {
1712 expected_revert.actual_count += 1;
1713 if expected_revert.actual_count < expected_revert.count {
1714 self.expected_revert = Some(expected_revert.clone());
1715 }
1716
1717 outcome.result.result = InstructionResult::Return;
1718 outcome.result.output = retdata;
1719 outcome.address = address;
1720 }
1721 Err(err) => {
1722 outcome.result.result = InstructionResult::Revert;
1723 outcome.result.output = err.abi_encode().into();
1724 }
1725 };
1726 }
1727
1728 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1731 if curr_depth > 0
1733 && let Some(last_depth) = &mut recorded_account_diffs_stack.pop()
1734 {
1735 if outcome.result.is_revert() {
1738 last_depth.iter_mut().for_each(|element| {
1739 element.reverted = true;
1740 element
1741 .storageAccesses
1742 .iter_mut()
1743 .for_each(|storage_access| storage_access.reverted = true);
1744 })
1745 }
1746
1747 if let Some(create_access) = last_depth.first_mut() {
1748 let depth = ecx.journaled_state.depth();
1753 if create_access.depth == depth as u64 {
1754 debug_assert_eq!(
1755 create_access.kind as u8,
1756 crate::Vm::AccountAccessKind::Create as u8
1757 );
1758 if let Some(address) = outcome.address
1759 && let Ok(created_acc) = ecx.journaled_state.load_account(address)
1760 {
1761 create_access.newBalance = created_acc.info.balance;
1762 create_access.deployedCode =
1763 created_acc.info.code.clone().unwrap_or_default().original_bytes();
1764 }
1765 }
1766 if let Some(last) = recorded_account_diffs_stack.last_mut() {
1771 last.append(last_depth);
1772 } else {
1773 recorded_account_diffs_stack.push(last_depth.clone());
1774 }
1775 }
1776 }
1777 }
1778
1779 if !self.expected_creates.is_empty()
1781 && let (Some(address), Some(call)) = (outcome.address, call)
1782 && let Ok(created_acc) = ecx.journaled_state.load_account(address)
1783 {
1784 let bytecode = created_acc.info.code.clone().unwrap_or_default().original_bytes();
1785 if let Some((index, _)) =
1786 self.expected_creates.iter().find_position(|expected_create| {
1787 expected_create.deployer == call.caller
1788 && expected_create.create_scheme.eq(call.scheme.into())
1789 && expected_create.bytecode == bytecode
1790 })
1791 {
1792 self.expected_creates.swap_remove(index);
1793 }
1794 }
1795 }
1796}
1797
1798impl InspectorExt for Cheatcodes {
1799 fn should_use_create2_factory(&mut self, ecx: Ecx, inputs: &CreateInputs) -> bool {
1800 if let CreateScheme::Create2 { .. } = inputs.scheme {
1801 let depth = ecx.journaled_state.depth();
1802 let target_depth = if let Some(prank) = &self.get_prank(depth) {
1803 prank.depth
1804 } else if let Some(broadcast) = &self.broadcast {
1805 broadcast.depth
1806 } else {
1807 1
1808 };
1809
1810 depth == target_depth
1811 && (self.broadcast.is_some() || self.config.always_use_create_2_factory)
1812 } else {
1813 false
1814 }
1815 }
1816
1817 fn create2_deployer(&self) -> Address {
1818 self.config.evm_opts.create2_deployer
1819 }
1820}
1821
1822impl Cheatcodes {
1823 #[cold]
1824 fn meter_gas(&mut self, interpreter: &mut Interpreter) {
1825 if let Some(paused_gas) = self.gas_metering.paused_frames.last() {
1826 let memory = *interpreter.gas.memory();
1829 interpreter.gas = *paused_gas;
1830 interpreter.gas.memory_mut().words_num = memory.words_num;
1831 interpreter.gas.memory_mut().expansion_cost = memory.expansion_cost;
1832 } else {
1833 self.gas_metering.paused_frames.push(interpreter.gas);
1835 }
1836 }
1837
1838 #[cold]
1839 fn meter_gas_record(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1840 if interpreter.bytecode.action.as_ref().and_then(|i| i.instruction_result()).is_none() {
1841 self.gas_metering.gas_records.iter_mut().for_each(|record| {
1842 let curr_depth = ecx.journaled_state.depth();
1843 if curr_depth == record.depth {
1844 if self.gas_metering.last_gas_used != 0 {
1847 let gas_diff =
1848 interpreter.gas.spent().saturating_sub(self.gas_metering.last_gas_used);
1849 record.gas_used = record.gas_used.saturating_add(gas_diff);
1850 }
1851
1852 self.gas_metering.last_gas_used = interpreter.gas.spent();
1855 }
1856 });
1857 }
1858 }
1859
1860 #[cold]
1861 fn meter_gas_end(&mut self, interpreter: &mut Interpreter) {
1862 if let Some(interpreter_action) = interpreter.bytecode.action.as_ref()
1864 && will_exit(interpreter_action)
1865 {
1866 self.gas_metering.paused_frames.pop();
1867 }
1868 }
1869
1870 #[cold]
1871 fn meter_gas_reset(&mut self, interpreter: &mut Interpreter) {
1872 interpreter.gas = Gas::new(interpreter.gas.limit());
1873 self.gas_metering.reset = false;
1874 }
1875
1876 #[cold]
1877 fn meter_gas_check(&mut self, interpreter: &mut Interpreter) {
1878 if let Some(interpreter_action) = interpreter.bytecode.action.as_ref()
1879 && will_exit(interpreter_action)
1880 {
1881 if interpreter.gas.spent()
1885 < u64::try_from(interpreter.gas.refunded()).unwrap_or_default()
1886 {
1887 interpreter.gas = Gas::new(interpreter.gas.limit());
1888 }
1889 }
1890 }
1891
1892 #[cold]
1900 fn arbitrary_storage_end(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1901 let (key, target_address) = if interpreter.bytecode.opcode() == op::SLOAD {
1902 (try_or_return!(interpreter.stack.peek(0)), interpreter.input.target_address)
1903 } else {
1904 return;
1905 };
1906
1907 let Some(value) = ecx.sload(target_address, key) else {
1908 return;
1909 };
1910
1911 if (value.is_cold && value.data.is_zero())
1912 || self.should_overwrite_arbitrary_storage(&target_address, key)
1913 {
1914 if self.has_arbitrary_storage(&target_address) {
1915 let arbitrary_value = self.rng().random();
1916 self.arbitrary_storage.as_mut().unwrap().save(
1917 ecx,
1918 target_address,
1919 key,
1920 arbitrary_value,
1921 );
1922 } else if self.is_arbitrary_storage_copy(&target_address) {
1923 let arbitrary_value = self.rng().random();
1924 self.arbitrary_storage.as_mut().unwrap().copy(
1925 ecx,
1926 target_address,
1927 key,
1928 arbitrary_value,
1929 );
1930 }
1931 }
1932 }
1933
1934 #[cold]
1936 fn record_accesses(&mut self, interpreter: &mut Interpreter) {
1937 let access = &mut self.accesses;
1938 match interpreter.bytecode.opcode() {
1939 op::SLOAD => {
1940 let key = try_or_return!(interpreter.stack.peek(0));
1941 access.record_read(interpreter.input.target_address, key);
1942 }
1943 op::SSTORE => {
1944 let key = try_or_return!(interpreter.stack.peek(0));
1945 access.record_write(interpreter.input.target_address, key);
1946 }
1947 _ => {}
1948 }
1949 }
1950
1951 #[cold]
1952 fn record_state_diffs(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1953 let Some(account_accesses) = &mut self.recorded_account_diffs_stack else { return };
1954 match interpreter.bytecode.opcode() {
1955 op::SELFDESTRUCT => {
1956 let Some(last) = account_accesses.last_mut() else { return };
1958
1959 let target = try_or_return!(interpreter.stack.peek(0));
1961 let target = Address::from_word(B256::from(target));
1962 let (initialized, old_balance) = ecx
1963 .journaled_state
1964 .load_account(target)
1965 .map(|account| (account.info.exists(), account.info.balance))
1966 .unwrap_or_default();
1967
1968 let value = ecx
1970 .balance(interpreter.input.target_address)
1971 .map(|b| b.data)
1972 .unwrap_or(U256::ZERO);
1973
1974 last.push(crate::Vm::AccountAccess {
1976 chainInfo: crate::Vm::ChainInfo {
1977 forkId: ecx.journaled_state.database.active_fork_id().unwrap_or_default(),
1978 chainId: U256::from(ecx.cfg.chain_id),
1979 },
1980 accessor: interpreter.input.target_address,
1981 account: target,
1982 kind: crate::Vm::AccountAccessKind::SelfDestruct,
1983 initialized,
1984 oldBalance: old_balance,
1985 newBalance: old_balance + value,
1986 value,
1987 data: Bytes::new(),
1988 reverted: false,
1989 deployedCode: Bytes::new(),
1990 storageAccesses: vec![],
1991 depth: ecx
1992 .journaled_state
1993 .depth()
1994 .try_into()
1995 .expect("journaled state depth exceeds u64"),
1996 });
1997 }
1998
1999 op::SLOAD => {
2000 let Some(last) = account_accesses.last_mut() else { return };
2001
2002 let key = try_or_return!(interpreter.stack.peek(0));
2003 let address = interpreter.input.target_address;
2004
2005 let mut present_value = U256::ZERO;
2008 if ecx.journaled_state.load_account(address).is_ok()
2010 && let Some(previous) = ecx.sload(address, key)
2011 {
2012 present_value = previous.data;
2013 }
2014 let access = crate::Vm::StorageAccess {
2015 account: interpreter.input.target_address,
2016 slot: key.into(),
2017 isWrite: false,
2018 previousValue: present_value.into(),
2019 newValue: present_value.into(),
2020 reverted: false,
2021 };
2022 let curr_depth = ecx
2023 .journaled_state
2024 .depth()
2025 .try_into()
2026 .expect("journaled state depth exceeds u64");
2027 append_storage_access(last, access, curr_depth);
2028 }
2029 op::SSTORE => {
2030 let Some(last) = account_accesses.last_mut() else { return };
2031
2032 let key = try_or_return!(interpreter.stack.peek(0));
2033 let value = try_or_return!(interpreter.stack.peek(1));
2034 let address = interpreter.input.target_address;
2035 let mut previous_value = U256::ZERO;
2038 if ecx.journaled_state.load_account(address).is_ok()
2039 && let Some(previous) = ecx.sload(address, key)
2040 {
2041 previous_value = previous.data;
2042 }
2043
2044 let access = crate::Vm::StorageAccess {
2045 account: address,
2046 slot: key.into(),
2047 isWrite: true,
2048 previousValue: previous_value.into(),
2049 newValue: value.into(),
2050 reverted: false,
2051 };
2052 let curr_depth = ecx
2053 .journaled_state
2054 .depth()
2055 .try_into()
2056 .expect("journaled state depth exceeds u64");
2057 append_storage_access(last, access, curr_depth);
2058 }
2059
2060 op::EXTCODECOPY | op::EXTCODESIZE | op::EXTCODEHASH | op::BALANCE => {
2062 let kind = match interpreter.bytecode.opcode() {
2063 op::EXTCODECOPY => crate::Vm::AccountAccessKind::Extcodecopy,
2064 op::EXTCODESIZE => crate::Vm::AccountAccessKind::Extcodesize,
2065 op::EXTCODEHASH => crate::Vm::AccountAccessKind::Extcodehash,
2066 op::BALANCE => crate::Vm::AccountAccessKind::Balance,
2067 _ => unreachable!(),
2068 };
2069 let address =
2070 Address::from_word(B256::from(try_or_return!(interpreter.stack.peek(0))));
2071 let initialized;
2072 let balance;
2073 if let Ok(acc) = ecx.journaled_state.load_account(address) {
2074 initialized = acc.info.exists();
2075 balance = acc.info.balance;
2076 } else {
2077 initialized = false;
2078 balance = U256::ZERO;
2079 }
2080 let curr_depth = ecx
2081 .journaled_state
2082 .depth()
2083 .try_into()
2084 .expect("journaled state depth exceeds u64");
2085 let account_access = crate::Vm::AccountAccess {
2086 chainInfo: crate::Vm::ChainInfo {
2087 forkId: ecx.journaled_state.database.active_fork_id().unwrap_or_default(),
2088 chainId: U256::from(ecx.cfg.chain_id),
2089 },
2090 accessor: interpreter.input.target_address,
2091 account: address,
2092 kind,
2093 initialized,
2094 oldBalance: balance,
2095 newBalance: balance,
2096 value: U256::ZERO,
2097 data: Bytes::new(),
2098 reverted: false,
2099 deployedCode: Bytes::new(),
2100 storageAccesses: vec![],
2101 depth: curr_depth,
2102 };
2103 if let Some(last) = account_accesses.last_mut() {
2106 last.push(account_access);
2107 } else {
2108 account_accesses.push(vec![account_access]);
2109 }
2110 }
2111 _ => {}
2112 }
2113 }
2114
2115 #[cold]
2120 fn check_mem_opcodes(&self, interpreter: &mut Interpreter, depth: u64) {
2121 let Some(ranges) = self.allowed_mem_writes.get(&depth) else {
2122 return;
2123 };
2124
2125 macro_rules! mem_opcode_match {
2134 ($(($opcode:ident, $offset_depth:expr, $size_depth:expr, $writes:expr)),* $(,)?) => {
2135 match interpreter.bytecode.opcode() {
2136 op::MSTORE => {
2141 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2143
2144 if !ranges.iter().any(|range| {
2147 range.contains(&offset) && range.contains(&(offset + 31))
2148 }) {
2149 let value = try_or_return!(interpreter.stack.peek(1)).to_be_bytes::<32>();
2154 if value[..SELECTOR_LEN] == stopExpectSafeMemoryCall::SELECTOR {
2155 return
2156 }
2157
2158 disallowed_mem_write(offset, 32, interpreter, ranges);
2159 return
2160 }
2161 }
2162 op::MSTORE8 => {
2163 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2165
2166 if !ranges.iter().any(|range| range.contains(&offset)) {
2169 disallowed_mem_write(offset, 1, interpreter, ranges);
2170 return
2171 }
2172 }
2173
2174 op::MLOAD => {
2179 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2181
2182 if offset >= interpreter.memory.size() as u64 && !ranges.iter().any(|range| {
2186 range.contains(&offset) && range.contains(&(offset + 31))
2187 }) {
2188 disallowed_mem_write(offset, 32, interpreter, ranges);
2189 return
2190 }
2191 }
2192
2193 op::CALL => {
2198 let dest_offset = try_or_return!(interpreter.stack.peek(5)).saturating_to::<u64>();
2200
2201 let size = try_or_return!(interpreter.stack.peek(6)).saturating_to::<u64>();
2203
2204 let fail_cond = !ranges.iter().any(|range| {
2208 range.contains(&dest_offset) &&
2209 range.contains(&(dest_offset + size.saturating_sub(1)))
2210 });
2211
2212 if fail_cond {
2215 let to = Address::from_word(try_or_return!(interpreter.stack.peek(1)).to_be_bytes::<32>().into());
2219 if to == CHEATCODE_ADDRESS {
2220 let args_offset = try_or_return!(interpreter.stack.peek(3)).saturating_to::<usize>();
2221 let args_size = try_or_return!(interpreter.stack.peek(4)).saturating_to::<usize>();
2222 let memory_word = interpreter.memory.slice_len(args_offset, args_size);
2223 if memory_word[..SELECTOR_LEN] == stopExpectSafeMemoryCall::SELECTOR {
2224 return
2225 }
2226 }
2227
2228 disallowed_mem_write(dest_offset, size, interpreter, ranges);
2229 return
2230 }
2231 }
2232
2233 $(op::$opcode => {
2234 let dest_offset = try_or_return!(interpreter.stack.peek($offset_depth)).saturating_to::<u64>();
2236
2237 let size = try_or_return!(interpreter.stack.peek($size_depth)).saturating_to::<u64>();
2239
2240 let fail_cond = !ranges.iter().any(|range| {
2244 range.contains(&dest_offset) &&
2245 range.contains(&(dest_offset + size.saturating_sub(1)))
2246 }) && ($writes ||
2247 [dest_offset, (dest_offset + size).saturating_sub(1)].into_iter().any(|offset| {
2248 offset >= interpreter.memory.size() as u64
2249 })
2250 );
2251
2252 if fail_cond {
2255 disallowed_mem_write(dest_offset, size, interpreter, ranges);
2256 return
2257 }
2258 })*
2259
2260 _ => {}
2261 }
2262 }
2263 }
2264
2265 mem_opcode_match!(
2268 (CALLDATACOPY, 0, 2, true),
2269 (CODECOPY, 0, 2, true),
2270 (RETURNDATACOPY, 0, 2, true),
2271 (EXTCODECOPY, 1, 3, true),
2272 (CALLCODE, 5, 6, true),
2273 (STATICCALL, 4, 5, true),
2274 (DELEGATECALL, 4, 5, true),
2275 (KECCAK256, 0, 1, false),
2276 (LOG0, 0, 1, false),
2277 (LOG1, 0, 1, false),
2278 (LOG2, 0, 1, false),
2279 (LOG3, 0, 1, false),
2280 (LOG4, 0, 1, false),
2281 (CREATE, 1, 2, false),
2282 (CREATE2, 1, 2, false),
2283 (RETURN, 0, 1, false),
2284 (REVERT, 0, 1, false),
2285 );
2286 }
2287}
2288
2289fn disallowed_mem_write(
2295 dest_offset: u64,
2296 size: u64,
2297 interpreter: &mut Interpreter,
2298 ranges: &[Range<u64>],
2299) {
2300 let revert_string = format!(
2301 "memory write at offset 0x{:02X} of size 0x{:02X} not allowed; safe range: {}",
2302 dest_offset,
2303 size,
2304 ranges.iter().map(|r| format!("(0x{:02X}, 0x{:02X}]", r.start, r.end)).join(" U ")
2305 );
2306
2307 interpreter.bytecode.set_action(InterpreterAction::new_return(
2308 InstructionResult::Revert,
2309 Bytes::from(revert_string.into_bytes()),
2310 interpreter.gas,
2311 ));
2312}
2313
2314fn check_if_fixed_gas_limit(ecx: &Ecx, call_gas_limit: u64) -> bool {
2317 ecx.tx.gas_limit > ecx.block.gas_limit &&
2322 call_gas_limit <= ecx.block.gas_limit
2323 && call_gas_limit > 2300
2326}
2327
2328fn access_is_call(kind: crate::Vm::AccountAccessKind) -> bool {
2330 matches!(
2331 kind,
2332 crate::Vm::AccountAccessKind::Call
2333 | crate::Vm::AccountAccessKind::StaticCall
2334 | crate::Vm::AccountAccessKind::CallCode
2335 | crate::Vm::AccountAccessKind::DelegateCall
2336 )
2337}
2338
2339fn append_storage_access(
2341 last: &mut Vec<AccountAccess>,
2342 storage_access: crate::Vm::StorageAccess,
2343 storage_depth: u64,
2344) {
2345 if !last.is_empty() && last.first().unwrap().depth < storage_depth {
2347 if last.len() == 1 {
2353 last.first_mut().unwrap().storageAccesses.push(storage_access);
2354 } else {
2355 let last_record = last.last_mut().unwrap();
2356 if last_record.kind as u8 == crate::Vm::AccountAccessKind::Resume as u8 {
2357 last_record.storageAccesses.push(storage_access);
2358 } else {
2359 let entry = last.first().unwrap();
2360 let resume_record = crate::Vm::AccountAccess {
2361 chainInfo: crate::Vm::ChainInfo {
2362 forkId: entry.chainInfo.forkId,
2363 chainId: entry.chainInfo.chainId,
2364 },
2365 accessor: entry.accessor,
2366 account: entry.account,
2367 kind: crate::Vm::AccountAccessKind::Resume,
2368 initialized: entry.initialized,
2369 storageAccesses: vec![storage_access],
2370 reverted: entry.reverted,
2371 oldBalance: U256::ZERO,
2373 newBalance: U256::ZERO,
2374 value: U256::ZERO,
2375 data: Bytes::new(),
2376 deployedCode: Bytes::new(),
2377 depth: entry.depth,
2378 };
2379 last.push(resume_record);
2380 }
2381 }
2382 }
2383}
2384
2385fn apply_dispatch(
2387 calls: &Vm::VmCalls,
2388 ccx: &mut CheatsCtxt,
2389 executor: &mut dyn CheatcodesExecutor,
2390) -> Result {
2391 let cheat = calls_as_dyn_cheatcode(calls);
2392
2393 let _guard = debug_span!(target: "cheatcodes", "apply", id = %cheat.id()).entered();
2394 trace!(target: "cheatcodes", cheat = ?cheat.as_debug(), "applying");
2395
2396 if let spec::Status::Deprecated(replacement) = *cheat.status() {
2397 ccx.state.deprecated.insert(cheat.signature(), replacement);
2398 }
2399
2400 let mut result = cheat.dyn_apply(ccx, executor);
2402
2403 if let Err(e) = &mut result
2405 && e.is_str()
2406 {
2407 let name = cheat.name();
2408 if !name.contains("assert") && name != "rpcUrl" {
2412 *e = fmt_err!("vm.{name}: {e}");
2413 }
2414 }
2415
2416 trace!(
2417 target: "cheatcodes",
2418 return = %match &result {
2419 Ok(b) => hex::encode(b),
2420 Err(e) => e.to_string(),
2421 }
2422 );
2423
2424 result
2425}
2426
2427fn calls_as_dyn_cheatcode(calls: &Vm::VmCalls) -> &dyn DynCheatcode {
2428 macro_rules! as_dyn {
2429 ($($variant:ident),*) => {
2430 match calls {
2431 $(Vm::VmCalls::$variant(cheat) => cheat,)*
2432 }
2433 };
2434 }
2435 vm_calls!(as_dyn)
2436}
2437
2438fn will_exit(action: &InterpreterAction) -> bool {
2440 match action {
2441 InterpreterAction::Return(result) => {
2442 result.result.is_ok_or_revert() || result.result.is_error()
2443 }
2444 _ => false,
2445 }
2446}