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, SeedableRng};
50use rand_chacha::ChaChaRng;
51use revm::{
52 bytecode::opcode as op,
53 context::{result::EVMError, BlockEnv, JournalTr, LocalContext, TransactionType},
54 context_interface::{transaction::SignedAuthorization, CreateScheme},
55 handler::FrameResult,
56 interpreter::{
57 interpreter_types::{Jumps, MemoryTr},
58 CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, FrameInput, Gas, Host,
59 InstructionResult, Interpreter, InterpreterAction, InterpreterResult,
60 },
61 state::EvmStorageSlot,
62 Inspector, Journal,
63};
64use serde_json::Value;
65use std::{
66 cmp::max,
67 collections::{BTreeMap, VecDeque},
68 fs::File,
69 io::BufReader,
70 ops::Range,
71 path::PathBuf,
72 sync::Arc,
73};
74
75mod utils;
76
77pub type Ecx<'a, 'b, 'c> = &'a mut EthEvmContext<&'b mut (dyn DatabaseExt + 'c)>;
78
79pub trait CheatcodesExecutor {
85 fn get_inspector<'a>(&'a mut self, cheats: &'a mut Cheatcodes) -> Box<dyn InspectorExt + 'a>;
88
89 fn exec_create(
91 &mut self,
92 inputs: CreateInputs,
93 ccx: &mut CheatsCtxt,
94 ) -> Result<CreateOutcome, EVMError<DatabaseError>> {
95 with_evm(self, ccx, |evm| {
96 evm.inner.ctx.journaled_state.depth += 1;
97
98 let frame = FrameInput::Create(Box::new(inputs));
99
100 let outcome = match evm.run_execution(frame)? {
101 FrameResult::Call(_) | FrameResult::EOFCreate(_) => unreachable!(),
102 FrameResult::Create(create) => create,
103 };
104
105 evm.inner.ctx.journaled_state.depth -= 1;
106
107 Ok(outcome)
108 })
109 }
110
111 fn console_log(&mut self, ccx: &mut CheatsCtxt, msg: &str) {
112 self.get_inspector(ccx.state).console_log(msg);
113 }
114
115 fn tracing_inspector(&mut self) -> Option<&mut Option<TracingInspector>> {
117 None
118 }
119}
120
121fn with_evm<E, F, O>(
123 executor: &mut E,
124 ccx: &mut CheatsCtxt,
125 f: F,
126) -> Result<O, EVMError<DatabaseError>>
127where
128 E: CheatcodesExecutor + ?Sized,
129 F: for<'a, 'b> FnOnce(
130 &mut FoundryEvm<'a, &'b mut dyn InspectorExt>,
131 ) -> Result<O, EVMError<DatabaseError>>,
132{
133 let mut inspector = executor.get_inspector(ccx.state);
134 let error = std::mem::replace(&mut ccx.ecx.error, Ok(()));
135
136 let ctx = EthEvmContext {
137 block: ccx.ecx.block.clone(),
138 cfg: ccx.ecx.cfg.clone(),
139 tx: ccx.ecx.tx.clone(),
140 journaled_state: Journal {
141 inner: ccx.ecx.journaled_state.inner.clone(),
142 database: &mut *ccx.ecx.journaled_state.database as &mut dyn DatabaseExt,
143 },
144 local: LocalContext::default(),
145 chain: (),
146 error,
147 };
148
149 let mut evm = new_evm_with_existing_context(ctx, &mut *inspector);
150
151 let res = f(&mut evm)?;
152
153 ccx.ecx.journaled_state.inner = evm.inner.ctx.journaled_state.inner;
154 ccx.ecx.block = evm.inner.ctx.block;
155 ccx.ecx.tx = evm.inner.ctx.tx;
156 ccx.ecx.cfg = evm.inner.ctx.cfg;
157 ccx.ecx.error = evm.inner.ctx.error;
158
159 Ok(res)
160}
161
162#[derive(Debug, Default, Clone, Copy)]
165struct TransparentCheatcodesExecutor;
166
167impl CheatcodesExecutor for TransparentCheatcodesExecutor {
168 fn get_inspector<'a>(&'a mut self, cheats: &'a mut Cheatcodes) -> Box<dyn InspectorExt + 'a> {
169 Box::new(cheats)
170 }
171}
172
173macro_rules! try_or_return {
174 ($e:expr) => {
175 match $e {
176 Ok(v) => v,
177 Err(_) => return,
178 }
179 };
180}
181
182#[derive(Debug, Default)]
184pub struct TestContext {
185 pub opened_read_files: HashMap<PathBuf, BufReader<File>>,
187}
188
189impl Clone for TestContext {
191 fn clone(&self) -> Self {
192 Default::default()
193 }
194}
195
196impl TestContext {
197 #[inline]
199 pub fn clear(&mut self) {
200 self.opened_read_files.clear();
201 }
202}
203
204#[derive(Clone, Debug)]
206pub struct BroadcastableTransaction {
207 pub rpc: Option<String>,
209 pub transaction: TransactionMaybeSigned,
211}
212
213#[derive(Clone, Debug, Copy)]
214pub struct RecordDebugStepInfo {
215 pub start_node_idx: usize,
217 pub original_tracer_config: TracingInspectorConfig,
219}
220
221#[derive(Clone, Debug, Default)]
223pub struct GasMetering {
224 pub paused: bool,
226 pub touched: bool,
229 pub reset: bool,
231 pub paused_frames: Vec<Gas>,
233
234 pub active_gas_snapshot: Option<(String, String)>,
236
237 pub last_call_gas: Option<crate::Vm::Gas>,
240
241 pub recording: bool,
243 pub last_gas_used: u64,
245 pub gas_records: Vec<GasRecord>,
247}
248
249impl GasMetering {
250 pub fn start(&mut self) {
252 self.recording = true;
253 }
254
255 pub fn stop(&mut self) {
257 self.recording = false;
258 }
259
260 pub fn resume(&mut self) {
262 if self.paused {
263 self.paused = false;
264 self.touched = true;
265 }
266 self.paused_frames.clear();
267 }
268
269 pub fn reset(&mut self) {
271 self.paused = false;
272 self.touched = true;
273 self.reset = true;
274 self.paused_frames.clear();
275 }
276}
277
278#[derive(Clone, Debug, Default)]
280pub struct ArbitraryStorage {
281 pub values: HashMap<Address, HashMap<U256, U256>>,
285 pub copies: HashMap<Address, Address>,
287 pub overwrites: HashSet<Address>,
289}
290
291impl ArbitraryStorage {
292 pub fn mark_arbitrary(&mut self, address: &Address, overwrite: bool) {
294 self.values.insert(*address, HashMap::default());
295 if overwrite {
296 self.overwrites.insert(*address);
297 } else {
298 self.overwrites.remove(address);
299 }
300 }
301
302 pub fn mark_copy(&mut self, from: &Address, to: &Address) {
304 if self.values.contains_key(from) {
305 self.copies.insert(*to, *from);
306 }
307 }
308
309 pub fn save(&mut self, ecx: Ecx, address: Address, slot: U256, data: U256) {
313 self.values.get_mut(&address).expect("missing arbitrary address entry").insert(slot, data);
314 if let Ok(mut account) = ecx.journaled_state.load_account(address) {
315 account.storage.insert(slot, EvmStorageSlot::new(data));
316 }
317 }
318
319 pub fn copy(&mut self, ecx: Ecx, target: Address, slot: U256, new_value: U256) -> U256 {
325 let source = self.copies.get(&target).expect("missing arbitrary copy target entry");
326 let storage_cache = self.values.get_mut(source).expect("missing arbitrary source storage");
327 let value = match storage_cache.get(&slot) {
328 Some(value) => *value,
329 None => {
330 storage_cache.insert(slot, new_value);
331 if let Ok(mut source_account) = ecx.journaled_state.load_account(*source) {
333 source_account.storage.insert(slot, EvmStorageSlot::new(new_value));
334 }
335 new_value
336 }
337 };
338 if let Ok(mut target_account) = ecx.journaled_state.load_account(target) {
340 target_account.storage.insert(slot, EvmStorageSlot::new(value));
341 }
342 value
343 }
344}
345
346pub type BroadcastableTransactions = VecDeque<BroadcastableTransaction>;
348
349#[derive(Clone, Debug)]
367pub struct Cheatcodes {
368 pub block: Option<BlockEnv>,
373
374 pub active_delegation: Option<SignedAuthorization>,
378
379 pub active_blob_sidecar: Option<BlobTransactionSidecar>,
381
382 pub gas_price: Option<u128>,
387
388 pub labels: AddressHashMap<String>,
390
391 pub pranks: BTreeMap<usize, Prank>,
393
394 pub expected_revert: Option<ExpectedRevert>,
396
397 pub assume_no_revert: Option<AssumeNoRevert>,
399
400 pub fork_revert_diagnostic: Option<RevertDiagnostic>,
402
403 pub accesses: RecordAccess,
405
406 pub recording_accesses: bool,
408
409 pub recorded_account_diffs_stack: Option<Vec<Vec<AccountAccess>>>,
415
416 pub record_debug_steps_info: Option<RecordDebugStepInfo>,
418
419 pub recorded_logs: Option<Vec<crate::Vm::Log>>,
421
422 pub mocked_calls: HashMap<Address, BTreeMap<MockCallDataContext, VecDeque<MockCallReturnData>>>,
425
426 pub mocked_functions: HashMap<Address, HashMap<Bytes, Address>>,
428
429 pub expected_calls: ExpectedCallTracker,
431 pub expected_emits: ExpectedEmitTracker,
433 pub expected_creates: Vec<ExpectedCreate>,
435
436 pub allowed_mem_writes: HashMap<u64, Vec<Range<u64>>>,
438
439 pub broadcast: Option<Broadcast>,
441
442 pub broadcastable_transactions: BroadcastableTransactions,
444
445 pub access_list: Option<AccessList>,
447
448 pub config: Arc<CheatsConfig>,
450
451 pub test_context: TestContext,
453
454 pub fs_commit: bool,
457
458 pub serialized_jsons: BTreeMap<String, BTreeMap<String, Value>>,
461
462 pub eth_deals: Vec<DealRecord>,
464
465 pub gas_metering: GasMetering,
467
468 pub gas_snapshots: BTreeMap<String, BTreeMap<String, String>>,
471
472 pub mapping_slots: Option<AddressHashMap<MappingSlots>>,
474
475 pub pc: usize,
477 pub breakpoints: Breakpoints,
480
481 pub intercept_next_create_call: bool,
483
484 test_runner: Option<TestRunner>,
487
488 rng: Option<ChaChaRng>,
490
491 pub ignored_traces: IgnoredTraces,
493
494 pub arbitrary_storage: Option<ArbitraryStorage>,
496
497 pub deprecated: HashMap<&'static str, Option<&'static str>>,
499 pub wallets: Option<Wallets>,
501}
502
503impl Default for Cheatcodes {
507 fn default() -> Self {
508 Self::new(Arc::default())
509 }
510}
511
512impl Cheatcodes {
513 pub fn new(config: Arc<CheatsConfig>) -> Self {
515 Self {
516 fs_commit: true,
517 labels: config.labels.clone(),
518 config,
519 block: Default::default(),
520 active_delegation: Default::default(),
521 active_blob_sidecar: Default::default(),
522 gas_price: Default::default(),
523 pranks: Default::default(),
524 expected_revert: Default::default(),
525 assume_no_revert: Default::default(),
526 fork_revert_diagnostic: Default::default(),
527 accesses: Default::default(),
528 recording_accesses: Default::default(),
529 recorded_account_diffs_stack: Default::default(),
530 recorded_logs: Default::default(),
531 record_debug_steps_info: Default::default(),
532 mocked_calls: Default::default(),
533 mocked_functions: Default::default(),
534 expected_calls: Default::default(),
535 expected_emits: Default::default(),
536 expected_creates: Default::default(),
537 allowed_mem_writes: Default::default(),
538 broadcast: Default::default(),
539 broadcastable_transactions: Default::default(),
540 access_list: Default::default(),
541 test_context: Default::default(),
542 serialized_jsons: Default::default(),
543 eth_deals: Default::default(),
544 gas_metering: Default::default(),
545 gas_snapshots: Default::default(),
546 mapping_slots: Default::default(),
547 pc: Default::default(),
548 breakpoints: Default::default(),
549 intercept_next_create_call: Default::default(),
550 test_runner: Default::default(),
551 ignored_traces: Default::default(),
552 arbitrary_storage: Default::default(),
553 deprecated: Default::default(),
554 wallets: Default::default(),
555 rng: Default::default(),
556 }
557 }
558
559 pub fn get_prank(&self, depth: usize) -> Option<&Prank> {
563 self.pranks.range(..=depth).last().map(|(_, prank)| prank)
564 }
565
566 pub fn wallets(&mut self) -> &Wallets {
568 self.wallets.get_or_insert_with(|| Wallets::new(MultiWallet::default(), None))
569 }
570
571 pub fn set_wallets(&mut self, wallets: Wallets) {
573 self.wallets = Some(wallets);
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 match (self.active_delegation.take(), self.active_blob_sidecar.take()) {
1140 (Some(_), Some(_)) => {
1141 let msg = "both delegation and blob are active; `attachBlob` and `attachDelegation` are not compatible";
1142 return Some(CallOutcome {
1143 result: InterpreterResult {
1144 result: InstructionResult::Revert,
1145 output: Error::encode(msg),
1146 gas,
1147 },
1148 memory_offset: call.return_memory_offset.clone(),
1149 });
1150 }
1151 (Some(auth_list), None) => {
1152 tx_req.authorization_list = Some(vec![auth_list]);
1153 tx_req.sidecar = None;
1154
1155 account.info.nonce += 1;
1157 }
1158 (None, Some(blob_sidecar)) => {
1159 tx_req.set_blob_sidecar(blob_sidecar);
1160 tx_req.authorization_list = None;
1161 }
1162 (None, None) => {
1163 tx_req.sidecar = None;
1164 tx_req.authorization_list = None;
1165 }
1166 }
1167
1168 self.broadcastable_transactions.push_back(BroadcastableTransaction {
1169 rpc: ecx.journaled_state.database.active_fork_url(),
1170 transaction: tx_req.into(),
1171 });
1172 debug!(target: "cheatcodes", tx=?self.broadcastable_transactions.back().unwrap(), "broadcastable call");
1173
1174 if !self.config.evm_opts.isolate {
1176 let prev = account.info.nonce;
1177 account.info.nonce += 1;
1178 debug!(target: "cheatcodes", address=%broadcast.new_origin, nonce=prev+1, prev, "incremented nonce");
1179 }
1180 } else if broadcast.single_call {
1181 let msg =
1182 "`staticcall`s are not allowed after `broadcast`; use `startBroadcast` instead";
1183 return Some(CallOutcome {
1184 result: InterpreterResult {
1185 result: InstructionResult::Revert,
1186 output: Error::encode(msg),
1187 gas,
1188 },
1189 memory_offset: call.return_memory_offset.clone(),
1190 });
1191 }
1192 }
1193 }
1194
1195 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1197 let initialized;
1200 let old_balance;
1201 if let Ok(acc) = ecx.journaled_state.load_account(call.target_address) {
1202 initialized = acc.info.exists();
1203 old_balance = acc.info.balance;
1204 } else {
1205 initialized = false;
1206 old_balance = U256::ZERO;
1207 }
1208 let kind = match call.scheme {
1209 CallScheme::Call => crate::Vm::AccountAccessKind::Call,
1210 CallScheme::CallCode => crate::Vm::AccountAccessKind::CallCode,
1211 CallScheme::DelegateCall => crate::Vm::AccountAccessKind::DelegateCall,
1212 CallScheme::StaticCall => crate::Vm::AccountAccessKind::StaticCall,
1213 CallScheme::ExtCall => crate::Vm::AccountAccessKind::Call,
1214 CallScheme::ExtStaticCall => crate::Vm::AccountAccessKind::StaticCall,
1215 CallScheme::ExtDelegateCall => crate::Vm::AccountAccessKind::DelegateCall,
1216 };
1217 recorded_account_diffs_stack.push(vec![AccountAccess {
1223 chainInfo: crate::Vm::ChainInfo {
1224 forkId: ecx.journaled_state.db().active_fork_id().unwrap_or_default(),
1225 chainId: U256::from(ecx.cfg.chain_id),
1226 },
1227 accessor: call.caller,
1228 account: call.bytecode_address,
1229 kind,
1230 initialized,
1231 oldBalance: old_balance,
1232 newBalance: U256::ZERO, value: call.call_value(),
1234 data: call.input.bytes(ecx),
1235 reverted: false,
1236 deployedCode: Bytes::new(),
1237 storageAccesses: vec![], depth: ecx
1239 .journaled_state
1240 .depth()
1241 .try_into()
1242 .expect("journaled state depth exceeds u64"),
1243 }]);
1244 }
1245
1246 None
1247 }
1248
1249 pub fn rng(&mut self) -> &mut impl Rng {
1250 self.rng.get_or_insert_with(|| match self.config.seed {
1253 Some(seed) => ChaChaRng::from_seed(seed.to_be_bytes::<32>()),
1254 None => ChaChaRng::from_os_rng(),
1255 })
1256 }
1257
1258 pub fn test_runner(&mut self) -> &mut TestRunner {
1259 self.test_runner.get_or_insert_with(|| match self.config.seed {
1260 Some(seed) => TestRunner::new_with_rng(
1261 proptest::test_runner::Config::default(),
1262 TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>()),
1263 ),
1264 None => TestRunner::new(proptest::test_runner::Config::default()),
1265 })
1266 }
1267
1268 pub fn arbitrary_storage(&mut self) -> &mut ArbitraryStorage {
1271 self.arbitrary_storage.get_or_insert_with(ArbitraryStorage::default)
1272 }
1273
1274 pub fn has_arbitrary_storage(&self, address: &Address) -> bool {
1276 match &self.arbitrary_storage {
1277 Some(storage) => storage.values.contains_key(address),
1278 None => false,
1279 }
1280 }
1281
1282 pub fn should_overwrite_arbitrary_storage(
1286 &self,
1287 address: &Address,
1288 storage_slot: U256,
1289 ) -> bool {
1290 match &self.arbitrary_storage {
1291 Some(storage) => {
1292 storage.overwrites.contains(address) &&
1293 storage
1294 .values
1295 .get(address)
1296 .and_then(|arbitrary_values| arbitrary_values.get(&storage_slot))
1297 .is_none()
1298 }
1299 None => false,
1300 }
1301 }
1302
1303 pub fn is_arbitrary_storage_copy(&self, address: &Address) -> bool {
1305 match &self.arbitrary_storage {
1306 Some(storage) => storage.copies.contains_key(address),
1307 None => false,
1308 }
1309 }
1310}
1311
1312impl Inspector<EthEvmContext<&mut dyn DatabaseExt>> for Cheatcodes {
1313 #[inline]
1314 fn initialize_interp(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1315 if let Some(block) = self.block.take() {
1318 ecx.block = block;
1319 }
1320 if let Some(gas_price) = self.gas_price.take() {
1321 ecx.tx.gas_price = gas_price;
1322 }
1323
1324 if self.gas_metering.paused {
1326 self.gas_metering.paused_frames.push(interpreter.control.gas);
1327 }
1328
1329 if let Some(expected) = &mut self.expected_revert {
1331 expected.max_depth = max(ecx.journaled_state.depth(), expected.max_depth);
1332 }
1333 }
1334
1335 #[inline]
1336 fn step(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1337 self.pc = interpreter.bytecode.pc();
1338
1339 if self.gas_metering.paused {
1341 self.meter_gas(interpreter);
1342 }
1343
1344 if self.gas_metering.reset {
1346 self.meter_gas_reset(interpreter);
1347 }
1348
1349 if self.recording_accesses {
1351 self.record_accesses(interpreter);
1352 }
1353
1354 if self.recorded_account_diffs_stack.is_some() {
1356 self.record_state_diffs(interpreter, ecx);
1357 }
1358
1359 if !self.allowed_mem_writes.is_empty() {
1361 self.check_mem_opcodes(
1362 interpreter,
1363 ecx.journaled_state.depth().try_into().expect("journaled state depth exceeds u64"),
1364 );
1365 }
1366
1367 if let Some(mapping_slots) = &mut self.mapping_slots {
1369 mapping::step(mapping_slots, interpreter);
1370 }
1371
1372 if self.gas_metering.recording {
1374 self.meter_gas_record(interpreter, ecx);
1375 }
1376 }
1377
1378 #[inline]
1379 fn step_end(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1380 if self.gas_metering.paused {
1381 self.meter_gas_end(interpreter);
1382 }
1383
1384 if self.gas_metering.touched {
1385 self.meter_gas_check(interpreter);
1386 }
1387
1388 if self.arbitrary_storage.is_some() {
1390 self.arbitrary_storage_end(interpreter, ecx);
1391 }
1392 }
1393
1394 fn log(&mut self, interpreter: &mut Interpreter, _ecx: Ecx, log: Log) {
1395 if !self.expected_emits.is_empty() {
1396 expect::handle_expect_emit(self, &log, interpreter);
1397 }
1398
1399 if let Some(storage_recorded_logs) = &mut self.recorded_logs {
1401 storage_recorded_logs.push(Vm::Log {
1402 topics: log.data.topics().to_vec(),
1403 data: log.data.data.clone(),
1404 emitter: log.address,
1405 });
1406 }
1407 }
1408
1409 fn call(&mut self, ecx: Ecx, inputs: &mut CallInputs) -> Option<CallOutcome> {
1410 Self::call_with_executor(self, ecx, inputs, &mut TransparentCheatcodesExecutor)
1411 }
1412
1413 fn call_end(&mut self, ecx: Ecx, call: &CallInputs, outcome: &mut CallOutcome) {
1414 let cheatcode_call = call.target_address == CHEATCODE_ADDRESS ||
1415 call.target_address == HARDHAT_CONSOLE_ADDRESS;
1416
1417 if !cheatcode_call {
1421 let curr_depth = ecx.journaled_state.depth();
1423 if let Some(prank) = &self.get_prank(curr_depth) {
1424 if curr_depth == prank.depth {
1425 ecx.tx.caller = prank.prank_origin;
1426
1427 if prank.single_call {
1429 self.pranks.remove(&curr_depth);
1430 }
1431 }
1432 }
1433
1434 if let Some(broadcast) = &self.broadcast {
1436 if curr_depth == broadcast.depth {
1437 ecx.tx.caller = broadcast.original_origin;
1438
1439 if broadcast.single_call {
1441 let _ = self.broadcast.take();
1442 }
1443 }
1444 }
1445 }
1446
1447 if let Some(assume_no_revert) = &mut self.assume_no_revert {
1449 if outcome.result.is_revert() && assume_no_revert.reverted_by.is_none() {
1452 assume_no_revert.reverted_by = Some(call.target_address);
1453 }
1454
1455 let curr_depth = ecx.journaled_state.depth();
1457 if curr_depth <= assume_no_revert.depth && !cheatcode_call {
1458 if outcome.result.is_revert() {
1461 let assume_no_revert = std::mem::take(&mut self.assume_no_revert).unwrap();
1462 return match revert_handlers::handle_assume_no_revert(
1463 &assume_no_revert,
1464 outcome.result.result,
1465 &outcome.result.output,
1466 &self.config.available_artifacts,
1467 ) {
1468 Ok(_) => {
1471 outcome.result.output = Error::from(MAGIC_ASSUME).abi_encode().into();
1472 }
1473 Err(error) => {
1476 trace!(expected=?assume_no_revert, ?error, status=?outcome.result.result, "Expected revert mismatch");
1477 outcome.result.result = InstructionResult::Revert;
1478 outcome.result.output = error.abi_encode().into();
1479 }
1480 }
1481 } else {
1482 self.assume_no_revert = None;
1484 }
1485 }
1486 }
1487
1488 if let Some(expected_revert) = &mut self.expected_revert {
1490 if outcome.result.is_revert() {
1493 if expected_revert.reverter.is_some() &&
1497 (expected_revert.reverted_by.is_none() || expected_revert.count > 1)
1498 {
1499 expected_revert.reverted_by = Some(call.target_address);
1500 }
1501 }
1502
1503 let curr_depth = ecx.journaled_state.depth();
1504 if curr_depth <= expected_revert.depth {
1505 let needs_processing = match expected_revert.kind {
1506 ExpectedRevertKind::Default => !cheatcode_call,
1507 ExpectedRevertKind::Cheatcode { pending_processing } => {
1510 cheatcode_call && !pending_processing
1511 }
1512 };
1513
1514 if needs_processing {
1515 let mut expected_revert = std::mem::take(&mut self.expected_revert).unwrap();
1516 return match revert_handlers::handle_expect_revert(
1517 cheatcode_call,
1518 false,
1519 self.config.internal_expect_revert,
1520 &expected_revert,
1521 outcome.result.result,
1522 outcome.result.output.clone(),
1523 &self.config.available_artifacts,
1524 ) {
1525 Err(error) => {
1526 trace!(expected=?expected_revert, ?error, status=?outcome.result.result, "Expected revert mismatch");
1527 outcome.result.result = InstructionResult::Revert;
1528 outcome.result.output = error.abi_encode().into();
1529 }
1530 Ok((_, retdata)) => {
1531 expected_revert.actual_count += 1;
1532 if expected_revert.actual_count < expected_revert.count {
1533 self.expected_revert = Some(expected_revert.clone());
1534 }
1535 outcome.result.result = InstructionResult::Return;
1536 outcome.result.output = retdata;
1537 }
1538 };
1539 }
1540
1541 if let ExpectedRevertKind::Cheatcode { pending_processing } =
1544 &mut self.expected_revert.as_mut().unwrap().kind
1545 {
1546 *pending_processing = false;
1547 }
1548 }
1549 }
1550
1551 if cheatcode_call {
1554 return;
1555 }
1556
1557 let gas = outcome.result.gas;
1560 self.gas_metering.last_call_gas = Some(crate::Vm::Gas {
1561 gasLimit: gas.limit(),
1562 gasTotalUsed: gas.spent(),
1563 gasMemoryUsed: 0,
1564 gasRefunded: gas.refunded(),
1565 gasRemaining: gas.remaining(),
1566 });
1567
1568 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1571 if ecx.journaled_state.depth() > 0 {
1573 if let Some(last_recorded_depth) = &mut recorded_account_diffs_stack.pop() {
1574 if outcome.result.is_revert() {
1577 last_recorded_depth.iter_mut().for_each(|element| {
1578 element.reverted = true;
1579 element
1580 .storageAccesses
1581 .iter_mut()
1582 .for_each(|storage_access| storage_access.reverted = true);
1583 })
1584 }
1585
1586 if let Some(call_access) = last_recorded_depth.first_mut() {
1587 let curr_depth = ecx.journaled_state.depth();
1592 if call_access.depth == curr_depth as u64 {
1593 if let Ok(acc) = ecx.journaled_state.load_account(call.target_address) {
1594 debug_assert!(access_is_call(call_access.kind));
1595 call_access.newBalance = acc.info.balance;
1596 }
1597 }
1598 if let Some(last) = recorded_account_diffs_stack.last_mut() {
1603 last.append(last_recorded_depth);
1604 } else {
1605 recorded_account_diffs_stack.push(last_recorded_depth.clone());
1606 }
1607 }
1608 }
1609 }
1610 }
1611
1612 let should_check_emits = self
1624 .expected_emits
1625 .iter()
1626 .any(|(expected, _)| {
1627 let curr_depth = ecx.journaled_state.depth();
1628 expected.depth == curr_depth
1629 }) &&
1630 !call.is_static;
1632 if should_check_emits {
1633 let expected_counts = self
1634 .expected_emits
1635 .iter()
1636 .filter_map(|(expected, count_map)| {
1637 let count = match expected.address {
1638 Some(emitter) => match count_map.get(&emitter) {
1639 Some(log_count) => expected
1640 .log
1641 .as_ref()
1642 .map(|l| log_count.count(l))
1643 .unwrap_or_else(|| log_count.count_unchecked()),
1644 None => 0,
1645 },
1646 None => match &expected.log {
1647 Some(log) => count_map.values().map(|logs| logs.count(log)).sum(),
1648 None => count_map.values().map(|logs| logs.count_unchecked()).sum(),
1649 },
1650 };
1651
1652 if count != expected.count {
1653 Some((expected, count))
1654 } else {
1655 None
1656 }
1657 })
1658 .collect::<Vec<_>>();
1659
1660 if self.expected_emits.iter().any(|(expected, _)| !expected.found && expected.count > 0)
1662 {
1663 outcome.result.result = InstructionResult::Revert;
1664 outcome.result.output = "log != expected log".abi_encode().into();
1665 return;
1666 }
1667
1668 if !expected_counts.is_empty() {
1669 let msg = if outcome.result.is_ok() {
1670 let (expected, count) = expected_counts.first().unwrap();
1671 format!("log emitted {count} times, expected {}", expected.count)
1672 } else {
1673 "expected an emit, but the call reverted instead. \
1674 ensure you're testing the happy path when using `expectEmit`"
1675 .to_string()
1676 };
1677
1678 outcome.result.result = InstructionResult::Revert;
1679 outcome.result.output = Error::encode(msg);
1680 return;
1681 }
1682
1683 self.expected_emits.clear()
1687 }
1688
1689 let diag = self.fork_revert_diagnostic.take();
1692
1693 if outcome.result.is_revert() {
1696 if let Some(err) = diag {
1697 outcome.result.output = Error::encode(err.to_error_msg(&self.labels));
1698 return;
1699 }
1700 }
1701
1702 if let TxKind::Call(test_contract) = ecx.tx.kind {
1705 if ecx.journaled_state.db().is_forked_mode() &&
1708 outcome.result.result == InstructionResult::Stop &&
1709 call.target_address != test_contract
1710 {
1711 let journaled_state = ecx.journaled_state.clone();
1712 self.fork_revert_diagnostic =
1713 ecx.journaled_state.db().diagnose_revert(call.target_address, &journaled_state);
1714 }
1715 }
1716
1717 if ecx.journaled_state.depth() == 0 {
1719 if outcome.result.is_revert() {
1723 return;
1724 }
1725
1726 for (address, calldatas) in &self.expected_calls {
1731 for (calldata, (expected, actual_count)) in calldatas {
1733 let ExpectedCallData { gas, min_gas, value, count, call_type } = expected;
1735
1736 let failed = match call_type {
1737 ExpectedCallType::Count => *count != *actual_count,
1741 ExpectedCallType::NonCount => *count > *actual_count,
1746 };
1747 if failed {
1748 let expected_values = [
1749 Some(format!("data {}", hex::encode_prefixed(calldata))),
1750 value.as_ref().map(|v| format!("value {v}")),
1751 gas.map(|g| format!("gas {g}")),
1752 min_gas.map(|g| format!("minimum gas {g}")),
1753 ]
1754 .into_iter()
1755 .flatten()
1756 .join(", ");
1757 let but = if outcome.result.is_ok() {
1758 let s = if *actual_count == 1 { "" } else { "s" };
1759 format!("was called {actual_count} time{s}")
1760 } else {
1761 "the call reverted instead; \
1762 ensure you're testing the happy path when using `expectCall`"
1763 .to_string()
1764 };
1765 let s = if *count == 1 { "" } else { "s" };
1766 let msg = format!(
1767 "expected call to {address} with {expected_values} \
1768 to be called {count} time{s}, but {but}"
1769 );
1770 outcome.result.result = InstructionResult::Revert;
1771 outcome.result.output = Error::encode(msg);
1772
1773 return;
1774 }
1775 }
1776 }
1777
1778 self.expected_emits.retain(|(expected, _)| expected.count > 0 && !expected.found);
1781 if !self.expected_emits.is_empty() {
1783 let msg = if outcome.result.is_ok() {
1784 "expected an emit, but no logs were emitted afterwards. \
1785 you might have mismatched events or not enough events were emitted"
1786 } else {
1787 "expected an emit, but the call reverted instead. \
1788 ensure you're testing the happy path when using `expectEmit`"
1789 };
1790 outcome.result.result = InstructionResult::Revert;
1791 outcome.result.output = Error::encode(msg);
1792 return;
1793 }
1794
1795 if let Some(expected_create) = self.expected_creates.first() {
1797 let msg = format!(
1798 "expected {} call by address {} for bytecode {} but not found",
1799 expected_create.create_scheme,
1800 hex::encode_prefixed(expected_create.deployer),
1801 hex::encode_prefixed(&expected_create.bytecode),
1802 );
1803 outcome.result.result = InstructionResult::Revert;
1804 outcome.result.output = Error::encode(msg);
1805 }
1806 }
1807 }
1808
1809 fn create(&mut self, ecx: Ecx, call: &mut CreateInputs) -> Option<CreateOutcome> {
1810 self.create_common(ecx, call)
1811 }
1812
1813 fn create_end(&mut self, ecx: Ecx, call: &CreateInputs, outcome: &mut CreateOutcome) {
1814 self.create_end_common(ecx, Some(call), outcome)
1815 }
1816}
1817
1818impl InspectorExt for Cheatcodes {
1819 fn should_use_create2_factory(&mut self, ecx: Ecx, inputs: &CreateInputs) -> bool {
1820 if let CreateScheme::Create2 { .. } = inputs.scheme {
1821 let depth = ecx.journaled_state.depth();
1822 let target_depth = if let Some(prank) = &self.get_prank(depth) {
1823 prank.depth
1824 } else if let Some(broadcast) = &self.broadcast {
1825 broadcast.depth
1826 } else {
1827 1
1828 };
1829
1830 depth == target_depth &&
1831 (self.broadcast.is_some() || self.config.always_use_create_2_factory)
1832 } else {
1833 false
1834 }
1835 }
1836
1837 fn create2_deployer(&self) -> Address {
1838 self.config.evm_opts.create2_deployer
1839 }
1840}
1841
1842impl Cheatcodes {
1843 #[cold]
1844 fn meter_gas(&mut self, interpreter: &mut Interpreter) {
1845 if let Some(paused_gas) = self.gas_metering.paused_frames.last() {
1846 let memory = *interpreter.control.gas.memory();
1849 interpreter.control.gas = *paused_gas;
1850 interpreter.control.gas.memory_mut().words_num = memory.words_num;
1851 interpreter.control.gas.memory_mut().expansion_cost = memory.expansion_cost;
1852 } else {
1853 self.gas_metering.paused_frames.push(interpreter.control.gas);
1855 }
1856 }
1857
1858 #[cold]
1859 fn meter_gas_record(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1860 if matches!(interpreter.control.instruction_result, InstructionResult::Continue) {
1861 self.gas_metering.gas_records.iter_mut().for_each(|record| {
1862 let curr_depth = ecx.journaled_state.depth();
1863 if curr_depth == record.depth {
1864 if self.gas_metering.last_gas_used != 0 {
1867 let gas_diff = interpreter
1868 .control
1869 .gas
1870 .spent()
1871 .saturating_sub(self.gas_metering.last_gas_used);
1872 record.gas_used = record.gas_used.saturating_add(gas_diff);
1873 }
1874
1875 self.gas_metering.last_gas_used = interpreter.control.gas.spent();
1878 }
1879 });
1880 }
1881 }
1882
1883 #[cold]
1884 fn meter_gas_end(&mut self, interpreter: &mut Interpreter) {
1885 if will_exit(interpreter.control.instruction_result) {
1887 self.gas_metering.paused_frames.pop();
1888 }
1889 }
1890
1891 #[cold]
1892 fn meter_gas_reset(&mut self, interpreter: &mut Interpreter) {
1893 interpreter.control.gas = Gas::new(interpreter.control.gas.limit());
1894 self.gas_metering.reset = false;
1895 }
1896
1897 #[cold]
1898 fn meter_gas_check(&mut self, interpreter: &mut Interpreter) {
1899 if will_exit(interpreter.control.instruction_result) {
1900 if interpreter.control.gas.spent() <
1904 u64::try_from(interpreter.control.gas.refunded()).unwrap_or_default()
1905 {
1906 interpreter.control.gas = Gas::new(interpreter.control.gas.limit());
1907 }
1908 }
1909 }
1910
1911 #[cold]
1919 fn arbitrary_storage_end(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1920 let (key, target_address) = if interpreter.bytecode.opcode() == op::SLOAD {
1921 (try_or_return!(interpreter.stack.peek(0)), interpreter.input.target_address)
1922 } else {
1923 return
1924 };
1925
1926 let Some(value) = ecx.sload(target_address, key) else {
1927 return;
1928 };
1929
1930 if (value.is_cold && value.data.is_zero()) ||
1931 self.should_overwrite_arbitrary_storage(&target_address, key)
1932 {
1933 if self.has_arbitrary_storage(&target_address) {
1934 let arbitrary_value = self.rng().random();
1935 self.arbitrary_storage.as_mut().unwrap().save(
1936 ecx,
1937 target_address,
1938 key,
1939 arbitrary_value,
1940 );
1941 } else if self.is_arbitrary_storage_copy(&target_address) {
1942 let arbitrary_value = self.rng().random();
1943 self.arbitrary_storage.as_mut().unwrap().copy(
1944 ecx,
1945 target_address,
1946 key,
1947 arbitrary_value,
1948 );
1949 }
1950 }
1951 }
1952
1953 #[cold]
1955 fn record_accesses(&mut self, interpreter: &mut Interpreter) {
1956 let access = &mut self.accesses;
1957 match interpreter.bytecode.opcode() {
1958 op::SLOAD => {
1959 let key = try_or_return!(interpreter.stack.peek(0));
1960 access.record_read(interpreter.input.target_address, key);
1961 }
1962 op::SSTORE => {
1963 let key = try_or_return!(interpreter.stack.peek(0));
1964 access.record_write(interpreter.input.target_address, key);
1965 }
1966 _ => {}
1967 }
1968 }
1969
1970 #[cold]
1971 fn record_state_diffs(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1972 let Some(account_accesses) = &mut self.recorded_account_diffs_stack else { return };
1973 match interpreter.bytecode.opcode() {
1974 op::SELFDESTRUCT => {
1975 let Some(last) = account_accesses.last_mut() else { return };
1977
1978 let target = try_or_return!(interpreter.stack.peek(0));
1980 let target = Address::from_word(B256::from(target));
1981 let (initialized, old_balance) = ecx
1982 .journaled_state
1983 .load_account(target)
1984 .map(|account| (account.info.exists(), account.info.balance))
1985 .unwrap_or_default();
1986
1987 let value = ecx
1989 .balance(interpreter.input.target_address)
1990 .map(|b| b.data)
1991 .unwrap_or(U256::ZERO);
1992
1993 last.push(crate::Vm::AccountAccess {
1995 chainInfo: crate::Vm::ChainInfo {
1996 forkId: ecx.journaled_state.database.active_fork_id().unwrap_or_default(),
1997 chainId: U256::from(ecx.cfg.chain_id),
1998 },
1999 accessor: interpreter.input.target_address,
2000 account: target,
2001 kind: crate::Vm::AccountAccessKind::SelfDestruct,
2002 initialized,
2003 oldBalance: old_balance,
2004 newBalance: old_balance + value,
2005 value,
2006 data: Bytes::new(),
2007 reverted: false,
2008 deployedCode: Bytes::new(),
2009 storageAccesses: vec![],
2010 depth: ecx
2011 .journaled_state
2012 .depth()
2013 .try_into()
2014 .expect("journaled state depth exceeds u64"),
2015 });
2016 }
2017
2018 op::SLOAD => {
2019 let Some(last) = account_accesses.last_mut() else { return };
2020
2021 let key = try_or_return!(interpreter.stack.peek(0));
2022 let address = interpreter.input.target_address;
2023
2024 let mut present_value = U256::ZERO;
2027 if ecx.journaled_state.load_account(address).is_ok() {
2029 if let Some(previous) = ecx.sload(address, key) {
2030 present_value = previous.data;
2031 }
2032 }
2033 let access = crate::Vm::StorageAccess {
2034 account: interpreter.input.target_address,
2035 slot: key.into(),
2036 isWrite: false,
2037 previousValue: present_value.into(),
2038 newValue: present_value.into(),
2039 reverted: false,
2040 };
2041 let curr_depth = ecx
2042 .journaled_state
2043 .depth()
2044 .try_into()
2045 .expect("journaled state depth exceeds u64");
2046 append_storage_access(last, access, curr_depth);
2047 }
2048 op::SSTORE => {
2049 let Some(last) = account_accesses.last_mut() else { return };
2050
2051 let key = try_or_return!(interpreter.stack.peek(0));
2052 let value = try_or_return!(interpreter.stack.peek(1));
2053 let address = interpreter.input.target_address;
2054 let mut previous_value = U256::ZERO;
2057 if ecx.journaled_state.load_account(address).is_ok() {
2058 if let Some(previous) = ecx.sload(address, key) {
2059 previous_value = previous.data;
2060 }
2061 }
2062
2063 let access = crate::Vm::StorageAccess {
2064 account: address,
2065 slot: key.into(),
2066 isWrite: true,
2067 previousValue: previous_value.into(),
2068 newValue: value.into(),
2069 reverted: false,
2070 };
2071 let curr_depth = ecx
2072 .journaled_state
2073 .depth()
2074 .try_into()
2075 .expect("journaled state depth exceeds u64");
2076 append_storage_access(last, access, curr_depth);
2077 }
2078
2079 op::EXTCODECOPY | op::EXTCODESIZE | op::EXTCODEHASH | op::BALANCE => {
2081 let kind = match interpreter.bytecode.opcode() {
2082 op::EXTCODECOPY => crate::Vm::AccountAccessKind::Extcodecopy,
2083 op::EXTCODESIZE => crate::Vm::AccountAccessKind::Extcodesize,
2084 op::EXTCODEHASH => crate::Vm::AccountAccessKind::Extcodehash,
2085 op::BALANCE => crate::Vm::AccountAccessKind::Balance,
2086 _ => unreachable!(),
2087 };
2088 let address =
2089 Address::from_word(B256::from(try_or_return!(interpreter.stack.peek(0))));
2090 let initialized;
2091 let balance;
2092 if let Ok(acc) = ecx.journaled_state.load_account(address) {
2093 initialized = acc.info.exists();
2094 balance = acc.info.balance;
2095 } else {
2096 initialized = false;
2097 balance = U256::ZERO;
2098 }
2099 let curr_depth = ecx
2100 .journaled_state
2101 .depth()
2102 .try_into()
2103 .expect("journaled state depth exceeds u64");
2104 let account_access = crate::Vm::AccountAccess {
2105 chainInfo: crate::Vm::ChainInfo {
2106 forkId: ecx.journaled_state.database.active_fork_id().unwrap_or_default(),
2107 chainId: U256::from(ecx.cfg.chain_id),
2108 },
2109 accessor: interpreter.input.target_address,
2110 account: address,
2111 kind,
2112 initialized,
2113 oldBalance: balance,
2114 newBalance: balance,
2115 value: U256::ZERO,
2116 data: Bytes::new(),
2117 reverted: false,
2118 deployedCode: Bytes::new(),
2119 storageAccesses: vec![],
2120 depth: curr_depth,
2121 };
2122 if let Some(last) = account_accesses.last_mut() {
2125 last.push(account_access);
2126 } else {
2127 account_accesses.push(vec![account_access]);
2128 }
2129 }
2130 _ => {}
2131 }
2132 }
2133
2134 #[cold]
2139 fn check_mem_opcodes(&self, interpreter: &mut Interpreter, depth: u64) {
2140 let Some(ranges) = self.allowed_mem_writes.get(&depth) else {
2141 return;
2142 };
2143
2144 macro_rules! mem_opcode_match {
2153 ($(($opcode:ident, $offset_depth:expr, $size_depth:expr, $writes:expr)),* $(,)?) => {
2154 match interpreter.bytecode.opcode() {
2155 op::MSTORE => {
2160 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2162
2163 if !ranges.iter().any(|range| {
2166 range.contains(&offset) && range.contains(&(offset + 31))
2167 }) {
2168 let value = try_or_return!(interpreter.stack.peek(1)).to_be_bytes::<32>();
2173 if value[..SELECTOR_LEN] == stopExpectSafeMemoryCall::SELECTOR {
2174 return
2175 }
2176
2177 disallowed_mem_write(offset, 32, interpreter, ranges);
2178 return
2179 }
2180 }
2181 op::MSTORE8 => {
2182 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2184
2185 if !ranges.iter().any(|range| range.contains(&offset)) {
2188 disallowed_mem_write(offset, 1, interpreter, ranges);
2189 return
2190 }
2191 }
2192
2193 op::MLOAD => {
2198 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2200
2201 if offset >= interpreter.memory.size() as u64 && !ranges.iter().any(|range| {
2205 range.contains(&offset) && range.contains(&(offset + 31))
2206 }) {
2207 disallowed_mem_write(offset, 32, interpreter, ranges);
2208 return
2209 }
2210 }
2211
2212 op::CALL => {
2217 let dest_offset = try_or_return!(interpreter.stack.peek(5)).saturating_to::<u64>();
2219
2220 let size = try_or_return!(interpreter.stack.peek(6)).saturating_to::<u64>();
2222
2223 let fail_cond = !ranges.iter().any(|range| {
2227 range.contains(&dest_offset) &&
2228 range.contains(&(dest_offset + size.saturating_sub(1)))
2229 });
2230
2231 if fail_cond {
2234 let to = Address::from_word(try_or_return!(interpreter.stack.peek(1)).to_be_bytes::<32>().into());
2238 if to == CHEATCODE_ADDRESS {
2239 let args_offset = try_or_return!(interpreter.stack.peek(3)).saturating_to::<usize>();
2240 let args_size = try_or_return!(interpreter.stack.peek(4)).saturating_to::<usize>();
2241 let memory_word = interpreter.memory.slice_len(args_offset, args_size);
2242 if memory_word[..SELECTOR_LEN] == stopExpectSafeMemoryCall::SELECTOR {
2243 return
2244 }
2245 }
2246
2247 disallowed_mem_write(dest_offset, size, interpreter, ranges);
2248 return
2249 }
2250 }
2251
2252 $(op::$opcode => {
2253 let dest_offset = try_or_return!(interpreter.stack.peek($offset_depth)).saturating_to::<u64>();
2255
2256 let size = try_or_return!(interpreter.stack.peek($size_depth)).saturating_to::<u64>();
2258
2259 let fail_cond = !ranges.iter().any(|range| {
2263 range.contains(&dest_offset) &&
2264 range.contains(&(dest_offset + size.saturating_sub(1)))
2265 }) && ($writes ||
2266 [dest_offset, (dest_offset + size).saturating_sub(1)].into_iter().any(|offset| {
2267 offset >= interpreter.memory.size() as u64
2268 })
2269 );
2270
2271 if fail_cond {
2274 disallowed_mem_write(dest_offset, size, interpreter, ranges);
2275 return
2276 }
2277 })*
2278
2279 _ => {}
2280 }
2281 }
2282 }
2283
2284 mem_opcode_match!(
2287 (CALLDATACOPY, 0, 2, true),
2288 (CODECOPY, 0, 2, true),
2289 (RETURNDATACOPY, 0, 2, true),
2290 (EXTCODECOPY, 1, 3, true),
2291 (CALLCODE, 5, 6, true),
2292 (STATICCALL, 4, 5, true),
2293 (DELEGATECALL, 4, 5, true),
2294 (KECCAK256, 0, 1, false),
2295 (LOG0, 0, 1, false),
2296 (LOG1, 0, 1, false),
2297 (LOG2, 0, 1, false),
2298 (LOG3, 0, 1, false),
2299 (LOG4, 0, 1, false),
2300 (CREATE, 1, 2, false),
2301 (CREATE2, 1, 2, false),
2302 (RETURN, 0, 1, false),
2303 (REVERT, 0, 1, false),
2304 );
2305 }
2306}
2307
2308fn disallowed_mem_write(
2314 dest_offset: u64,
2315 size: u64,
2316 interpreter: &mut Interpreter,
2317 ranges: &[Range<u64>],
2318) {
2319 let revert_string = format!(
2320 "memory write at offset 0x{:02X} of size 0x{:02X} not allowed; safe range: {}",
2321 dest_offset,
2322 size,
2323 ranges.iter().map(|r| format!("(0x{:02X}, 0x{:02X}]", r.start, r.end)).join(" U ")
2324 );
2325
2326 interpreter.control.instruction_result = InstructionResult::Revert;
2327 interpreter.control.next_action = InterpreterAction::Return {
2328 result: InterpreterResult {
2329 output: Error::encode(revert_string),
2330 gas: interpreter.control.gas,
2331 result: InstructionResult::Revert,
2332 },
2333 };
2334}
2335
2336fn check_if_fixed_gas_limit(ecx: &Ecx, call_gas_limit: u64) -> bool {
2339 ecx.tx.gas_limit > ecx.block.gas_limit &&
2344 call_gas_limit <= ecx.block.gas_limit
2345 && call_gas_limit > 2300
2348}
2349
2350fn access_is_call(kind: crate::Vm::AccountAccessKind) -> bool {
2352 matches!(
2353 kind,
2354 crate::Vm::AccountAccessKind::Call |
2355 crate::Vm::AccountAccessKind::StaticCall |
2356 crate::Vm::AccountAccessKind::CallCode |
2357 crate::Vm::AccountAccessKind::DelegateCall
2358 )
2359}
2360
2361fn append_storage_access(
2363 last: &mut Vec<AccountAccess>,
2364 storage_access: crate::Vm::StorageAccess,
2365 storage_depth: u64,
2366) {
2367 if !last.is_empty() && last.first().unwrap().depth < storage_depth {
2369 if last.len() == 1 {
2375 last.first_mut().unwrap().storageAccesses.push(storage_access);
2376 } else {
2377 let last_record = last.last_mut().unwrap();
2378 if last_record.kind as u8 == crate::Vm::AccountAccessKind::Resume as u8 {
2379 last_record.storageAccesses.push(storage_access);
2380 } else {
2381 let entry = last.first().unwrap();
2382 let resume_record = crate::Vm::AccountAccess {
2383 chainInfo: crate::Vm::ChainInfo {
2384 forkId: entry.chainInfo.forkId,
2385 chainId: entry.chainInfo.chainId,
2386 },
2387 accessor: entry.accessor,
2388 account: entry.account,
2389 kind: crate::Vm::AccountAccessKind::Resume,
2390 initialized: entry.initialized,
2391 storageAccesses: vec![storage_access],
2392 reverted: entry.reverted,
2393 oldBalance: U256::ZERO,
2395 newBalance: U256::ZERO,
2396 value: U256::ZERO,
2397 data: Bytes::new(),
2398 deployedCode: Bytes::new(),
2399 depth: entry.depth,
2400 };
2401 last.push(resume_record);
2402 }
2403 }
2404 }
2405}
2406
2407fn apply_dispatch(
2409 calls: &Vm::VmCalls,
2410 ccx: &mut CheatsCtxt,
2411 executor: &mut dyn CheatcodesExecutor,
2412) -> Result {
2413 let cheat = calls_as_dyn_cheatcode(calls);
2414
2415 let _guard = debug_span!(target: "cheatcodes", "apply", id = %cheat.id()).entered();
2416 trace!(target: "cheatcodes", cheat = ?cheat.as_debug(), "applying");
2417
2418 if let spec::Status::Deprecated(replacement) = *cheat.status() {
2419 ccx.state.deprecated.insert(cheat.signature(), replacement);
2420 }
2421
2422 let mut result = cheat.dyn_apply(ccx, executor);
2424
2425 if let Err(e) = &mut result {
2427 if e.is_str() {
2428 let name = cheat.name();
2429 if !name.contains("assert") && name != "rpcUrl" {
2433 *e = fmt_err!("vm.{name}: {e}");
2434 }
2435 }
2436 }
2437
2438 trace!(
2439 target: "cheatcodes",
2440 return = %match &result {
2441 Ok(b) => hex::encode(b),
2442 Err(e) => e.to_string(),
2443 }
2444 );
2445
2446 result
2447}
2448
2449fn calls_as_dyn_cheatcode(calls: &Vm::VmCalls) -> &dyn DynCheatcode {
2450 macro_rules! as_dyn {
2451 ($($variant:ident),*) => {
2452 match calls {
2453 $(Vm::VmCalls::$variant(cheat) => cheat,)*
2454 }
2455 };
2456 }
2457 vm_calls!(as_dyn)
2458}
2459
2460fn will_exit(ir: InstructionResult) -> bool {
2462 !matches!(ir, InstructionResult::Continue | InstructionResult::CallOrCreate)
2463}