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 input.set_caller(prank.new_caller);
1563 prank_applied = true;
1564 }
1565
1566 if let Some(new_origin) = prank.new_origin {
1568 ecx.tx.caller = new_origin;
1569 prank_applied = true;
1570 }
1571
1572 if prank_applied && let Some(applied_prank) = prank.first_time_applied() {
1574 self.pranks.insert(curr_depth, applied_prank);
1575 }
1576 }
1577
1578 self.apply_accesslist(ecx);
1580
1581 if let Some(broadcast) = &self.broadcast
1583 && curr_depth >= broadcast.depth
1584 && input.caller() == broadcast.original_caller
1585 {
1586 if let Err(err) = ecx.journaled_state.load_account(broadcast.new_origin) {
1587 return Some(CreateOutcome {
1588 result: InterpreterResult {
1589 result: InstructionResult::Revert,
1590 output: Error::encode(err),
1591 gas,
1592 },
1593 address: None,
1594 });
1595 }
1596
1597 ecx.tx.caller = broadcast.new_origin;
1598
1599 if curr_depth == broadcast.depth {
1600 input.set_caller(broadcast.new_origin);
1601 let is_fixed_gas_limit = check_if_fixed_gas_limit(&ecx, input.gas_limit());
1602
1603 let account = &ecx.journaled_state.inner.state()[&broadcast.new_origin];
1604 self.broadcastable_transactions.push_back(BroadcastableTransaction {
1605 rpc: ecx.journaled_state.database.active_fork_url(),
1606 transaction: TransactionRequest {
1607 from: Some(broadcast.new_origin),
1608 to: None,
1609 value: Some(input.value()),
1610 input: TransactionInput::new(input.init_code()),
1611 nonce: Some(account.info.nonce),
1612 gas: if is_fixed_gas_limit { Some(input.gas_limit()) } else { None },
1613 ..Default::default()
1614 }
1615 .into(),
1616 });
1617
1618 input.log_debug(self, &input.scheme().unwrap_or(CreateScheme::Create));
1619 }
1620 }
1621
1622 let address = input.allow_cheatcodes(self, ecx);
1624
1625 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1627 recorded_account_diffs_stack.push(vec![AccountAccess {
1628 chainInfo: crate::Vm::ChainInfo {
1629 forkId: ecx.journaled_state.db().active_fork_id().unwrap_or_default(),
1630 chainId: U256::from(ecx.cfg.chain_id),
1631 },
1632 accessor: input.caller(),
1633 account: address,
1634 kind: crate::Vm::AccountAccessKind::Create,
1635 initialized: true,
1636 oldBalance: U256::ZERO, newBalance: U256::ZERO, value: input.value(),
1639 data: input.init_code(),
1640 reverted: false,
1641 deployedCode: Bytes::new(), storageAccesses: vec![], depth: curr_depth as u64,
1644 }]);
1645 }
1646
1647 None
1648 }
1649
1650 fn create_end(&mut self, ecx: Ecx, call: &CreateInputs, outcome: &mut CreateOutcome) {
1651 let call = Some(call);
1652 let curr_depth = ecx.journaled_state.depth();
1653
1654 if let Some(prank) = &self.get_prank(curr_depth)
1656 && curr_depth == prank.depth
1657 {
1658 ecx.tx.caller = prank.prank_origin;
1659
1660 if prank.single_call {
1662 std::mem::take(&mut self.pranks);
1663 }
1664 }
1665
1666 if let Some(broadcast) = &self.broadcast
1668 && curr_depth == broadcast.depth
1669 {
1670 ecx.tx.caller = broadcast.original_origin;
1671
1672 if broadcast.single_call {
1674 std::mem::take(&mut self.broadcast);
1675 }
1676 }
1677
1678 if let Some(expected_revert) = &self.expected_revert
1680 && curr_depth <= expected_revert.depth
1681 && matches!(expected_revert.kind, ExpectedRevertKind::Default)
1682 {
1683 let mut expected_revert = std::mem::take(&mut self.expected_revert).unwrap();
1684 return match revert_handlers::handle_expect_revert(
1685 false,
1686 true,
1687 self.config.internal_expect_revert,
1688 &expected_revert,
1689 outcome.result.result,
1690 outcome.result.output.clone(),
1691 &self.config.available_artifacts,
1692 ) {
1693 Ok((address, retdata)) => {
1694 expected_revert.actual_count += 1;
1695 if expected_revert.actual_count < expected_revert.count {
1696 self.expected_revert = Some(expected_revert.clone());
1697 }
1698
1699 outcome.result.result = InstructionResult::Return;
1700 outcome.result.output = retdata;
1701 outcome.address = address;
1702 }
1703 Err(err) => {
1704 outcome.result.result = InstructionResult::Revert;
1705 outcome.result.output = err.abi_encode().into();
1706 }
1707 };
1708 }
1709
1710 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1713 if curr_depth > 0
1715 && let Some(last_depth) = &mut recorded_account_diffs_stack.pop()
1716 {
1717 if outcome.result.is_revert() {
1720 last_depth.iter_mut().for_each(|element| {
1721 element.reverted = true;
1722 element
1723 .storageAccesses
1724 .iter_mut()
1725 .for_each(|storage_access| storage_access.reverted = true);
1726 })
1727 }
1728
1729 if let Some(create_access) = last_depth.first_mut() {
1730 let depth = ecx.journaled_state.depth();
1735 if create_access.depth == depth as u64 {
1736 debug_assert_eq!(
1737 create_access.kind as u8,
1738 crate::Vm::AccountAccessKind::Create as u8
1739 );
1740 if let Some(address) = outcome.address
1741 && let Ok(created_acc) = ecx.journaled_state.load_account(address)
1742 {
1743 create_access.newBalance = created_acc.info.balance;
1744 create_access.deployedCode =
1745 created_acc.info.code.clone().unwrap_or_default().original_bytes();
1746 }
1747 }
1748 if let Some(last) = recorded_account_diffs_stack.last_mut() {
1753 last.append(last_depth);
1754 } else {
1755 recorded_account_diffs_stack.push(last_depth.clone());
1756 }
1757 }
1758 }
1759 }
1760
1761 if !self.expected_creates.is_empty()
1763 && let (Some(address), Some(call)) = (outcome.address, call)
1764 && let Ok(created_acc) = ecx.journaled_state.load_account(address)
1765 {
1766 let bytecode = created_acc.info.code.clone().unwrap_or_default().original_bytes();
1767 if let Some((index, _)) =
1768 self.expected_creates.iter().find_position(|expected_create| {
1769 expected_create.deployer == call.caller
1770 && expected_create.create_scheme.eq(call.scheme.into())
1771 && expected_create.bytecode == bytecode
1772 })
1773 {
1774 self.expected_creates.swap_remove(index);
1775 }
1776 }
1777 }
1778}
1779
1780impl InspectorExt for Cheatcodes {
1781 fn should_use_create2_factory(&mut self, ecx: Ecx, inputs: &CreateInputs) -> bool {
1782 if let CreateScheme::Create2 { .. } = inputs.scheme {
1783 let depth = ecx.journaled_state.depth();
1784 let target_depth = if let Some(prank) = &self.get_prank(depth) {
1785 prank.depth
1786 } else if let Some(broadcast) = &self.broadcast {
1787 broadcast.depth
1788 } else {
1789 1
1790 };
1791
1792 depth == target_depth
1793 && (self.broadcast.is_some() || self.config.always_use_create_2_factory)
1794 } else {
1795 false
1796 }
1797 }
1798
1799 fn create2_deployer(&self) -> Address {
1800 self.config.evm_opts.create2_deployer
1801 }
1802}
1803
1804impl Cheatcodes {
1805 #[cold]
1806 fn meter_gas(&mut self, interpreter: &mut Interpreter) {
1807 if let Some(paused_gas) = self.gas_metering.paused_frames.last() {
1808 let memory = *interpreter.gas.memory();
1811 interpreter.gas = *paused_gas;
1812 interpreter.gas.memory_mut().words_num = memory.words_num;
1813 interpreter.gas.memory_mut().expansion_cost = memory.expansion_cost;
1814 } else {
1815 self.gas_metering.paused_frames.push(interpreter.gas);
1817 }
1818 }
1819
1820 #[cold]
1821 fn meter_gas_record(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1822 if interpreter.bytecode.action.as_ref().and_then(|i| i.instruction_result()).is_none() {
1823 self.gas_metering.gas_records.iter_mut().for_each(|record| {
1824 let curr_depth = ecx.journaled_state.depth();
1825 if curr_depth == record.depth {
1826 if self.gas_metering.last_gas_used != 0 {
1829 let gas_diff =
1830 interpreter.gas.spent().saturating_sub(self.gas_metering.last_gas_used);
1831 record.gas_used = record.gas_used.saturating_add(gas_diff);
1832 }
1833
1834 self.gas_metering.last_gas_used = interpreter.gas.spent();
1837 }
1838 });
1839 }
1840 }
1841
1842 #[cold]
1843 fn meter_gas_end(&mut self, interpreter: &mut Interpreter) {
1844 if let Some(interpreter_action) = interpreter.bytecode.action.as_ref()
1846 && will_exit(interpreter_action)
1847 {
1848 self.gas_metering.paused_frames.pop();
1849 }
1850 }
1851
1852 #[cold]
1853 fn meter_gas_reset(&mut self, interpreter: &mut Interpreter) {
1854 interpreter.gas = Gas::new(interpreter.gas.limit());
1855 self.gas_metering.reset = false;
1856 }
1857
1858 #[cold]
1859 fn meter_gas_check(&mut self, interpreter: &mut Interpreter) {
1860 if let Some(interpreter_action) = interpreter.bytecode.action.as_ref()
1861 && will_exit(interpreter_action)
1862 {
1863 if interpreter.gas.spent()
1867 < u64::try_from(interpreter.gas.refunded()).unwrap_or_default()
1868 {
1869 interpreter.gas = Gas::new(interpreter.gas.limit());
1870 }
1871 }
1872 }
1873
1874 #[cold]
1882 fn arbitrary_storage_end(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1883 let (key, target_address) = if interpreter.bytecode.opcode() == op::SLOAD {
1884 (try_or_return!(interpreter.stack.peek(0)), interpreter.input.target_address)
1885 } else {
1886 return;
1887 };
1888
1889 let Some(value) = ecx.sload(target_address, key) else {
1890 return;
1891 };
1892
1893 if (value.is_cold && value.data.is_zero())
1894 || self.should_overwrite_arbitrary_storage(&target_address, key)
1895 {
1896 if self.has_arbitrary_storage(&target_address) {
1897 let arbitrary_value = self.rng().random();
1898 self.arbitrary_storage.as_mut().unwrap().save(
1899 ecx,
1900 target_address,
1901 key,
1902 arbitrary_value,
1903 );
1904 } else if self.is_arbitrary_storage_copy(&target_address) {
1905 let arbitrary_value = self.rng().random();
1906 self.arbitrary_storage.as_mut().unwrap().copy(
1907 ecx,
1908 target_address,
1909 key,
1910 arbitrary_value,
1911 );
1912 }
1913 }
1914 }
1915
1916 #[cold]
1918 fn record_accesses(&mut self, interpreter: &mut Interpreter) {
1919 let access = &mut self.accesses;
1920 match interpreter.bytecode.opcode() {
1921 op::SLOAD => {
1922 let key = try_or_return!(interpreter.stack.peek(0));
1923 access.record_read(interpreter.input.target_address, key);
1924 }
1925 op::SSTORE => {
1926 let key = try_or_return!(interpreter.stack.peek(0));
1927 access.record_write(interpreter.input.target_address, key);
1928 }
1929 _ => {}
1930 }
1931 }
1932
1933 #[cold]
1934 fn record_state_diffs(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1935 let Some(account_accesses) = &mut self.recorded_account_diffs_stack else { return };
1936 match interpreter.bytecode.opcode() {
1937 op::SELFDESTRUCT => {
1938 let Some(last) = account_accesses.last_mut() else { return };
1940
1941 let target = try_or_return!(interpreter.stack.peek(0));
1943 let target = Address::from_word(B256::from(target));
1944 let (initialized, old_balance) = ecx
1945 .journaled_state
1946 .load_account(target)
1947 .map(|account| (account.info.exists(), account.info.balance))
1948 .unwrap_or_default();
1949
1950 let value = ecx
1952 .balance(interpreter.input.target_address)
1953 .map(|b| b.data)
1954 .unwrap_or(U256::ZERO);
1955
1956 last.push(crate::Vm::AccountAccess {
1958 chainInfo: crate::Vm::ChainInfo {
1959 forkId: ecx.journaled_state.database.active_fork_id().unwrap_or_default(),
1960 chainId: U256::from(ecx.cfg.chain_id),
1961 },
1962 accessor: interpreter.input.target_address,
1963 account: target,
1964 kind: crate::Vm::AccountAccessKind::SelfDestruct,
1965 initialized,
1966 oldBalance: old_balance,
1967 newBalance: old_balance + value,
1968 value,
1969 data: Bytes::new(),
1970 reverted: false,
1971 deployedCode: Bytes::new(),
1972 storageAccesses: vec![],
1973 depth: ecx
1974 .journaled_state
1975 .depth()
1976 .try_into()
1977 .expect("journaled state depth exceeds u64"),
1978 });
1979 }
1980
1981 op::SLOAD => {
1982 let Some(last) = account_accesses.last_mut() else { return };
1983
1984 let key = try_or_return!(interpreter.stack.peek(0));
1985 let address = interpreter.input.target_address;
1986
1987 let mut present_value = U256::ZERO;
1990 if ecx.journaled_state.load_account(address).is_ok()
1992 && let Some(previous) = ecx.sload(address, key)
1993 {
1994 present_value = previous.data;
1995 }
1996 let access = crate::Vm::StorageAccess {
1997 account: interpreter.input.target_address,
1998 slot: key.into(),
1999 isWrite: false,
2000 previousValue: present_value.into(),
2001 newValue: present_value.into(),
2002 reverted: false,
2003 };
2004 let curr_depth = ecx
2005 .journaled_state
2006 .depth()
2007 .try_into()
2008 .expect("journaled state depth exceeds u64");
2009 append_storage_access(last, access, curr_depth);
2010 }
2011 op::SSTORE => {
2012 let Some(last) = account_accesses.last_mut() else { return };
2013
2014 let key = try_or_return!(interpreter.stack.peek(0));
2015 let value = try_or_return!(interpreter.stack.peek(1));
2016 let address = interpreter.input.target_address;
2017 let mut previous_value = U256::ZERO;
2020 if ecx.journaled_state.load_account(address).is_ok()
2021 && let Some(previous) = ecx.sload(address, key)
2022 {
2023 previous_value = previous.data;
2024 }
2025
2026 let access = crate::Vm::StorageAccess {
2027 account: address,
2028 slot: key.into(),
2029 isWrite: true,
2030 previousValue: previous_value.into(),
2031 newValue: value.into(),
2032 reverted: false,
2033 };
2034 let curr_depth = ecx
2035 .journaled_state
2036 .depth()
2037 .try_into()
2038 .expect("journaled state depth exceeds u64");
2039 append_storage_access(last, access, curr_depth);
2040 }
2041
2042 op::EXTCODECOPY | op::EXTCODESIZE | op::EXTCODEHASH | op::BALANCE => {
2044 let kind = match interpreter.bytecode.opcode() {
2045 op::EXTCODECOPY => crate::Vm::AccountAccessKind::Extcodecopy,
2046 op::EXTCODESIZE => crate::Vm::AccountAccessKind::Extcodesize,
2047 op::EXTCODEHASH => crate::Vm::AccountAccessKind::Extcodehash,
2048 op::BALANCE => crate::Vm::AccountAccessKind::Balance,
2049 _ => unreachable!(),
2050 };
2051 let address =
2052 Address::from_word(B256::from(try_or_return!(interpreter.stack.peek(0))));
2053 let initialized;
2054 let balance;
2055 if let Ok(acc) = ecx.journaled_state.load_account(address) {
2056 initialized = acc.info.exists();
2057 balance = acc.info.balance;
2058 } else {
2059 initialized = false;
2060 balance = U256::ZERO;
2061 }
2062 let curr_depth = ecx
2063 .journaled_state
2064 .depth()
2065 .try_into()
2066 .expect("journaled state depth exceeds u64");
2067 let account_access = crate::Vm::AccountAccess {
2068 chainInfo: crate::Vm::ChainInfo {
2069 forkId: ecx.journaled_state.database.active_fork_id().unwrap_or_default(),
2070 chainId: U256::from(ecx.cfg.chain_id),
2071 },
2072 accessor: interpreter.input.target_address,
2073 account: address,
2074 kind,
2075 initialized,
2076 oldBalance: balance,
2077 newBalance: balance,
2078 value: U256::ZERO,
2079 data: Bytes::new(),
2080 reverted: false,
2081 deployedCode: Bytes::new(),
2082 storageAccesses: vec![],
2083 depth: curr_depth,
2084 };
2085 if let Some(last) = account_accesses.last_mut() {
2088 last.push(account_access);
2089 } else {
2090 account_accesses.push(vec![account_access]);
2091 }
2092 }
2093 _ => {}
2094 }
2095 }
2096
2097 #[cold]
2102 fn check_mem_opcodes(&self, interpreter: &mut Interpreter, depth: u64) {
2103 let Some(ranges) = self.allowed_mem_writes.get(&depth) else {
2104 return;
2105 };
2106
2107 macro_rules! mem_opcode_match {
2116 ($(($opcode:ident, $offset_depth:expr, $size_depth:expr, $writes:expr)),* $(,)?) => {
2117 match interpreter.bytecode.opcode() {
2118 op::MSTORE => {
2123 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2125
2126 if !ranges.iter().any(|range| {
2129 range.contains(&offset) && range.contains(&(offset + 31))
2130 }) {
2131 let value = try_or_return!(interpreter.stack.peek(1)).to_be_bytes::<32>();
2136 if value[..SELECTOR_LEN] == stopExpectSafeMemoryCall::SELECTOR {
2137 return
2138 }
2139
2140 disallowed_mem_write(offset, 32, interpreter, ranges);
2141 return
2142 }
2143 }
2144 op::MSTORE8 => {
2145 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2147
2148 if !ranges.iter().any(|range| range.contains(&offset)) {
2151 disallowed_mem_write(offset, 1, interpreter, ranges);
2152 return
2153 }
2154 }
2155
2156 op::MLOAD => {
2161 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2163
2164 if offset >= interpreter.memory.size() as u64 && !ranges.iter().any(|range| {
2168 range.contains(&offset) && range.contains(&(offset + 31))
2169 }) {
2170 disallowed_mem_write(offset, 32, interpreter, ranges);
2171 return
2172 }
2173 }
2174
2175 op::CALL => {
2180 let dest_offset = try_or_return!(interpreter.stack.peek(5)).saturating_to::<u64>();
2182
2183 let size = try_or_return!(interpreter.stack.peek(6)).saturating_to::<u64>();
2185
2186 let fail_cond = !ranges.iter().any(|range| {
2190 range.contains(&dest_offset) &&
2191 range.contains(&(dest_offset + size.saturating_sub(1)))
2192 });
2193
2194 if fail_cond {
2197 let to = Address::from_word(try_or_return!(interpreter.stack.peek(1)).to_be_bytes::<32>().into());
2201 if to == CHEATCODE_ADDRESS {
2202 let args_offset = try_or_return!(interpreter.stack.peek(3)).saturating_to::<usize>();
2203 let args_size = try_or_return!(interpreter.stack.peek(4)).saturating_to::<usize>();
2204 let memory_word = interpreter.memory.slice_len(args_offset, args_size);
2205 if memory_word[..SELECTOR_LEN] == stopExpectSafeMemoryCall::SELECTOR {
2206 return
2207 }
2208 }
2209
2210 disallowed_mem_write(dest_offset, size, interpreter, ranges);
2211 return
2212 }
2213 }
2214
2215 $(op::$opcode => {
2216 let dest_offset = try_or_return!(interpreter.stack.peek($offset_depth)).saturating_to::<u64>();
2218
2219 let size = try_or_return!(interpreter.stack.peek($size_depth)).saturating_to::<u64>();
2221
2222 let fail_cond = !ranges.iter().any(|range| {
2226 range.contains(&dest_offset) &&
2227 range.contains(&(dest_offset + size.saturating_sub(1)))
2228 }) && ($writes ||
2229 [dest_offset, (dest_offset + size).saturating_sub(1)].into_iter().any(|offset| {
2230 offset >= interpreter.memory.size() as u64
2231 })
2232 );
2233
2234 if fail_cond {
2237 disallowed_mem_write(dest_offset, size, interpreter, ranges);
2238 return
2239 }
2240 })*
2241
2242 _ => {}
2243 }
2244 }
2245 }
2246
2247 mem_opcode_match!(
2250 (CALLDATACOPY, 0, 2, true),
2251 (CODECOPY, 0, 2, true),
2252 (RETURNDATACOPY, 0, 2, true),
2253 (EXTCODECOPY, 1, 3, true),
2254 (CALLCODE, 5, 6, true),
2255 (STATICCALL, 4, 5, true),
2256 (DELEGATECALL, 4, 5, true),
2257 (KECCAK256, 0, 1, false),
2258 (LOG0, 0, 1, false),
2259 (LOG1, 0, 1, false),
2260 (LOG2, 0, 1, false),
2261 (LOG3, 0, 1, false),
2262 (LOG4, 0, 1, false),
2263 (CREATE, 1, 2, false),
2264 (CREATE2, 1, 2, false),
2265 (RETURN, 0, 1, false),
2266 (REVERT, 0, 1, false),
2267 );
2268 }
2269}
2270
2271fn disallowed_mem_write(
2277 dest_offset: u64,
2278 size: u64,
2279 interpreter: &mut Interpreter,
2280 ranges: &[Range<u64>],
2281) {
2282 let revert_string = format!(
2283 "memory write at offset 0x{:02X} of size 0x{:02X} not allowed; safe range: {}",
2284 dest_offset,
2285 size,
2286 ranges.iter().map(|r| format!("(0x{:02X}, 0x{:02X}]", r.start, r.end)).join(" U ")
2287 );
2288
2289 interpreter.bytecode.set_action(InterpreterAction::new_return(
2290 InstructionResult::Revert,
2291 Bytes::from(revert_string.into_bytes()),
2292 interpreter.gas,
2293 ));
2294}
2295
2296fn check_if_fixed_gas_limit(ecx: &Ecx, call_gas_limit: u64) -> bool {
2299 ecx.tx.gas_limit > ecx.block.gas_limit &&
2304 call_gas_limit <= ecx.block.gas_limit
2305 && call_gas_limit > 2300
2308}
2309
2310fn access_is_call(kind: crate::Vm::AccountAccessKind) -> bool {
2312 matches!(
2313 kind,
2314 crate::Vm::AccountAccessKind::Call
2315 | crate::Vm::AccountAccessKind::StaticCall
2316 | crate::Vm::AccountAccessKind::CallCode
2317 | crate::Vm::AccountAccessKind::DelegateCall
2318 )
2319}
2320
2321fn append_storage_access(
2323 last: &mut Vec<AccountAccess>,
2324 storage_access: crate::Vm::StorageAccess,
2325 storage_depth: u64,
2326) {
2327 if !last.is_empty() && last.first().unwrap().depth < storage_depth {
2329 if last.len() == 1 {
2335 last.first_mut().unwrap().storageAccesses.push(storage_access);
2336 } else {
2337 let last_record = last.last_mut().unwrap();
2338 if last_record.kind as u8 == crate::Vm::AccountAccessKind::Resume as u8 {
2339 last_record.storageAccesses.push(storage_access);
2340 } else {
2341 let entry = last.first().unwrap();
2342 let resume_record = crate::Vm::AccountAccess {
2343 chainInfo: crate::Vm::ChainInfo {
2344 forkId: entry.chainInfo.forkId,
2345 chainId: entry.chainInfo.chainId,
2346 },
2347 accessor: entry.accessor,
2348 account: entry.account,
2349 kind: crate::Vm::AccountAccessKind::Resume,
2350 initialized: entry.initialized,
2351 storageAccesses: vec![storage_access],
2352 reverted: entry.reverted,
2353 oldBalance: U256::ZERO,
2355 newBalance: U256::ZERO,
2356 value: U256::ZERO,
2357 data: Bytes::new(),
2358 deployedCode: Bytes::new(),
2359 depth: entry.depth,
2360 };
2361 last.push(resume_record);
2362 }
2363 }
2364 }
2365}
2366
2367fn apply_dispatch(
2369 calls: &Vm::VmCalls,
2370 ccx: &mut CheatsCtxt,
2371 executor: &mut dyn CheatcodesExecutor,
2372) -> Result {
2373 let cheat = calls_as_dyn_cheatcode(calls);
2374
2375 let _guard = debug_span!(target: "cheatcodes", "apply", id = %cheat.id()).entered();
2376 trace!(target: "cheatcodes", cheat = ?cheat.as_debug(), "applying");
2377
2378 if let spec::Status::Deprecated(replacement) = *cheat.status() {
2379 ccx.state.deprecated.insert(cheat.signature(), replacement);
2380 }
2381
2382 let mut result = cheat.dyn_apply(ccx, executor);
2384
2385 if let Err(e) = &mut result
2387 && e.is_str()
2388 {
2389 let name = cheat.name();
2390 if !name.contains("assert") && name != "rpcUrl" {
2394 *e = fmt_err!("vm.{name}: {e}");
2395 }
2396 }
2397
2398 trace!(
2399 target: "cheatcodes",
2400 return = %match &result {
2401 Ok(b) => hex::encode(b),
2402 Err(e) => e.to_string(),
2403 }
2404 );
2405
2406 result
2407}
2408
2409fn calls_as_dyn_cheatcode(calls: &Vm::VmCalls) -> &dyn DynCheatcode {
2410 macro_rules! as_dyn {
2411 ($($variant:ident),*) => {
2412 match calls {
2413 $(Vm::VmCalls::$variant(cheat) => cheat,)*
2414 }
2415 };
2416 }
2417 vm_calls!(as_dyn)
2418}
2419
2420fn will_exit(action: &InterpreterAction) -> bool {
2422 match action {
2423 InterpreterAction::Return(result) => {
2424 result.result.is_ok_or_revert() || result.result.is_error()
2425 }
2426 _ => false,
2427 }
2428}