1use crate::{
4 evm::{
5 mapping::{self, MappingSlots},
6 mock::{MockCallDataContext, MockCallReturnData},
7 prank::Prank,
8 DealRecord, GasRecord, RecordAccess,
9 },
10 inspector::utils::CommonCreateInput,
11 script::{Broadcast, Wallets},
12 test::{
13 assume::AssumeNoRevert,
14 expect::{
15 self, ExpectedCallData, ExpectedCallTracker, ExpectedCallType, ExpectedCreate,
16 ExpectedEmitTracker, ExpectedRevert, ExpectedRevertKind,
17 },
18 revert_handlers,
19 },
20 utils::IgnoredTraces,
21 CheatsConfig, CheatsCtxt, DynCheatcode, Error, Result,
22 Vm::{self, AccountAccess},
23};
24use alloy_consensus::BlobTransactionSidecar;
25use alloy_evm::eth::EthEvmContext;
26use alloy_network::TransactionBuilder4844;
27use alloy_primitives::{
28 hex,
29 map::{AddressHashMap, HashMap, HashSet},
30 Address, Bytes, Log, TxKind, B256, U256,
31};
32use alloy_rpc_types::{
33 request::{TransactionInput, TransactionRequest},
34 AccessList,
35};
36use alloy_sol_types::{SolCall, SolInterface, SolValue};
37use foundry_common::{evm::Breakpoints, TransactionMaybeSigned, SELECTOR_LEN};
38use foundry_evm_core::{
39 abi::Vm::stopExpectSafeMemoryCall,
40 backend::{DatabaseError, DatabaseExt, RevertDiagnostic},
41 constants::{CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS, MAGIC_ASSUME},
42 evm::{new_evm_with_existing_context, FoundryEvm},
43 InspectorExt,
44};
45use foundry_evm_traces::{TracingInspector, TracingInspectorConfig};
46use foundry_wallets::multi_wallet::MultiWallet;
47use itertools::Itertools;
48use proptest::test_runner::{RngAlgorithm, TestRng, TestRunner};
49use rand::Rng;
50use revm::{
51 bytecode::opcode as op,
52 context::{result::EVMError, BlockEnv, JournalTr, LocalContext, TransactionType},
53 context_interface::{transaction::SignedAuthorization, CreateScheme},
54 handler::FrameResult,
55 interpreter::{
56 interpreter_types::{Jumps, MemoryTr},
57 CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, FrameInput, Gas, Host,
58 InstructionResult, Interpreter, InterpreterAction, InterpreterResult,
59 },
60 state::EvmStorageSlot,
61 Inspector, Journal,
62};
63use serde_json::Value;
64use std::{
65 cmp::max,
66 collections::{BTreeMap, VecDeque},
67 fs::File,
68 io::BufReader,
69 ops::Range,
70 path::PathBuf,
71 sync::Arc,
72};
73
74mod utils;
75
76pub type Ecx<'a, 'b, 'c> = &'a mut EthEvmContext<&'b mut (dyn DatabaseExt + 'c)>;
77
78pub trait CheatcodesExecutor {
84 fn get_inspector<'a>(&'a mut self, cheats: &'a mut Cheatcodes) -> Box<dyn InspectorExt + 'a>;
87
88 fn exec_create(
90 &mut self,
91 inputs: CreateInputs,
92 ccx: &mut CheatsCtxt,
93 ) -> Result<CreateOutcome, EVMError<DatabaseError>> {
94 with_evm(self, ccx, |evm| {
95 evm.inner.ctx.journaled_state.depth += 1;
96
97 let frame = FrameInput::Create(Box::new(inputs));
98
99 let outcome = match evm.run_execution(frame)? {
100 FrameResult::Call(_) | FrameResult::EOFCreate(_) => unreachable!(),
101 FrameResult::Create(create) => create,
102 };
103
104 evm.inner.ctx.journaled_state.depth -= 1;
105
106 Ok(outcome)
107 })
108 }
109
110 fn console_log(&mut self, ccx: &mut CheatsCtxt, msg: &str) {
111 self.get_inspector(ccx.state).console_log(msg);
112 }
113
114 fn tracing_inspector(&mut self) -> Option<&mut Option<TracingInspector>> {
116 None
117 }
118}
119
120fn with_evm<E, F, O>(
122 executor: &mut E,
123 ccx: &mut CheatsCtxt,
124 f: F,
125) -> Result<O, EVMError<DatabaseError>>
126where
127 E: CheatcodesExecutor + ?Sized,
128 F: for<'a, 'b> FnOnce(
129 &mut FoundryEvm<'a, &'b mut dyn InspectorExt>,
130 ) -> Result<O, EVMError<DatabaseError>>,
131{
132 let mut inspector = executor.get_inspector(ccx.state);
133 let error = std::mem::replace(&mut ccx.ecx.error, Ok(()));
134
135 let ctx = EthEvmContext {
136 block: ccx.ecx.block.clone(),
137 cfg: ccx.ecx.cfg.clone(),
138 tx: ccx.ecx.tx.clone(),
139 journaled_state: Journal {
140 inner: ccx.ecx.journaled_state.inner.clone(),
141 database: &mut *ccx.ecx.journaled_state.database as &mut dyn DatabaseExt,
142 },
143 local: LocalContext::default(),
144 chain: (),
145 error,
146 };
147
148 let mut evm = new_evm_with_existing_context(ctx, &mut *inspector);
149
150 let res = f(&mut evm)?;
151
152 ccx.ecx.journaled_state.inner = evm.inner.ctx.journaled_state.inner;
153 ccx.ecx.block = evm.inner.ctx.block;
154 ccx.ecx.tx = evm.inner.ctx.tx;
155 ccx.ecx.cfg = evm.inner.ctx.cfg;
156 ccx.ecx.error = evm.inner.ctx.error;
157
158 Ok(res)
159}
160
161#[derive(Debug, Default, Clone, Copy)]
164struct TransparentCheatcodesExecutor;
165
166impl CheatcodesExecutor for TransparentCheatcodesExecutor {
167 fn get_inspector<'a>(&'a mut self, cheats: &'a mut Cheatcodes) -> Box<dyn InspectorExt + 'a> {
168 Box::new(cheats)
169 }
170}
171
172macro_rules! try_or_return {
173 ($e:expr) => {
174 match $e {
175 Ok(v) => v,
176 Err(_) => return,
177 }
178 };
179}
180
181#[derive(Debug, Default)]
183pub struct TestContext {
184 pub opened_read_files: HashMap<PathBuf, BufReader<File>>,
186}
187
188impl Clone for TestContext {
190 fn clone(&self) -> Self {
191 Default::default()
192 }
193}
194
195impl TestContext {
196 #[inline]
198 pub fn clear(&mut self) {
199 self.opened_read_files.clear();
200 }
201}
202
203#[derive(Clone, Debug)]
205pub struct BroadcastableTransaction {
206 pub rpc: Option<String>,
208 pub transaction: TransactionMaybeSigned,
210}
211
212#[derive(Clone, Debug, Copy)]
213pub struct RecordDebugStepInfo {
214 pub start_node_idx: usize,
216 pub original_tracer_config: TracingInspectorConfig,
218}
219
220#[derive(Clone, Debug, Default)]
222pub struct GasMetering {
223 pub paused: bool,
225 pub touched: bool,
228 pub reset: bool,
230 pub paused_frames: Vec<Gas>,
232
233 pub active_gas_snapshot: Option<(String, String)>,
235
236 pub last_call_gas: Option<crate::Vm::Gas>,
239
240 pub recording: bool,
242 pub last_gas_used: u64,
244 pub gas_records: Vec<GasRecord>,
246}
247
248impl GasMetering {
249 pub fn start(&mut self) {
251 self.recording = true;
252 }
253
254 pub fn stop(&mut self) {
256 self.recording = false;
257 }
258
259 pub fn resume(&mut self) {
261 if self.paused {
262 self.paused = false;
263 self.touched = true;
264 }
265 self.paused_frames.clear();
266 }
267
268 pub fn reset(&mut self) {
270 self.paused = false;
271 self.touched = true;
272 self.reset = true;
273 self.paused_frames.clear();
274 }
275}
276
277#[derive(Clone, Debug, Default)]
279pub struct ArbitraryStorage {
280 pub values: HashMap<Address, HashMap<U256, U256>>,
284 pub copies: HashMap<Address, Address>,
286 pub overwrites: HashSet<Address>,
288}
289
290impl ArbitraryStorage {
291 pub fn mark_arbitrary(&mut self, address: &Address, overwrite: bool) {
293 self.values.insert(*address, HashMap::default());
294 if overwrite {
295 self.overwrites.insert(*address);
296 } else {
297 self.overwrites.remove(address);
298 }
299 }
300
301 pub fn mark_copy(&mut self, from: &Address, to: &Address) {
303 if self.values.contains_key(from) {
304 self.copies.insert(*to, *from);
305 }
306 }
307
308 pub fn save(&mut self, ecx: Ecx, address: Address, slot: U256, data: U256) {
312 self.values.get_mut(&address).expect("missing arbitrary address entry").insert(slot, data);
313 if let Ok(mut account) = ecx.journaled_state.load_account(address) {
314 account.storage.insert(slot, EvmStorageSlot::new(data));
315 }
316 }
317
318 pub fn copy(&mut self, ecx: Ecx, target: Address, slot: U256, new_value: U256) -> U256 {
324 let source = self.copies.get(&target).expect("missing arbitrary copy target entry");
325 let storage_cache = self.values.get_mut(source).expect("missing arbitrary source storage");
326 let value = match storage_cache.get(&slot) {
327 Some(value) => *value,
328 None => {
329 storage_cache.insert(slot, new_value);
330 if let Ok(mut source_account) = ecx.journaled_state.load_account(*source) {
332 source_account.storage.insert(slot, EvmStorageSlot::new(new_value));
333 }
334 new_value
335 }
336 };
337 if let Ok(mut target_account) = ecx.journaled_state.load_account(target) {
339 target_account.storage.insert(slot, EvmStorageSlot::new(value));
340 }
341 value
342 }
343}
344
345pub type BroadcastableTransactions = VecDeque<BroadcastableTransaction>;
347
348#[derive(Clone, Debug)]
366pub struct Cheatcodes {
367 pub block: Option<BlockEnv>,
372
373 pub active_delegations: Vec<SignedAuthorization>,
377
378 pub active_blob_sidecar: Option<BlobTransactionSidecar>,
380
381 pub gas_price: Option<u128>,
386
387 pub labels: AddressHashMap<String>,
389
390 pub pranks: BTreeMap<usize, Prank>,
392
393 pub expected_revert: Option<ExpectedRevert>,
395
396 pub assume_no_revert: Option<AssumeNoRevert>,
398
399 pub fork_revert_diagnostic: Option<RevertDiagnostic>,
401
402 pub accesses: RecordAccess,
404
405 pub recording_accesses: bool,
407
408 pub recorded_account_diffs_stack: Option<Vec<Vec<AccountAccess>>>,
414
415 pub record_debug_steps_info: Option<RecordDebugStepInfo>,
417
418 pub recorded_logs: Option<Vec<crate::Vm::Log>>,
420
421 pub mocked_calls: HashMap<Address, BTreeMap<MockCallDataContext, VecDeque<MockCallReturnData>>>,
424
425 pub mocked_functions: HashMap<Address, HashMap<Bytes, Address>>,
427
428 pub expected_calls: ExpectedCallTracker,
430 pub expected_emits: ExpectedEmitTracker,
432 pub expected_creates: Vec<ExpectedCreate>,
434
435 pub allowed_mem_writes: HashMap<u64, Vec<Range<u64>>>,
437
438 pub broadcast: Option<Broadcast>,
440
441 pub broadcastable_transactions: BroadcastableTransactions,
443
444 pub access_list: Option<AccessList>,
446
447 pub config: Arc<CheatsConfig>,
449
450 pub test_context: TestContext,
452
453 pub fs_commit: bool,
456
457 pub serialized_jsons: BTreeMap<String, BTreeMap<String, Value>>,
460
461 pub eth_deals: Vec<DealRecord>,
463
464 pub gas_metering: GasMetering,
466
467 pub gas_snapshots: BTreeMap<String, BTreeMap<String, String>>,
470
471 pub mapping_slots: Option<AddressHashMap<MappingSlots>>,
473
474 pub pc: usize,
476 pub breakpoints: Breakpoints,
479
480 pub intercept_next_create_call: bool,
482
483 test_runner: Option<TestRunner>,
486
487 pub ignored_traces: IgnoredTraces,
489
490 pub arbitrary_storage: Option<ArbitraryStorage>,
492
493 pub deprecated: HashMap<&'static str, Option<&'static str>>,
495 pub wallets: Option<Wallets>,
497}
498
499impl Default for Cheatcodes {
503 fn default() -> Self {
504 Self::new(Arc::default())
505 }
506}
507
508impl Cheatcodes {
509 pub fn new(config: Arc<CheatsConfig>) -> Self {
511 Self {
512 fs_commit: true,
513 labels: config.labels.clone(),
514 config,
515 block: Default::default(),
516 active_delegations: Default::default(),
517 active_blob_sidecar: Default::default(),
518 gas_price: Default::default(),
519 pranks: Default::default(),
520 expected_revert: Default::default(),
521 assume_no_revert: Default::default(),
522 fork_revert_diagnostic: Default::default(),
523 accesses: Default::default(),
524 recording_accesses: Default::default(),
525 recorded_account_diffs_stack: Default::default(),
526 recorded_logs: Default::default(),
527 record_debug_steps_info: Default::default(),
528 mocked_calls: Default::default(),
529 mocked_functions: Default::default(),
530 expected_calls: Default::default(),
531 expected_emits: Default::default(),
532 expected_creates: Default::default(),
533 allowed_mem_writes: Default::default(),
534 broadcast: Default::default(),
535 broadcastable_transactions: Default::default(),
536 access_list: Default::default(),
537 test_context: Default::default(),
538 serialized_jsons: Default::default(),
539 eth_deals: Default::default(),
540 gas_metering: Default::default(),
541 gas_snapshots: Default::default(),
542 mapping_slots: Default::default(),
543 pc: Default::default(),
544 breakpoints: Default::default(),
545 intercept_next_create_call: Default::default(),
546 test_runner: Default::default(),
547 ignored_traces: Default::default(),
548 arbitrary_storage: Default::default(),
549 deprecated: Default::default(),
550 wallets: Default::default(),
551 }
552 }
553
554 pub fn get_prank(&self, depth: usize) -> Option<&Prank> {
558 self.pranks.range(..=depth).last().map(|(_, prank)| prank)
559 }
560
561 pub fn wallets(&mut self) -> &Wallets {
563 self.wallets.get_or_insert_with(|| Wallets::new(MultiWallet::default(), None))
564 }
565
566 pub fn set_wallets(&mut self, wallets: Wallets) {
568 self.wallets = Some(wallets);
569 }
570
571 pub fn add_delegation(&mut self, authorization: SignedAuthorization) {
573 self.active_delegations.push(authorization);
574 }
575
576 fn apply_cheatcode(
578 &mut self,
579 ecx: Ecx,
580 call: &CallInputs,
581 executor: &mut dyn CheatcodesExecutor,
582 ) -> Result {
583 let decoded = Vm::VmCalls::abi_decode(&call.input.bytes(ecx)).map_err(|e| {
585 if let alloy_sol_types::Error::UnknownSelector { name: _, selector } = e {
586 let msg = format!(
587 "unknown cheatcode with selector {selector}; \
588 you may have a mismatch between the `Vm` interface (likely in `forge-std`) \
589 and the `forge` version"
590 );
591 return alloy_sol_types::Error::Other(std::borrow::Cow::Owned(msg));
592 }
593 e
594 })?;
595
596 let caller = call.caller;
597
598 ecx.journaled_state.database.ensure_cheatcode_access_forking_mode(&caller)?;
601
602 apply_dispatch(
603 &decoded,
604 &mut CheatsCtxt { state: self, ecx, gas_limit: call.gas_limit, caller },
605 executor,
606 )
607 }
608
609 fn allow_cheatcodes_on_create(&self, ecx: Ecx, caller: Address, created_address: Address) {
615 if ecx.journaled_state.depth <= 1 ||
616 ecx.journaled_state.database.has_cheatcode_access(&caller)
617 {
618 ecx.journaled_state.database.allow_cheatcode_access(created_address);
619 }
620 }
621
622 fn apply_accesslist(&mut self, ecx: Ecx) {
628 if let Some(access_list) = &self.access_list {
629 ecx.tx.access_list = access_list.clone();
630
631 if ecx.tx.tx_type == TransactionType::Legacy as u8 {
632 ecx.tx.tx_type = TransactionType::Eip2930 as u8;
633 }
634 }
635 }
636
637 pub fn on_revert(&mut self, ecx: Ecx) {
642 trace!(deals=?self.eth_deals.len(), "rolling back deals");
643
644 if self.expected_revert.is_some() {
646 return;
647 }
648
649 if ecx.journaled_state.depth() > 0 {
651 return;
652 }
653
654 while let Some(record) = self.eth_deals.pop() {
658 if let Some(acc) = ecx.journaled_state.state.get_mut(&record.address) {
659 acc.info.balance = record.old_balance;
660 }
661 }
662 }
663
664 fn create_common<Input>(&mut self, ecx: Ecx, mut input: Input) -> Option<CreateOutcome>
666 where
667 Input: CommonCreateInput,
668 {
669 if self.intercept_next_create_call {
671 self.intercept_next_create_call = false;
673
674 let output = input.init_code();
676
677 return Some(CreateOutcome {
679 result: InterpreterResult {
680 result: InstructionResult::Revert,
681 output,
682 gas: Gas::new(input.gas_limit()),
683 },
684 address: None,
685 });
686 }
687
688 let gas = Gas::new(input.gas_limit());
689 let curr_depth = ecx.journaled_state.depth();
690
691 if let Some(prank) = &self.get_prank(curr_depth) {
693 if curr_depth >= prank.depth && input.caller() == prank.prank_caller {
694 let mut prank_applied = false;
695
696 if curr_depth == prank.depth {
698 input.set_caller(prank.new_caller);
699 prank_applied = true;
700 }
701
702 if let Some(new_origin) = prank.new_origin {
704 ecx.tx.caller = new_origin;
705 prank_applied = true;
706 }
707
708 if prank_applied {
710 if let Some(applied_prank) = prank.first_time_applied() {
711 self.pranks.insert(curr_depth, applied_prank);
712 }
713 }
714 }
715 }
716
717 self.apply_accesslist(ecx);
719
720 if let Some(broadcast) = &self.broadcast {
722 if curr_depth >= broadcast.depth && input.caller() == broadcast.original_caller {
723 if let Err(err) = ecx.journaled_state.load_account(broadcast.new_origin) {
724 return Some(CreateOutcome {
725 result: InterpreterResult {
726 result: InstructionResult::Revert,
727 output: Error::encode(err),
728 gas,
729 },
730 address: None,
731 });
732 }
733
734 ecx.tx.caller = broadcast.new_origin;
735
736 if curr_depth == broadcast.depth {
737 input.set_caller(broadcast.new_origin);
738 let is_fixed_gas_limit = check_if_fixed_gas_limit(&ecx, input.gas_limit());
739
740 let account = &ecx.journaled_state.inner.state()[&broadcast.new_origin];
741 self.broadcastable_transactions.push_back(BroadcastableTransaction {
742 rpc: ecx.journaled_state.database.active_fork_url(),
743 transaction: TransactionRequest {
744 from: Some(broadcast.new_origin),
745 to: None,
746 value: Some(input.value()),
747 input: TransactionInput::new(input.init_code()),
748 nonce: Some(account.info.nonce),
749 gas: if is_fixed_gas_limit { Some(input.gas_limit()) } else { None },
750 ..Default::default()
751 }
752 .into(),
753 });
754
755 input.log_debug(self, &input.scheme().unwrap_or(CreateScheme::Create));
756 }
757 }
758 }
759
760 let address = input.allow_cheatcodes(self, ecx);
762
763 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
765 recorded_account_diffs_stack.push(vec![AccountAccess {
766 chainInfo: crate::Vm::ChainInfo {
767 forkId: ecx.journaled_state.db().active_fork_id().unwrap_or_default(),
768 chainId: U256::from(ecx.cfg.chain_id),
769 },
770 accessor: input.caller(),
771 account: address,
772 kind: crate::Vm::AccountAccessKind::Create,
773 initialized: true,
774 oldBalance: U256::ZERO, newBalance: U256::ZERO, value: input.value(),
777 data: input.init_code(),
778 reverted: false,
779 deployedCode: Bytes::new(), storageAccesses: vec![], depth: curr_depth as u64,
782 }]);
783 }
784
785 None
786 }
787
788 fn create_end_common(
790 &mut self,
791 ecx: Ecx,
792 call: Option<&CreateInputs>,
793 outcome: &mut CreateOutcome,
794 ) {
795 let curr_depth = ecx.journaled_state.depth();
796
797 if let Some(prank) = &self.get_prank(curr_depth) {
799 if curr_depth == prank.depth {
800 ecx.tx.caller = prank.prank_origin;
801
802 if prank.single_call {
804 std::mem::take(&mut self.pranks);
805 }
806 }
807 }
808
809 if let Some(broadcast) = &self.broadcast {
811 if curr_depth == broadcast.depth {
812 ecx.tx.caller = broadcast.original_origin;
813
814 if broadcast.single_call {
816 std::mem::take(&mut self.broadcast);
817 }
818 }
819 }
820
821 if let Some(expected_revert) = &self.expected_revert {
823 if curr_depth <= expected_revert.depth &&
824 matches!(expected_revert.kind, ExpectedRevertKind::Default)
825 {
826 let mut expected_revert = std::mem::take(&mut self.expected_revert).unwrap();
827 return match revert_handlers::handle_expect_revert(
828 false,
829 true,
830 self.config.internal_expect_revert,
831 &expected_revert,
832 outcome.result.result,
833 outcome.result.output.clone(),
834 &self.config.available_artifacts,
835 ) {
836 Ok((address, retdata)) => {
837 expected_revert.actual_count += 1;
838 if expected_revert.actual_count < expected_revert.count {
839 self.expected_revert = Some(expected_revert.clone());
840 }
841
842 outcome.result.result = InstructionResult::Return;
843 outcome.result.output = retdata;
844 outcome.address = address;
845 }
846 Err(err) => {
847 outcome.result.result = InstructionResult::Revert;
848 outcome.result.output = err.abi_encode().into();
849 }
850 };
851 }
852 }
853
854 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
857 if curr_depth > 0 {
859 if let Some(last_depth) = &mut recorded_account_diffs_stack.pop() {
860 if outcome.result.is_revert() {
863 last_depth.iter_mut().for_each(|element| {
864 element.reverted = true;
865 element
866 .storageAccesses
867 .iter_mut()
868 .for_each(|storage_access| storage_access.reverted = true);
869 })
870 }
871
872 if let Some(create_access) = last_depth.first_mut() {
873 let depth = ecx.journaled_state.depth();
878 if create_access.depth == depth as u64 {
879 debug_assert_eq!(
880 create_access.kind as u8,
881 crate::Vm::AccountAccessKind::Create as u8
882 );
883 if let Some(address) = outcome.address {
884 if let Ok(created_acc) = ecx.journaled_state.load_account(address) {
885 create_access.newBalance = created_acc.info.balance;
886 create_access.deployedCode = created_acc
887 .info
888 .code
889 .clone()
890 .unwrap_or_default()
891 .original_bytes();
892 }
893 }
894 }
895 if let Some(last) = recorded_account_diffs_stack.last_mut() {
900 last.append(last_depth);
901 } else {
902 recorded_account_diffs_stack.push(last_depth.clone());
903 }
904 }
905 }
906 }
907 }
908
909 if !self.expected_creates.is_empty() {
911 if let (Some(address), Some(call)) = (outcome.address, call) {
912 if let Ok(created_acc) = ecx.journaled_state.load_account(address) {
913 let bytecode =
914 created_acc.info.code.clone().unwrap_or_default().original_bytes();
915 if let Some((index, _)) =
916 self.expected_creates.iter().find_position(|expected_create| {
917 expected_create.deployer == call.caller &&
918 expected_create.create_scheme.eq(call.scheme.into()) &&
919 expected_create.bytecode == bytecode
920 })
921 {
922 self.expected_creates.swap_remove(index);
923 }
924 }
925 }
926 }
927 }
928
929 pub fn call_with_executor(
930 &mut self,
931 ecx: Ecx,
932 call: &mut CallInputs,
933 executor: &mut impl CheatcodesExecutor,
934 ) -> Option<CallOutcome> {
935 let gas = Gas::new(call.gas_limit);
936 let curr_depth = ecx.journaled_state.depth();
937
938 if curr_depth == 0 {
942 let sender = ecx.tx.caller;
943 let account = match super::evm::journaled_account(ecx, sender) {
944 Ok(account) => account,
945 Err(err) => {
946 return Some(CallOutcome {
947 result: InterpreterResult {
948 result: InstructionResult::Revert,
949 output: err.abi_encode().into(),
950 gas,
951 },
952 memory_offset: call.return_memory_offset.clone(),
953 })
954 }
955 };
956 let prev = account.info.nonce;
957 account.info.nonce = prev.saturating_sub(1);
958
959 trace!(target: "cheatcodes", %sender, nonce=account.info.nonce, prev, "corrected nonce");
960 }
961
962 if call.target_address == CHEATCODE_ADDRESS {
963 return match self.apply_cheatcode(ecx, call, executor) {
964 Ok(retdata) => Some(CallOutcome {
965 result: InterpreterResult {
966 result: InstructionResult::Return,
967 output: retdata.into(),
968 gas,
969 },
970 memory_offset: call.return_memory_offset.clone(),
971 }),
972 Err(err) => Some(CallOutcome {
973 result: InterpreterResult {
974 result: InstructionResult::Revert,
975 output: err.abi_encode().into(),
976 gas,
977 },
978 memory_offset: call.return_memory_offset.clone(),
979 }),
980 };
981 }
982
983 if call.target_address == HARDHAT_CONSOLE_ADDRESS {
984 return None;
985 }
986
987 if let Some(expected_calls_for_target) = self.expected_calls.get_mut(&call.bytecode_address)
991 {
992 for (calldata, (expected, actual_count)) in expected_calls_for_target {
994 if calldata.len() <= call.input.len() &&
997 *calldata == call.input.bytes(ecx)[..calldata.len()] &&
999 expected
1001 .value.is_none_or(|value| Some(value) == call.transfer_value()) &&
1002 expected.gas.is_none_or(|gas| gas == call.gas_limit) &&
1004 expected.min_gas.is_none_or(|min_gas| min_gas <= call.gas_limit)
1006 {
1007 *actual_count += 1;
1008 }
1009 }
1010 }
1011
1012 if let Some(mocks) = self.mocked_calls.get_mut(&call.bytecode_address) {
1014 let ctx = MockCallDataContext {
1015 calldata: call.input.bytes(ecx),
1016 value: call.transfer_value(),
1017 };
1018
1019 if let Some(return_data_queue) =
1020 match mocks.get_mut(&ctx) {
1021 Some(queue) => Some(queue),
1022 None => mocks
1023 .iter_mut()
1024 .find(|(mock, _)| {
1025 call.input.bytes(ecx).get(..mock.calldata.len()) ==
1026 Some(&mock.calldata[..]) &&
1027 mock.value
1028 .is_none_or(|value| Some(value) == call.transfer_value())
1029 })
1030 .map(|(_, v)| v),
1031 }
1032 {
1033 if let Some(return_data) = if return_data_queue.len() == 1 {
1034 return_data_queue.front().map(|x| x.to_owned())
1036 } else {
1037 return_data_queue.pop_front()
1039 } {
1040 return Some(CallOutcome {
1041 result: InterpreterResult {
1042 result: return_data.ret_type,
1043 output: return_data.data,
1044 gas,
1045 },
1046 memory_offset: call.return_memory_offset.clone(),
1047 });
1048 }
1049 }
1050 }
1051
1052 if let Some(prank) = &self.get_prank(curr_depth) {
1054 if prank.delegate_call && curr_depth == prank.depth {
1056 if let CallScheme::DelegateCall | CallScheme::ExtDelegateCall = call.scheme {
1057 call.target_address = prank.new_caller;
1058 call.caller = prank.new_caller;
1059 if let Some(new_origin) = prank.new_origin {
1060 ecx.tx.caller = new_origin;
1061 }
1062 }
1063 }
1064
1065 if curr_depth >= prank.depth && call.caller == prank.prank_caller {
1066 let mut prank_applied = false;
1067
1068 if curr_depth == prank.depth {
1070 call.caller = prank.new_caller;
1071 prank_applied = true;
1072 }
1073
1074 if let Some(new_origin) = prank.new_origin {
1076 ecx.tx.caller = new_origin;
1077 prank_applied = true;
1078 }
1079
1080 if prank_applied {
1082 if let Some(applied_prank) = prank.first_time_applied() {
1083 self.pranks.insert(curr_depth, applied_prank);
1084 }
1085 }
1086 }
1087 }
1088
1089 self.apply_accesslist(ecx);
1091
1092 if let Some(broadcast) = &self.broadcast {
1094 if curr_depth == broadcast.depth && call.caller == broadcast.original_caller {
1099 ecx.tx.caller = broadcast.new_origin;
1103
1104 call.caller = broadcast.new_origin;
1105 if !call.is_static {
1110 if let Err(err) = ecx.journaled_state.load_account(broadcast.new_origin) {
1111 return Some(CallOutcome {
1112 result: InterpreterResult {
1113 result: InstructionResult::Revert,
1114 output: Error::encode(err),
1115 gas,
1116 },
1117 memory_offset: call.return_memory_offset.clone(),
1118 });
1119 }
1120
1121 let is_fixed_gas_limit = check_if_fixed_gas_limit(&ecx, call.gas_limit);
1122
1123 let input = TransactionInput::new(call.input.bytes(ecx));
1124
1125 let account =
1126 ecx.journaled_state.inner.state().get_mut(&broadcast.new_origin).unwrap();
1127
1128 let mut tx_req = TransactionRequest {
1129 from: Some(broadcast.new_origin),
1130 to: Some(TxKind::from(Some(call.target_address))),
1131 value: call.transfer_value(),
1132 input,
1133 nonce: Some(account.info.nonce),
1134 chain_id: Some(ecx.cfg.chain_id),
1135 gas: if is_fixed_gas_limit { Some(call.gas_limit) } else { None },
1136 ..Default::default()
1137 };
1138
1139 let active_delegations = std::mem::take(&mut self.active_delegations);
1140 if let Some(blob_sidecar) = self.active_blob_sidecar.take() {
1142 if !active_delegations.is_empty() {
1144 let msg = "both delegation and blob are active; `attachBlob` and `attachDelegation` are not compatible";
1145 return Some(CallOutcome {
1146 result: InterpreterResult {
1147 result: InstructionResult::Revert,
1148 output: Error::encode(msg),
1149 gas,
1150 },
1151 memory_offset: call.return_memory_offset.clone(),
1152 });
1153 }
1154 tx_req.set_blob_sidecar(blob_sidecar);
1155 }
1156
1157 if !active_delegations.is_empty() {
1159 for auth in &active_delegations {
1160 let Ok(authority) = auth.recover_authority() else {
1161 continue;
1162 };
1163 if authority == broadcast.new_origin {
1164 account.info.nonce += 1;
1167 }
1168 }
1169 tx_req.authorization_list = Some(active_delegations);
1170 }
1171
1172 self.broadcastable_transactions.push_back(BroadcastableTransaction {
1173 rpc: ecx.journaled_state.database.active_fork_url(),
1174 transaction: tx_req.into(),
1175 });
1176 debug!(target: "cheatcodes", tx=?self.broadcastable_transactions.back().unwrap(), "broadcastable call");
1177
1178 if !self.config.evm_opts.isolate {
1180 let prev = account.info.nonce;
1181 account.info.nonce += 1;
1182 debug!(target: "cheatcodes", address=%broadcast.new_origin, nonce=prev+1, prev, "incremented nonce");
1183 }
1184 } else if broadcast.single_call {
1185 let msg =
1186 "`staticcall`s are not allowed after `broadcast`; use `startBroadcast` instead";
1187 return Some(CallOutcome {
1188 result: InterpreterResult {
1189 result: InstructionResult::Revert,
1190 output: Error::encode(msg),
1191 gas,
1192 },
1193 memory_offset: call.return_memory_offset.clone(),
1194 });
1195 }
1196 }
1197 }
1198
1199 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1201 let initialized;
1204 let old_balance;
1205 if let Ok(acc) = ecx.journaled_state.load_account(call.target_address) {
1206 initialized = acc.info.exists();
1207 old_balance = acc.info.balance;
1208 } else {
1209 initialized = false;
1210 old_balance = U256::ZERO;
1211 }
1212 let kind = match call.scheme {
1213 CallScheme::Call => crate::Vm::AccountAccessKind::Call,
1214 CallScheme::CallCode => crate::Vm::AccountAccessKind::CallCode,
1215 CallScheme::DelegateCall => crate::Vm::AccountAccessKind::DelegateCall,
1216 CallScheme::StaticCall => crate::Vm::AccountAccessKind::StaticCall,
1217 CallScheme::ExtCall => crate::Vm::AccountAccessKind::Call,
1218 CallScheme::ExtStaticCall => crate::Vm::AccountAccessKind::StaticCall,
1219 CallScheme::ExtDelegateCall => crate::Vm::AccountAccessKind::DelegateCall,
1220 };
1221 recorded_account_diffs_stack.push(vec![AccountAccess {
1227 chainInfo: crate::Vm::ChainInfo {
1228 forkId: ecx.journaled_state.db().active_fork_id().unwrap_or_default(),
1229 chainId: U256::from(ecx.cfg.chain_id),
1230 },
1231 accessor: call.caller,
1232 account: call.bytecode_address,
1233 kind,
1234 initialized,
1235 oldBalance: old_balance,
1236 newBalance: U256::ZERO, value: call.call_value(),
1238 data: call.input.bytes(ecx),
1239 reverted: false,
1240 deployedCode: Bytes::new(),
1241 storageAccesses: vec![], depth: ecx
1243 .journaled_state
1244 .depth()
1245 .try_into()
1246 .expect("journaled state depth exceeds u64"),
1247 }]);
1248 }
1249
1250 None
1251 }
1252
1253 pub fn rng(&mut self) -> &mut impl Rng {
1254 self.test_runner().rng()
1255 }
1256
1257 pub fn test_runner(&mut self) -> &mut TestRunner {
1258 self.test_runner.get_or_insert_with(|| match self.config.seed {
1259 Some(seed) => TestRunner::new_with_rng(
1260 proptest::test_runner::Config::default(),
1261 TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>()),
1262 ),
1263 None => TestRunner::new(proptest::test_runner::Config::default()),
1264 })
1265 }
1266
1267 pub fn arbitrary_storage(&mut self) -> &mut ArbitraryStorage {
1270 self.arbitrary_storage.get_or_insert_with(ArbitraryStorage::default)
1271 }
1272
1273 pub fn has_arbitrary_storage(&self, address: &Address) -> bool {
1275 match &self.arbitrary_storage {
1276 Some(storage) => storage.values.contains_key(address),
1277 None => false,
1278 }
1279 }
1280
1281 pub fn should_overwrite_arbitrary_storage(
1285 &self,
1286 address: &Address,
1287 storage_slot: U256,
1288 ) -> bool {
1289 match &self.arbitrary_storage {
1290 Some(storage) => {
1291 storage.overwrites.contains(address) &&
1292 storage
1293 .values
1294 .get(address)
1295 .and_then(|arbitrary_values| arbitrary_values.get(&storage_slot))
1296 .is_none()
1297 }
1298 None => false,
1299 }
1300 }
1301
1302 pub fn is_arbitrary_storage_copy(&self, address: &Address) -> bool {
1304 match &self.arbitrary_storage {
1305 Some(storage) => storage.copies.contains_key(address),
1306 None => false,
1307 }
1308 }
1309}
1310
1311impl Inspector<EthEvmContext<&mut dyn DatabaseExt>> for Cheatcodes {
1312 #[inline]
1313 fn initialize_interp(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1314 if let Some(block) = self.block.take() {
1317 ecx.block = block;
1318 }
1319 if let Some(gas_price) = self.gas_price.take() {
1320 ecx.tx.gas_price = gas_price;
1321 }
1322
1323 if self.gas_metering.paused {
1325 self.gas_metering.paused_frames.push(interpreter.control.gas);
1326 }
1327
1328 if let Some(expected) = &mut self.expected_revert {
1330 expected.max_depth = max(ecx.journaled_state.depth(), expected.max_depth);
1331 }
1332 }
1333
1334 #[inline]
1335 fn step(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1336 self.pc = interpreter.bytecode.pc();
1337
1338 if self.gas_metering.paused {
1340 self.meter_gas(interpreter);
1341 }
1342
1343 if self.gas_metering.reset {
1345 self.meter_gas_reset(interpreter);
1346 }
1347
1348 if self.recording_accesses {
1350 self.record_accesses(interpreter);
1351 }
1352
1353 if self.recorded_account_diffs_stack.is_some() {
1355 self.record_state_diffs(interpreter, ecx);
1356 }
1357
1358 if !self.allowed_mem_writes.is_empty() {
1360 self.check_mem_opcodes(
1361 interpreter,
1362 ecx.journaled_state.depth().try_into().expect("journaled state depth exceeds u64"),
1363 );
1364 }
1365
1366 if let Some(mapping_slots) = &mut self.mapping_slots {
1368 mapping::step(mapping_slots, interpreter);
1369 }
1370
1371 if self.gas_metering.recording {
1373 self.meter_gas_record(interpreter, ecx);
1374 }
1375 }
1376
1377 #[inline]
1378 fn step_end(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1379 if self.gas_metering.paused {
1380 self.meter_gas_end(interpreter);
1381 }
1382
1383 if self.gas_metering.touched {
1384 self.meter_gas_check(interpreter);
1385 }
1386
1387 if self.arbitrary_storage.is_some() {
1389 self.arbitrary_storage_end(interpreter, ecx);
1390 }
1391 }
1392
1393 fn log(&mut self, interpreter: &mut Interpreter, _ecx: Ecx, log: Log) {
1394 if !self.expected_emits.is_empty() {
1395 expect::handle_expect_emit(self, &log, interpreter);
1396 }
1397
1398 if let Some(storage_recorded_logs) = &mut self.recorded_logs {
1400 storage_recorded_logs.push(Vm::Log {
1401 topics: log.data.topics().to_vec(),
1402 data: log.data.data.clone(),
1403 emitter: log.address,
1404 });
1405 }
1406 }
1407
1408 fn call(&mut self, ecx: Ecx, inputs: &mut CallInputs) -> Option<CallOutcome> {
1409 Self::call_with_executor(self, ecx, inputs, &mut TransparentCheatcodesExecutor)
1410 }
1411
1412 fn call_end(&mut self, ecx: Ecx, call: &CallInputs, outcome: &mut CallOutcome) {
1413 let cheatcode_call = call.target_address == CHEATCODE_ADDRESS ||
1414 call.target_address == HARDHAT_CONSOLE_ADDRESS;
1415
1416 if !cheatcode_call {
1420 let curr_depth = ecx.journaled_state.depth();
1422 if let Some(prank) = &self.get_prank(curr_depth) {
1423 if curr_depth == prank.depth {
1424 ecx.tx.caller = prank.prank_origin;
1425
1426 if prank.single_call {
1428 self.pranks.remove(&curr_depth);
1429 }
1430 }
1431 }
1432
1433 if let Some(broadcast) = &self.broadcast {
1435 if curr_depth == broadcast.depth {
1436 ecx.tx.caller = broadcast.original_origin;
1437
1438 if broadcast.single_call {
1440 let _ = self.broadcast.take();
1441 }
1442 }
1443 }
1444 }
1445
1446 if let Some(assume_no_revert) = &mut self.assume_no_revert {
1448 if outcome.result.is_revert() && assume_no_revert.reverted_by.is_none() {
1451 assume_no_revert.reverted_by = Some(call.target_address);
1452 }
1453
1454 let curr_depth = ecx.journaled_state.depth();
1456 if curr_depth <= assume_no_revert.depth && !cheatcode_call {
1457 if outcome.result.is_revert() {
1460 let assume_no_revert = std::mem::take(&mut self.assume_no_revert).unwrap();
1461 return match revert_handlers::handle_assume_no_revert(
1462 &assume_no_revert,
1463 outcome.result.result,
1464 &outcome.result.output,
1465 &self.config.available_artifacts,
1466 ) {
1467 Ok(_) => {
1470 outcome.result.output = Error::from(MAGIC_ASSUME).abi_encode().into();
1471 }
1472 Err(error) => {
1475 trace!(expected=?assume_no_revert, ?error, status=?outcome.result.result, "Expected revert mismatch");
1476 outcome.result.result = InstructionResult::Revert;
1477 outcome.result.output = error.abi_encode().into();
1478 }
1479 }
1480 } else {
1481 self.assume_no_revert = None;
1483 }
1484 }
1485 }
1486
1487 if let Some(expected_revert) = &mut self.expected_revert {
1489 if outcome.result.is_revert() {
1492 if expected_revert.reverter.is_some() &&
1496 (expected_revert.reverted_by.is_none() || expected_revert.count > 1)
1497 {
1498 expected_revert.reverted_by = Some(call.target_address);
1499 }
1500 }
1501
1502 let curr_depth = ecx.journaled_state.depth();
1503 if curr_depth <= expected_revert.depth {
1504 let needs_processing = match expected_revert.kind {
1505 ExpectedRevertKind::Default => !cheatcode_call,
1506 ExpectedRevertKind::Cheatcode { pending_processing } => {
1509 cheatcode_call && !pending_processing
1510 }
1511 };
1512
1513 if needs_processing {
1514 let mut expected_revert = std::mem::take(&mut self.expected_revert).unwrap();
1515 return match revert_handlers::handle_expect_revert(
1516 cheatcode_call,
1517 false,
1518 self.config.internal_expect_revert,
1519 &expected_revert,
1520 outcome.result.result,
1521 outcome.result.output.clone(),
1522 &self.config.available_artifacts,
1523 ) {
1524 Err(error) => {
1525 trace!(expected=?expected_revert, ?error, status=?outcome.result.result, "Expected revert mismatch");
1526 outcome.result.result = InstructionResult::Revert;
1527 outcome.result.output = error.abi_encode().into();
1528 }
1529 Ok((_, retdata)) => {
1530 expected_revert.actual_count += 1;
1531 if expected_revert.actual_count < expected_revert.count {
1532 self.expected_revert = Some(expected_revert.clone());
1533 }
1534 outcome.result.result = InstructionResult::Return;
1535 outcome.result.output = retdata;
1536 }
1537 };
1538 }
1539
1540 if let ExpectedRevertKind::Cheatcode { pending_processing } =
1543 &mut self.expected_revert.as_mut().unwrap().kind
1544 {
1545 *pending_processing = false;
1546 }
1547 }
1548 }
1549
1550 if cheatcode_call {
1553 return;
1554 }
1555
1556 let gas = outcome.result.gas;
1559 self.gas_metering.last_call_gas = Some(crate::Vm::Gas {
1560 gasLimit: gas.limit(),
1561 gasTotalUsed: gas.spent(),
1562 gasMemoryUsed: 0,
1563 gasRefunded: gas.refunded(),
1564 gasRemaining: gas.remaining(),
1565 });
1566
1567 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1570 if ecx.journaled_state.depth() > 0 {
1572 if let Some(last_recorded_depth) = &mut recorded_account_diffs_stack.pop() {
1573 if outcome.result.is_revert() {
1576 last_recorded_depth.iter_mut().for_each(|element| {
1577 element.reverted = true;
1578 element
1579 .storageAccesses
1580 .iter_mut()
1581 .for_each(|storage_access| storage_access.reverted = true);
1582 })
1583 }
1584
1585 if let Some(call_access) = last_recorded_depth.first_mut() {
1586 let curr_depth = ecx.journaled_state.depth();
1591 if call_access.depth == curr_depth as u64 {
1592 if let Ok(acc) = ecx.journaled_state.load_account(call.target_address) {
1593 debug_assert!(access_is_call(call_access.kind));
1594 call_access.newBalance = acc.info.balance;
1595 }
1596 }
1597 if let Some(last) = recorded_account_diffs_stack.last_mut() {
1602 last.append(last_recorded_depth);
1603 } else {
1604 recorded_account_diffs_stack.push(last_recorded_depth.clone());
1605 }
1606 }
1607 }
1608 }
1609 }
1610
1611 let should_check_emits = self
1623 .expected_emits
1624 .iter()
1625 .any(|(expected, _)| {
1626 let curr_depth = ecx.journaled_state.depth();
1627 expected.depth == curr_depth
1628 }) &&
1629 !call.is_static;
1631 if should_check_emits {
1632 let expected_counts = self
1633 .expected_emits
1634 .iter()
1635 .filter_map(|(expected, count_map)| {
1636 let count = match expected.address {
1637 Some(emitter) => match count_map.get(&emitter) {
1638 Some(log_count) => expected
1639 .log
1640 .as_ref()
1641 .map(|l| log_count.count(l))
1642 .unwrap_or_else(|| log_count.count_unchecked()),
1643 None => 0,
1644 },
1645 None => match &expected.log {
1646 Some(log) => count_map.values().map(|logs| logs.count(log)).sum(),
1647 None => count_map.values().map(|logs| logs.count_unchecked()).sum(),
1648 },
1649 };
1650
1651 if count != expected.count {
1652 Some((expected, count))
1653 } else {
1654 None
1655 }
1656 })
1657 .collect::<Vec<_>>();
1658
1659 if self.expected_emits.iter().any(|(expected, _)| !expected.found && expected.count > 0)
1661 {
1662 outcome.result.result = InstructionResult::Revert;
1663 outcome.result.output = "log != expected log".abi_encode().into();
1664 return;
1665 }
1666
1667 if !expected_counts.is_empty() {
1668 let msg = if outcome.result.is_ok() {
1669 let (expected, count) = expected_counts.first().unwrap();
1670 format!("log emitted {count} times, expected {}", expected.count)
1671 } else {
1672 "expected an emit, but the call reverted instead. \
1673 ensure you're testing the happy path when using `expectEmit`"
1674 .to_string()
1675 };
1676
1677 outcome.result.result = InstructionResult::Revert;
1678 outcome.result.output = Error::encode(msg);
1679 return;
1680 }
1681
1682 self.expected_emits.clear()
1686 }
1687
1688 let diag = self.fork_revert_diagnostic.take();
1691
1692 if outcome.result.is_revert() {
1695 if let Some(err) = diag {
1696 outcome.result.output = Error::encode(err.to_error_msg(&self.labels));
1697 return;
1698 }
1699 }
1700
1701 if let TxKind::Call(test_contract) = ecx.tx.kind {
1704 if ecx.journaled_state.db().is_forked_mode() &&
1707 outcome.result.result == InstructionResult::Stop &&
1708 call.target_address != test_contract
1709 {
1710 let journaled_state = ecx.journaled_state.clone();
1711 self.fork_revert_diagnostic =
1712 ecx.journaled_state.db().diagnose_revert(call.target_address, &journaled_state);
1713 }
1714 }
1715
1716 if ecx.journaled_state.depth() == 0 {
1718 if outcome.result.is_revert() {
1722 return;
1723 }
1724
1725 for (address, calldatas) in &self.expected_calls {
1730 for (calldata, (expected, actual_count)) in calldatas {
1732 let ExpectedCallData { gas, min_gas, value, count, call_type } = expected;
1734
1735 let failed = match call_type {
1736 ExpectedCallType::Count => *count != *actual_count,
1740 ExpectedCallType::NonCount => *count > *actual_count,
1745 };
1746 if failed {
1747 let expected_values = [
1748 Some(format!("data {}", hex::encode_prefixed(calldata))),
1749 value.as_ref().map(|v| format!("value {v}")),
1750 gas.map(|g| format!("gas {g}")),
1751 min_gas.map(|g| format!("minimum gas {g}")),
1752 ]
1753 .into_iter()
1754 .flatten()
1755 .join(", ");
1756 let but = if outcome.result.is_ok() {
1757 let s = if *actual_count == 1 { "" } else { "s" };
1758 format!("was called {actual_count} time{s}")
1759 } else {
1760 "the call reverted instead; \
1761 ensure you're testing the happy path when using `expectCall`"
1762 .to_string()
1763 };
1764 let s = if *count == 1 { "" } else { "s" };
1765 let msg = format!(
1766 "expected call to {address} with {expected_values} \
1767 to be called {count} time{s}, but {but}"
1768 );
1769 outcome.result.result = InstructionResult::Revert;
1770 outcome.result.output = Error::encode(msg);
1771
1772 return;
1773 }
1774 }
1775 }
1776
1777 self.expected_emits.retain(|(expected, _)| expected.count > 0 && !expected.found);
1780 if !self.expected_emits.is_empty() {
1782 let msg = if outcome.result.is_ok() {
1783 "expected an emit, but no logs were emitted afterwards. \
1784 you might have mismatched events or not enough events were emitted"
1785 } else {
1786 "expected an emit, but the call reverted instead. \
1787 ensure you're testing the happy path when using `expectEmit`"
1788 };
1789 outcome.result.result = InstructionResult::Revert;
1790 outcome.result.output = Error::encode(msg);
1791 return;
1792 }
1793
1794 if let Some(expected_create) = self.expected_creates.first() {
1796 let msg = format!(
1797 "expected {} call by address {} for bytecode {} but not found",
1798 expected_create.create_scheme,
1799 hex::encode_prefixed(expected_create.deployer),
1800 hex::encode_prefixed(&expected_create.bytecode),
1801 );
1802 outcome.result.result = InstructionResult::Revert;
1803 outcome.result.output = Error::encode(msg);
1804 }
1805 }
1806 }
1807
1808 fn create(&mut self, ecx: Ecx, call: &mut CreateInputs) -> Option<CreateOutcome> {
1809 self.create_common(ecx, call)
1810 }
1811
1812 fn create_end(&mut self, ecx: Ecx, call: &CreateInputs, outcome: &mut CreateOutcome) {
1813 self.create_end_common(ecx, Some(call), outcome)
1814 }
1815}
1816
1817impl InspectorExt for Cheatcodes {
1818 fn should_use_create2_factory(&mut self, ecx: Ecx, inputs: &CreateInputs) -> bool {
1819 if let CreateScheme::Create2 { .. } = inputs.scheme {
1820 let depth = ecx.journaled_state.depth();
1821 let target_depth = if let Some(prank) = &self.get_prank(depth) {
1822 prank.depth
1823 } else if let Some(broadcast) = &self.broadcast {
1824 broadcast.depth
1825 } else {
1826 1
1827 };
1828
1829 depth == target_depth &&
1830 (self.broadcast.is_some() || self.config.always_use_create_2_factory)
1831 } else {
1832 false
1833 }
1834 }
1835
1836 fn create2_deployer(&self) -> Address {
1837 self.config.evm_opts.create2_deployer
1838 }
1839}
1840
1841impl Cheatcodes {
1842 #[cold]
1843 fn meter_gas(&mut self, interpreter: &mut Interpreter) {
1844 if let Some(paused_gas) = self.gas_metering.paused_frames.last() {
1845 let memory = *interpreter.control.gas.memory();
1848 interpreter.control.gas = *paused_gas;
1849 interpreter.control.gas.memory_mut().words_num = memory.words_num;
1850 interpreter.control.gas.memory_mut().expansion_cost = memory.expansion_cost;
1851 } else {
1852 self.gas_metering.paused_frames.push(interpreter.control.gas);
1854 }
1855 }
1856
1857 #[cold]
1858 fn meter_gas_record(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1859 if matches!(interpreter.control.instruction_result, InstructionResult::Continue) {
1860 self.gas_metering.gas_records.iter_mut().for_each(|record| {
1861 let curr_depth = ecx.journaled_state.depth();
1862 if curr_depth == record.depth {
1863 if self.gas_metering.last_gas_used != 0 {
1866 let gas_diff = interpreter
1867 .control
1868 .gas
1869 .spent()
1870 .saturating_sub(self.gas_metering.last_gas_used);
1871 record.gas_used = record.gas_used.saturating_add(gas_diff);
1872 }
1873
1874 self.gas_metering.last_gas_used = interpreter.control.gas.spent();
1877 }
1878 });
1879 }
1880 }
1881
1882 #[cold]
1883 fn meter_gas_end(&mut self, interpreter: &mut Interpreter) {
1884 if will_exit(interpreter.control.instruction_result) {
1886 self.gas_metering.paused_frames.pop();
1887 }
1888 }
1889
1890 #[cold]
1891 fn meter_gas_reset(&mut self, interpreter: &mut Interpreter) {
1892 interpreter.control.gas = Gas::new(interpreter.control.gas.limit());
1893 self.gas_metering.reset = false;
1894 }
1895
1896 #[cold]
1897 fn meter_gas_check(&mut self, interpreter: &mut Interpreter) {
1898 if will_exit(interpreter.control.instruction_result) {
1899 if interpreter.control.gas.spent() <
1903 u64::try_from(interpreter.control.gas.refunded()).unwrap_or_default()
1904 {
1905 interpreter.control.gas = Gas::new(interpreter.control.gas.limit());
1906 }
1907 }
1908 }
1909
1910 #[cold]
1918 fn arbitrary_storage_end(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1919 let (key, target_address) = if interpreter.bytecode.opcode() == op::SLOAD {
1920 (try_or_return!(interpreter.stack.peek(0)), interpreter.input.target_address)
1921 } else {
1922 return
1923 };
1924
1925 let Some(value) = ecx.sload(target_address, key) else {
1926 return;
1927 };
1928
1929 if (value.is_cold && value.data.is_zero()) ||
1930 self.should_overwrite_arbitrary_storage(&target_address, key)
1931 {
1932 if self.has_arbitrary_storage(&target_address) {
1933 let arbitrary_value = self.rng().random();
1934 self.arbitrary_storage.as_mut().unwrap().save(
1935 ecx,
1936 target_address,
1937 key,
1938 arbitrary_value,
1939 );
1940 } else if self.is_arbitrary_storage_copy(&target_address) {
1941 let arbitrary_value = self.rng().random();
1942 self.arbitrary_storage.as_mut().unwrap().copy(
1943 ecx,
1944 target_address,
1945 key,
1946 arbitrary_value,
1947 );
1948 }
1949 }
1950 }
1951
1952 #[cold]
1954 fn record_accesses(&mut self, interpreter: &mut Interpreter) {
1955 let access = &mut self.accesses;
1956 match interpreter.bytecode.opcode() {
1957 op::SLOAD => {
1958 let key = try_or_return!(interpreter.stack.peek(0));
1959 access.record_read(interpreter.input.target_address, key);
1960 }
1961 op::SSTORE => {
1962 let key = try_or_return!(interpreter.stack.peek(0));
1963 access.record_write(interpreter.input.target_address, key);
1964 }
1965 _ => {}
1966 }
1967 }
1968
1969 #[cold]
1970 fn record_state_diffs(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1971 let Some(account_accesses) = &mut self.recorded_account_diffs_stack else { return };
1972 match interpreter.bytecode.opcode() {
1973 op::SELFDESTRUCT => {
1974 let Some(last) = account_accesses.last_mut() else { return };
1976
1977 let target = try_or_return!(interpreter.stack.peek(0));
1979 let target = Address::from_word(B256::from(target));
1980 let (initialized, old_balance) = ecx
1981 .journaled_state
1982 .load_account(target)
1983 .map(|account| (account.info.exists(), account.info.balance))
1984 .unwrap_or_default();
1985
1986 let value = ecx
1988 .balance(interpreter.input.target_address)
1989 .map(|b| b.data)
1990 .unwrap_or(U256::ZERO);
1991
1992 last.push(crate::Vm::AccountAccess {
1994 chainInfo: crate::Vm::ChainInfo {
1995 forkId: ecx.journaled_state.database.active_fork_id().unwrap_or_default(),
1996 chainId: U256::from(ecx.cfg.chain_id),
1997 },
1998 accessor: interpreter.input.target_address,
1999 account: target,
2000 kind: crate::Vm::AccountAccessKind::SelfDestruct,
2001 initialized,
2002 oldBalance: old_balance,
2003 newBalance: old_balance + value,
2004 value,
2005 data: Bytes::new(),
2006 reverted: false,
2007 deployedCode: Bytes::new(),
2008 storageAccesses: vec![],
2009 depth: ecx
2010 .journaled_state
2011 .depth()
2012 .try_into()
2013 .expect("journaled state depth exceeds u64"),
2014 });
2015 }
2016
2017 op::SLOAD => {
2018 let Some(last) = account_accesses.last_mut() else { return };
2019
2020 let key = try_or_return!(interpreter.stack.peek(0));
2021 let address = interpreter.input.target_address;
2022
2023 let mut present_value = U256::ZERO;
2026 if ecx.journaled_state.load_account(address).is_ok() {
2028 if let Some(previous) = ecx.sload(address, key) {
2029 present_value = previous.data;
2030 }
2031 }
2032 let access = crate::Vm::StorageAccess {
2033 account: interpreter.input.target_address,
2034 slot: key.into(),
2035 isWrite: false,
2036 previousValue: present_value.into(),
2037 newValue: present_value.into(),
2038 reverted: false,
2039 };
2040 let curr_depth = ecx
2041 .journaled_state
2042 .depth()
2043 .try_into()
2044 .expect("journaled state depth exceeds u64");
2045 append_storage_access(last, access, curr_depth);
2046 }
2047 op::SSTORE => {
2048 let Some(last) = account_accesses.last_mut() else { return };
2049
2050 let key = try_or_return!(interpreter.stack.peek(0));
2051 let value = try_or_return!(interpreter.stack.peek(1));
2052 let address = interpreter.input.target_address;
2053 let mut previous_value = U256::ZERO;
2056 if ecx.journaled_state.load_account(address).is_ok() {
2057 if let Some(previous) = ecx.sload(address, key) {
2058 previous_value = previous.data;
2059 }
2060 }
2061
2062 let access = crate::Vm::StorageAccess {
2063 account: address,
2064 slot: key.into(),
2065 isWrite: true,
2066 previousValue: previous_value.into(),
2067 newValue: value.into(),
2068 reverted: false,
2069 };
2070 let curr_depth = ecx
2071 .journaled_state
2072 .depth()
2073 .try_into()
2074 .expect("journaled state depth exceeds u64");
2075 append_storage_access(last, access, curr_depth);
2076 }
2077
2078 op::EXTCODECOPY | op::EXTCODESIZE | op::EXTCODEHASH | op::BALANCE => {
2080 let kind = match interpreter.bytecode.opcode() {
2081 op::EXTCODECOPY => crate::Vm::AccountAccessKind::Extcodecopy,
2082 op::EXTCODESIZE => crate::Vm::AccountAccessKind::Extcodesize,
2083 op::EXTCODEHASH => crate::Vm::AccountAccessKind::Extcodehash,
2084 op::BALANCE => crate::Vm::AccountAccessKind::Balance,
2085 _ => unreachable!(),
2086 };
2087 let address =
2088 Address::from_word(B256::from(try_or_return!(interpreter.stack.peek(0))));
2089 let initialized;
2090 let balance;
2091 if let Ok(acc) = ecx.journaled_state.load_account(address) {
2092 initialized = acc.info.exists();
2093 balance = acc.info.balance;
2094 } else {
2095 initialized = false;
2096 balance = U256::ZERO;
2097 }
2098 let curr_depth = ecx
2099 .journaled_state
2100 .depth()
2101 .try_into()
2102 .expect("journaled state depth exceeds u64");
2103 let account_access = crate::Vm::AccountAccess {
2104 chainInfo: crate::Vm::ChainInfo {
2105 forkId: ecx.journaled_state.database.active_fork_id().unwrap_or_default(),
2106 chainId: U256::from(ecx.cfg.chain_id),
2107 },
2108 accessor: interpreter.input.target_address,
2109 account: address,
2110 kind,
2111 initialized,
2112 oldBalance: balance,
2113 newBalance: balance,
2114 value: U256::ZERO,
2115 data: Bytes::new(),
2116 reverted: false,
2117 deployedCode: Bytes::new(),
2118 storageAccesses: vec![],
2119 depth: curr_depth,
2120 };
2121 if let Some(last) = account_accesses.last_mut() {
2124 last.push(account_access);
2125 } else {
2126 account_accesses.push(vec![account_access]);
2127 }
2128 }
2129 _ => {}
2130 }
2131 }
2132
2133 #[cold]
2138 fn check_mem_opcodes(&self, interpreter: &mut Interpreter, depth: u64) {
2139 let Some(ranges) = self.allowed_mem_writes.get(&depth) else {
2140 return;
2141 };
2142
2143 macro_rules! mem_opcode_match {
2152 ($(($opcode:ident, $offset_depth:expr, $size_depth:expr, $writes:expr)),* $(,)?) => {
2153 match interpreter.bytecode.opcode() {
2154 op::MSTORE => {
2159 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2161
2162 if !ranges.iter().any(|range| {
2165 range.contains(&offset) && range.contains(&(offset + 31))
2166 }) {
2167 let value = try_or_return!(interpreter.stack.peek(1)).to_be_bytes::<32>();
2172 if value[..SELECTOR_LEN] == stopExpectSafeMemoryCall::SELECTOR {
2173 return
2174 }
2175
2176 disallowed_mem_write(offset, 32, interpreter, ranges);
2177 return
2178 }
2179 }
2180 op::MSTORE8 => {
2181 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2183
2184 if !ranges.iter().any(|range| range.contains(&offset)) {
2187 disallowed_mem_write(offset, 1, interpreter, ranges);
2188 return
2189 }
2190 }
2191
2192 op::MLOAD => {
2197 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2199
2200 if offset >= interpreter.memory.size() as u64 && !ranges.iter().any(|range| {
2204 range.contains(&offset) && range.contains(&(offset + 31))
2205 }) {
2206 disallowed_mem_write(offset, 32, interpreter, ranges);
2207 return
2208 }
2209 }
2210
2211 op::CALL => {
2216 let dest_offset = try_or_return!(interpreter.stack.peek(5)).saturating_to::<u64>();
2218
2219 let size = try_or_return!(interpreter.stack.peek(6)).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 });
2229
2230 if fail_cond {
2233 let to = Address::from_word(try_or_return!(interpreter.stack.peek(1)).to_be_bytes::<32>().into());
2237 if to == CHEATCODE_ADDRESS {
2238 let args_offset = try_or_return!(interpreter.stack.peek(3)).saturating_to::<usize>();
2239 let args_size = try_or_return!(interpreter.stack.peek(4)).saturating_to::<usize>();
2240 let memory_word = interpreter.memory.slice_len(args_offset, args_size);
2241 if memory_word[..SELECTOR_LEN] == stopExpectSafeMemoryCall::SELECTOR {
2242 return
2243 }
2244 }
2245
2246 disallowed_mem_write(dest_offset, size, interpreter, ranges);
2247 return
2248 }
2249 }
2250
2251 $(op::$opcode => {
2252 let dest_offset = try_or_return!(interpreter.stack.peek($offset_depth)).saturating_to::<u64>();
2254
2255 let size = try_or_return!(interpreter.stack.peek($size_depth)).saturating_to::<u64>();
2257
2258 let fail_cond = !ranges.iter().any(|range| {
2262 range.contains(&dest_offset) &&
2263 range.contains(&(dest_offset + size.saturating_sub(1)))
2264 }) && ($writes ||
2265 [dest_offset, (dest_offset + size).saturating_sub(1)].into_iter().any(|offset| {
2266 offset >= interpreter.memory.size() as u64
2267 })
2268 );
2269
2270 if fail_cond {
2273 disallowed_mem_write(dest_offset, size, interpreter, ranges);
2274 return
2275 }
2276 })*
2277
2278 _ => {}
2279 }
2280 }
2281 }
2282
2283 mem_opcode_match!(
2286 (CALLDATACOPY, 0, 2, true),
2287 (CODECOPY, 0, 2, true),
2288 (RETURNDATACOPY, 0, 2, true),
2289 (EXTCODECOPY, 1, 3, true),
2290 (CALLCODE, 5, 6, true),
2291 (STATICCALL, 4, 5, true),
2292 (DELEGATECALL, 4, 5, true),
2293 (KECCAK256, 0, 1, false),
2294 (LOG0, 0, 1, false),
2295 (LOG1, 0, 1, false),
2296 (LOG2, 0, 1, false),
2297 (LOG3, 0, 1, false),
2298 (LOG4, 0, 1, false),
2299 (CREATE, 1, 2, false),
2300 (CREATE2, 1, 2, false),
2301 (RETURN, 0, 1, false),
2302 (REVERT, 0, 1, false),
2303 );
2304 }
2305}
2306
2307fn disallowed_mem_write(
2313 dest_offset: u64,
2314 size: u64,
2315 interpreter: &mut Interpreter,
2316 ranges: &[Range<u64>],
2317) {
2318 let revert_string = format!(
2319 "memory write at offset 0x{:02X} of size 0x{:02X} not allowed; safe range: {}",
2320 dest_offset,
2321 size,
2322 ranges.iter().map(|r| format!("(0x{:02X}, 0x{:02X}]", r.start, r.end)).join(" U ")
2323 );
2324
2325 interpreter.control.instruction_result = InstructionResult::Revert;
2326 interpreter.control.next_action = InterpreterAction::Return {
2327 result: InterpreterResult {
2328 output: Error::encode(revert_string),
2329 gas: interpreter.control.gas,
2330 result: InstructionResult::Revert,
2331 },
2332 };
2333}
2334
2335fn check_if_fixed_gas_limit(ecx: &Ecx, call_gas_limit: u64) -> bool {
2338 ecx.tx.gas_limit > ecx.block.gas_limit &&
2343 call_gas_limit <= ecx.block.gas_limit
2344 && call_gas_limit > 2300
2347}
2348
2349fn access_is_call(kind: crate::Vm::AccountAccessKind) -> bool {
2351 matches!(
2352 kind,
2353 crate::Vm::AccountAccessKind::Call |
2354 crate::Vm::AccountAccessKind::StaticCall |
2355 crate::Vm::AccountAccessKind::CallCode |
2356 crate::Vm::AccountAccessKind::DelegateCall
2357 )
2358}
2359
2360fn append_storage_access(
2362 last: &mut Vec<AccountAccess>,
2363 storage_access: crate::Vm::StorageAccess,
2364 storage_depth: u64,
2365) {
2366 if !last.is_empty() && last.first().unwrap().depth < storage_depth {
2368 if last.len() == 1 {
2374 last.first_mut().unwrap().storageAccesses.push(storage_access);
2375 } else {
2376 let last_record = last.last_mut().unwrap();
2377 if last_record.kind as u8 == crate::Vm::AccountAccessKind::Resume as u8 {
2378 last_record.storageAccesses.push(storage_access);
2379 } else {
2380 let entry = last.first().unwrap();
2381 let resume_record = crate::Vm::AccountAccess {
2382 chainInfo: crate::Vm::ChainInfo {
2383 forkId: entry.chainInfo.forkId,
2384 chainId: entry.chainInfo.chainId,
2385 },
2386 accessor: entry.accessor,
2387 account: entry.account,
2388 kind: crate::Vm::AccountAccessKind::Resume,
2389 initialized: entry.initialized,
2390 storageAccesses: vec![storage_access],
2391 reverted: entry.reverted,
2392 oldBalance: U256::ZERO,
2394 newBalance: U256::ZERO,
2395 value: U256::ZERO,
2396 data: Bytes::new(),
2397 deployedCode: Bytes::new(),
2398 depth: entry.depth,
2399 };
2400 last.push(resume_record);
2401 }
2402 }
2403 }
2404}
2405
2406fn apply_dispatch(
2408 calls: &Vm::VmCalls,
2409 ccx: &mut CheatsCtxt,
2410 executor: &mut dyn CheatcodesExecutor,
2411) -> Result {
2412 let cheat = calls_as_dyn_cheatcode(calls);
2413
2414 let _guard = debug_span!(target: "cheatcodes", "apply", id = %cheat.id()).entered();
2415 trace!(target: "cheatcodes", cheat = ?cheat.as_debug(), "applying");
2416
2417 if let spec::Status::Deprecated(replacement) = *cheat.status() {
2418 ccx.state.deprecated.insert(cheat.signature(), replacement);
2419 }
2420
2421 let mut result = cheat.dyn_apply(ccx, executor);
2423
2424 if let Err(e) = &mut result {
2426 if e.is_str() {
2427 let name = cheat.name();
2428 if !name.contains("assert") && name != "rpcUrl" {
2432 *e = fmt_err!("vm.{name}: {e}");
2433 }
2434 }
2435 }
2436
2437 trace!(
2438 target: "cheatcodes",
2439 return = %match &result {
2440 Ok(b) => hex::encode(b),
2441 Err(e) => e.to_string(),
2442 }
2443 );
2444
2445 result
2446}
2447
2448fn calls_as_dyn_cheatcode(calls: &Vm::VmCalls) -> &dyn DynCheatcode {
2449 macro_rules! as_dyn {
2450 ($($variant:ident),*) => {
2451 match calls {
2452 $(Vm::VmCalls::$variant(cheat) => cheat,)*
2453 }
2454 };
2455 }
2456 vm_calls!(as_dyn)
2457}
2458
2459fn will_exit(ir: InstructionResult) -> bool {
2461 !matches!(ir, InstructionResult::Continue | InstructionResult::CallOrCreate)
2462}