1use crate::{
4 CheatsConfig, CheatsCtxt, DynCheatcode, Error, Result,
5 Vm::{self, AccountAccess},
6 evm::{
7 DealRecord, GasRecord, RecordAccess, mapping,
8 mock::{MockCallDataContext, MockCallReturnData},
9 prank::Prank,
10 },
11 inspector::utils::CommonCreateInput,
12 script::{Broadcast, Wallets},
13 test::{
14 assume::AssumeNoRevert,
15 expect::{
16 self, ExpectedCallData, ExpectedCallTracker, ExpectedCallType, ExpectedCreate,
17 ExpectedEmitTracker, ExpectedRevert, ExpectedRevertKind,
18 },
19 revert_handlers,
20 },
21 utils::IgnoredTraces,
22};
23use alloy_consensus::BlobTransactionSidecar;
24use alloy_evm::eth::EthEvmContext;
25use alloy_network::TransactionBuilder4844;
26use alloy_primitives::{
27 Address, B256, Bytes, Log, TxKind, U256, hex,
28 map::{AddressHashMap, HashMap, HashSet},
29};
30use alloy_rpc_types::{
31 AccessList,
32 request::{TransactionInput, TransactionRequest},
33};
34use alloy_sol_types::{SolCall, SolInterface, SolValue};
35use foundry_common::{
36 SELECTOR_LEN, TransactionMaybeSigned, evm::Breakpoints, mapping_slots::MappingSlots,
37};
38use foundry_evm_core::{
39 InspectorExt,
40 abi::Vm::stopExpectSafeMemoryCall,
41 backend::{DatabaseError, DatabaseExt, RevertDiagnostic},
42 constants::{CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS, MAGIC_ASSUME},
43 evm::{FoundryEvm, new_evm_with_existing_context},
44};
45use foundry_evm_traces::{
46 TracingInspector, TracingInspectorConfig, identifier::SignaturesIdentifier,
47};
48use foundry_wallets::multi_wallet::MultiWallet;
49use itertools::Itertools;
50use proptest::test_runner::{RngAlgorithm, TestRng, TestRunner};
51use rand::Rng;
52use revm::{
53 Inspector, Journal,
54 bytecode::opcode as op,
55 context::{BlockEnv, JournalTr, LocalContext, TransactionType, result::EVMError},
56 context_interface::{CreateScheme, transaction::SignedAuthorization},
57 handler::FrameResult,
58 interpreter::{
59 CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, FrameInput, Gas, Host,
60 InstructionResult, Interpreter, InterpreterAction, InterpreterResult,
61 interpreter_types::{Jumps, LoopControl, MemoryTr},
62 },
63 state::EvmStorageSlot,
64};
65use serde_json::Value;
66use std::{
67 cmp::max,
68 collections::{BTreeMap, VecDeque},
69 fs::File,
70 io::BufReader,
71 ops::Range,
72 path::PathBuf,
73 sync::Arc,
74};
75
76mod utils;
77
78pub type Ecx<'a, 'b, 'c> = &'a mut EthEvmContext<&'b mut (dyn DatabaseExt + 'c)>;
79
80pub trait CheatcodesExecutor {
86 fn get_inspector<'a>(&'a mut self, cheats: &'a mut Cheatcodes) -> Box<dyn InspectorExt + 'a>;
89
90 fn exec_create(
92 &mut self,
93 inputs: CreateInputs,
94 ccx: &mut CheatsCtxt,
95 ) -> Result<CreateOutcome, EVMError<DatabaseError>> {
96 with_evm(self, ccx, |evm| {
97 evm.inner.ctx.journaled_state.depth += 1;
98
99 let frame = FrameInput::Create(Box::new(inputs));
100
101 let outcome = match evm.run_execution(frame)? {
102 FrameResult::Call(_) => unreachable!(),
103 FrameResult::Create(create) => create,
104 };
105
106 evm.inner.ctx.journaled_state.depth -= 1;
107
108 Ok(outcome)
109 })
110 }
111
112 fn console_log(&mut self, ccx: &mut CheatsCtxt, msg: &str) {
113 self.get_inspector(ccx.state).console_log(msg);
114 }
115
116 fn tracing_inspector(&mut self) -> Option<&mut TracingInspector> {
118 None
119 }
120}
121
122fn with_evm<E, F, O>(
124 executor: &mut E,
125 ccx: &mut CheatsCtxt,
126 f: F,
127) -> Result<O, EVMError<DatabaseError>>
128where
129 E: CheatcodesExecutor + ?Sized,
130 F: for<'a, 'b> FnOnce(
131 &mut FoundryEvm<'a, &'b mut dyn InspectorExt>,
132 ) -> Result<O, EVMError<DatabaseError>>,
133{
134 let mut inspector = executor.get_inspector(ccx.state);
135 let error = std::mem::replace(&mut ccx.ecx.error, Ok(()));
136
137 let ctx = EthEvmContext {
138 block: ccx.ecx.block.clone(),
139 cfg: ccx.ecx.cfg.clone(),
140 tx: ccx.ecx.tx.clone(),
141 journaled_state: Journal {
142 inner: ccx.ecx.journaled_state.inner.clone(),
143 database: &mut *ccx.ecx.journaled_state.database as &mut dyn DatabaseExt,
144 },
145 local: LocalContext::default(),
146 chain: (),
147 error,
148 };
149
150 let mut evm = new_evm_with_existing_context(ctx, &mut *inspector);
151
152 let res = f(&mut evm)?;
153
154 ccx.ecx.journaled_state.inner = evm.inner.ctx.journaled_state.inner;
155 ccx.ecx.block = evm.inner.ctx.block;
156 ccx.ecx.tx = evm.inner.ctx.tx;
157 ccx.ecx.cfg = evm.inner.ctx.cfg;
158 ccx.ecx.error = evm.inner.ctx.error;
159
160 Ok(res)
161}
162
163#[derive(Debug, Default, Clone, Copy)]
166struct TransparentCheatcodesExecutor;
167
168impl CheatcodesExecutor for TransparentCheatcodesExecutor {
169 fn get_inspector<'a>(&'a mut self, cheats: &'a mut Cheatcodes) -> Box<dyn InspectorExt + 'a> {
170 Box::new(cheats)
171 }
172}
173
174macro_rules! try_or_return {
175 ($e:expr) => {
176 match $e {
177 Ok(v) => v,
178 Err(_) => return,
179 }
180 };
181}
182
183#[derive(Debug, Default)]
185pub struct TestContext {
186 pub opened_read_files: HashMap<PathBuf, BufReader<File>>,
188}
189
190impl Clone for TestContext {
192 fn clone(&self) -> Self {
193 Default::default()
194 }
195}
196
197impl TestContext {
198 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, 0));
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, 0));
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, 0));
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_delegations: Vec<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 pub ignored_traces: IgnoredTraces,
490
491 pub arbitrary_storage: Option<ArbitraryStorage>,
493
494 pub deprecated: HashMap<&'static str, Option<&'static str>>,
496 pub wallets: Option<Wallets>,
498 pub signatures_identifier: Option<SignaturesIdentifier>,
500}
501
502impl Default for Cheatcodes {
506 fn default() -> Self {
507 Self::new(Arc::default())
508 }
509}
510
511impl Cheatcodes {
512 pub fn new(config: Arc<CheatsConfig>) -> Self {
514 Self {
515 fs_commit: true,
516 labels: config.labels.clone(),
517 config,
518 block: Default::default(),
519 active_delegations: Default::default(),
520 active_blob_sidecar: Default::default(),
521 gas_price: Default::default(),
522 pranks: Default::default(),
523 expected_revert: Default::default(),
524 assume_no_revert: Default::default(),
525 fork_revert_diagnostic: Default::default(),
526 accesses: Default::default(),
527 recording_accesses: Default::default(),
528 recorded_account_diffs_stack: Default::default(),
529 recorded_logs: Default::default(),
530 record_debug_steps_info: Default::default(),
531 mocked_calls: Default::default(),
532 mocked_functions: Default::default(),
533 expected_calls: Default::default(),
534 expected_emits: Default::default(),
535 expected_creates: Default::default(),
536 allowed_mem_writes: Default::default(),
537 broadcast: Default::default(),
538 broadcastable_transactions: Default::default(),
539 access_list: Default::default(),
540 test_context: Default::default(),
541 serialized_jsons: Default::default(),
542 eth_deals: Default::default(),
543 gas_metering: Default::default(),
544 gas_snapshots: Default::default(),
545 mapping_slots: Default::default(),
546 pc: Default::default(),
547 breakpoints: Default::default(),
548 intercept_next_create_call: Default::default(),
549 test_runner: Default::default(),
550 ignored_traces: Default::default(),
551 arbitrary_storage: Default::default(),
552 deprecated: Default::default(),
553 wallets: Default::default(),
554 signatures_identifier: SignaturesIdentifier::new(true).ok(),
555 }
556 }
557
558 pub fn get_prank(&self, depth: usize) -> Option<&Prank> {
562 self.pranks.range(..=depth).last().map(|(_, prank)| prank)
563 }
564
565 pub fn wallets(&mut self) -> &Wallets {
567 self.wallets.get_or_insert_with(|| Wallets::new(MultiWallet::default(), None))
568 }
569
570 pub fn set_wallets(&mut self, wallets: Wallets) {
572 self.wallets = Some(wallets);
573 }
574
575 pub fn add_delegation(&mut self, authorization: SignedAuthorization) {
577 self.active_delegations.push(authorization);
578 }
579
580 fn apply_cheatcode(
582 &mut self,
583 ecx: Ecx,
584 call: &CallInputs,
585 executor: &mut dyn CheatcodesExecutor,
586 ) -> Result {
587 let decoded = Vm::VmCalls::abi_decode(&call.input.bytes(ecx)).map_err(|e| {
589 if let alloy_sol_types::Error::UnknownSelector { name: _, selector } = e {
590 let msg = format!(
591 "unknown cheatcode with selector {selector}; \
592 you may have a mismatch between the `Vm` interface (likely in `forge-std`) \
593 and the `forge` version"
594 );
595 return alloy_sol_types::Error::Other(std::borrow::Cow::Owned(msg));
596 }
597 e
598 })?;
599
600 let caller = call.caller;
601
602 ecx.journaled_state.database.ensure_cheatcode_access_forking_mode(&caller)?;
605
606 apply_dispatch(
607 &decoded,
608 &mut CheatsCtxt { state: self, ecx, gas_limit: call.gas_limit, caller },
609 executor,
610 )
611 }
612
613 fn allow_cheatcodes_on_create(&self, ecx: Ecx, caller: Address, created_address: Address) {
619 if ecx.journaled_state.depth <= 1
620 || ecx.journaled_state.database.has_cheatcode_access(&caller)
621 {
622 ecx.journaled_state.database.allow_cheatcode_access(created_address);
623 }
624 }
625
626 fn apply_accesslist(&mut self, ecx: Ecx) {
632 if let Some(access_list) = &self.access_list {
633 ecx.tx.access_list = access_list.clone();
634
635 if ecx.tx.tx_type == TransactionType::Legacy as u8 {
636 ecx.tx.tx_type = TransactionType::Eip2930 as u8;
637 }
638 }
639 }
640
641 pub fn on_revert(&mut self, ecx: Ecx) {
646 trace!(deals=?self.eth_deals.len(), "rolling back deals");
647
648 if self.expected_revert.is_some() {
650 return;
651 }
652
653 if ecx.journaled_state.depth() > 0 {
655 return;
656 }
657
658 while let Some(record) = self.eth_deals.pop() {
662 if let Some(acc) = ecx.journaled_state.state.get_mut(&record.address) {
663 acc.info.balance = record.old_balance;
664 }
665 }
666 }
667
668 pub fn call_with_executor(
669 &mut self,
670 ecx: Ecx,
671 call: &mut CallInputs,
672 executor: &mut dyn CheatcodesExecutor,
673 ) -> Option<CallOutcome> {
674 let gas = Gas::new(call.gas_limit);
675 let curr_depth = ecx.journaled_state.depth();
676
677 if curr_depth == 0 {
681 let sender = ecx.tx.caller;
682 let account = match super::evm::journaled_account(ecx, sender) {
683 Ok(account) => account,
684 Err(err) => {
685 return Some(CallOutcome {
686 result: InterpreterResult {
687 result: InstructionResult::Revert,
688 output: err.abi_encode().into(),
689 gas,
690 },
691 memory_offset: call.return_memory_offset.clone(),
692 });
693 }
694 };
695 let prev = account.info.nonce;
696 account.info.nonce = prev.saturating_sub(1);
697
698 trace!(target: "cheatcodes", %sender, nonce=account.info.nonce, prev, "corrected nonce");
699 }
700
701 if call.target_address == CHEATCODE_ADDRESS {
702 return match self.apply_cheatcode(ecx, call, executor) {
703 Ok(retdata) => Some(CallOutcome {
704 result: InterpreterResult {
705 result: InstructionResult::Return,
706 output: retdata.into(),
707 gas,
708 },
709 memory_offset: call.return_memory_offset.clone(),
710 }),
711 Err(err) => Some(CallOutcome {
712 result: InterpreterResult {
713 result: InstructionResult::Revert,
714 output: err.abi_encode().into(),
715 gas,
716 },
717 memory_offset: call.return_memory_offset.clone(),
718 }),
719 };
720 }
721
722 if call.target_address == HARDHAT_CONSOLE_ADDRESS {
723 return None;
724 }
725
726 if let Some(expected_calls_for_target) = self.expected_calls.get_mut(&call.bytecode_address)
730 {
731 for (calldata, (expected, actual_count)) in expected_calls_for_target {
733 if calldata.len() <= call.input.len() &&
736 *calldata == call.input.bytes(ecx)[..calldata.len()] &&
738 expected
740 .value.is_none_or(|value| Some(value) == call.transfer_value()) &&
741 expected.gas.is_none_or(|gas| gas == call.gas_limit) &&
743 expected.min_gas.is_none_or(|min_gas| min_gas <= call.gas_limit)
745 {
746 *actual_count += 1;
747 }
748 }
749 }
750
751 if let Some(mocks) = self.mocked_calls.get_mut(&call.bytecode_address) {
753 let ctx = MockCallDataContext {
754 calldata: call.input.bytes(ecx),
755 value: call.transfer_value(),
756 };
757
758 if let Some(return_data_queue) = match mocks.get_mut(&ctx) {
759 Some(queue) => Some(queue),
760 None => mocks
761 .iter_mut()
762 .find(|(mock, _)| {
763 call.input.bytes(ecx).get(..mock.calldata.len()) == Some(&mock.calldata[..])
764 && mock.value.is_none_or(|value| Some(value) == call.transfer_value())
765 })
766 .map(|(_, v)| v),
767 } && let Some(return_data) = if return_data_queue.len() == 1 {
768 return_data_queue.front().map(|x| x.to_owned())
770 } else {
771 return_data_queue.pop_front()
773 } {
774 return Some(CallOutcome {
775 result: InterpreterResult {
776 result: return_data.ret_type,
777 output: return_data.data,
778 gas,
779 },
780 memory_offset: call.return_memory_offset.clone(),
781 });
782 }
783 }
784
785 if let Some(prank) = &self.get_prank(curr_depth) {
787 if prank.delegate_call
789 && curr_depth == prank.depth
790 && let CallScheme::DelegateCall = call.scheme
791 {
792 call.target_address = prank.new_caller;
793 call.caller = prank.new_caller;
794 if let Some(new_origin) = prank.new_origin {
795 ecx.tx.caller = new_origin;
796 }
797 }
798
799 if curr_depth >= prank.depth && call.caller == prank.prank_caller {
800 let mut prank_applied = false;
801
802 if curr_depth == prank.depth {
804 call.caller = prank.new_caller;
805 prank_applied = true;
806 }
807
808 if let Some(new_origin) = prank.new_origin {
810 ecx.tx.caller = new_origin;
811 prank_applied = true;
812 }
813
814 if prank_applied && let Some(applied_prank) = prank.first_time_applied() {
816 self.pranks.insert(curr_depth, applied_prank);
817 }
818 }
819 }
820
821 self.apply_accesslist(ecx);
823
824 if let Some(broadcast) = &self.broadcast {
826 if curr_depth == broadcast.depth && call.caller == broadcast.original_caller {
831 ecx.tx.caller = broadcast.new_origin;
835
836 call.caller = broadcast.new_origin;
837 if !call.is_static {
842 if let Err(err) = ecx.journaled_state.load_account(broadcast.new_origin) {
843 return Some(CallOutcome {
844 result: InterpreterResult {
845 result: InstructionResult::Revert,
846 output: Error::encode(err),
847 gas,
848 },
849 memory_offset: call.return_memory_offset.clone(),
850 });
851 }
852
853 let is_fixed_gas_limit = check_if_fixed_gas_limit(&ecx, call.gas_limit);
854
855 let input = TransactionInput::new(call.input.bytes(ecx));
856
857 let account =
858 ecx.journaled_state.inner.state().get_mut(&broadcast.new_origin).unwrap();
859
860 let mut tx_req = TransactionRequest {
861 from: Some(broadcast.new_origin),
862 to: Some(TxKind::from(Some(call.target_address))),
863 value: call.transfer_value(),
864 input,
865 nonce: Some(account.info.nonce),
866 chain_id: Some(ecx.cfg.chain_id),
867 gas: if is_fixed_gas_limit { Some(call.gas_limit) } else { None },
868 ..Default::default()
869 };
870
871 let active_delegations = std::mem::take(&mut self.active_delegations);
872 if let Some(blob_sidecar) = self.active_blob_sidecar.take() {
874 if !active_delegations.is_empty() {
876 let msg = "both delegation and blob are active; `attachBlob` and `attachDelegation` are not compatible";
877 return Some(CallOutcome {
878 result: InterpreterResult {
879 result: InstructionResult::Revert,
880 output: Error::encode(msg),
881 gas,
882 },
883 memory_offset: call.return_memory_offset.clone(),
884 });
885 }
886 tx_req.set_blob_sidecar(blob_sidecar);
887 }
888
889 if !active_delegations.is_empty() {
891 for auth in &active_delegations {
892 let Ok(authority) = auth.recover_authority() else {
893 continue;
894 };
895 if authority == broadcast.new_origin {
896 account.info.nonce += 1;
899 }
900 }
901 tx_req.authorization_list = Some(active_delegations);
902 }
903
904 self.broadcastable_transactions.push_back(BroadcastableTransaction {
905 rpc: ecx.journaled_state.database.active_fork_url(),
906 transaction: tx_req.into(),
907 });
908 debug!(target: "cheatcodes", tx=?self.broadcastable_transactions.back().unwrap(), "broadcastable call");
909
910 if !self.config.evm_opts.isolate {
912 let prev = account.info.nonce;
913 account.info.nonce += 1;
914 debug!(target: "cheatcodes", address=%broadcast.new_origin, nonce=prev+1, prev, "incremented nonce");
915 }
916 } else if broadcast.single_call {
917 let msg = "`staticcall`s are not allowed after `broadcast`; use `startBroadcast` instead";
918 return Some(CallOutcome {
919 result: InterpreterResult {
920 result: InstructionResult::Revert,
921 output: Error::encode(msg),
922 gas,
923 },
924 memory_offset: call.return_memory_offset.clone(),
925 });
926 }
927 }
928 }
929
930 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
932 let initialized;
935 let old_balance;
936 let old_nonce;
937 if let Ok(acc) = ecx.journaled_state.load_account(call.target_address) {
938 initialized = acc.info.exists();
939 old_balance = acc.info.balance;
940 old_nonce = acc.info.nonce;
941 } else {
942 initialized = false;
943 old_balance = U256::ZERO;
944 old_nonce = 0;
945 }
946 let kind = match call.scheme {
947 CallScheme::Call => crate::Vm::AccountAccessKind::Call,
948 CallScheme::CallCode => crate::Vm::AccountAccessKind::CallCode,
949 CallScheme::DelegateCall => crate::Vm::AccountAccessKind::DelegateCall,
950 CallScheme::StaticCall => crate::Vm::AccountAccessKind::StaticCall,
951 };
952 recorded_account_diffs_stack.push(vec![AccountAccess {
958 chainInfo: crate::Vm::ChainInfo {
959 forkId: ecx.journaled_state.db().active_fork_id().unwrap_or_default(),
960 chainId: U256::from(ecx.cfg.chain_id),
961 },
962 accessor: call.caller,
963 account: call.bytecode_address,
964 kind,
965 initialized,
966 oldBalance: old_balance,
967 newBalance: U256::ZERO, oldNonce: old_nonce,
969 newNonce: 0, value: call.call_value(),
971 data: call.input.bytes(ecx),
972 reverted: false,
973 deployedCode: Bytes::new(),
974 storageAccesses: vec![], depth: ecx
976 .journaled_state
977 .depth()
978 .try_into()
979 .expect("journaled state depth exceeds u64"),
980 }]);
981 }
982
983 None
984 }
985
986 pub fn rng(&mut self) -> &mut impl Rng {
987 self.test_runner().rng()
988 }
989
990 pub fn test_runner(&mut self) -> &mut TestRunner {
991 self.test_runner.get_or_insert_with(|| match self.config.seed {
992 Some(seed) => TestRunner::new_with_rng(
993 proptest::test_runner::Config::default(),
994 TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>()),
995 ),
996 None => TestRunner::new(proptest::test_runner::Config::default()),
997 })
998 }
999
1000 pub fn set_seed(&mut self, seed: U256) {
1001 self.test_runner = Some(TestRunner::new_with_rng(
1002 proptest::test_runner::Config::default(),
1003 TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>()),
1004 ));
1005 }
1006
1007 pub fn arbitrary_storage(&mut self) -> &mut ArbitraryStorage {
1010 self.arbitrary_storage.get_or_insert_with(ArbitraryStorage::default)
1011 }
1012
1013 pub fn has_arbitrary_storage(&self, address: &Address) -> bool {
1015 match &self.arbitrary_storage {
1016 Some(storage) => storage.values.contains_key(address),
1017 None => false,
1018 }
1019 }
1020
1021 pub fn should_overwrite_arbitrary_storage(
1025 &self,
1026 address: &Address,
1027 storage_slot: U256,
1028 ) -> bool {
1029 match &self.arbitrary_storage {
1030 Some(storage) => {
1031 storage.overwrites.contains(address)
1032 && storage
1033 .values
1034 .get(address)
1035 .and_then(|arbitrary_values| arbitrary_values.get(&storage_slot))
1036 .is_none()
1037 }
1038 None => false,
1039 }
1040 }
1041
1042 pub fn is_arbitrary_storage_copy(&self, address: &Address) -> bool {
1044 match &self.arbitrary_storage {
1045 Some(storage) => storage.copies.contains_key(address),
1046 None => false,
1047 }
1048 }
1049}
1050
1051impl Inspector<EthEvmContext<&mut dyn DatabaseExt>> for Cheatcodes {
1052 fn initialize_interp(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1053 if let Some(block) = self.block.take() {
1056 ecx.block = block;
1057 }
1058 if let Some(gas_price) = self.gas_price.take() {
1059 ecx.tx.gas_price = gas_price;
1060 }
1061
1062 if self.gas_metering.paused {
1064 self.gas_metering.paused_frames.push(interpreter.gas);
1065 }
1066
1067 if let Some(expected) = &mut self.expected_revert {
1069 expected.max_depth = max(ecx.journaled_state.depth(), expected.max_depth);
1070 }
1071 }
1072
1073 fn step(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1074 self.pc = interpreter.bytecode.pc();
1075
1076 if self.gas_metering.paused {
1078 self.meter_gas(interpreter);
1079 }
1080
1081 if self.gas_metering.reset {
1083 self.meter_gas_reset(interpreter);
1084 }
1085
1086 if self.recording_accesses {
1088 self.record_accesses(interpreter);
1089 }
1090
1091 if self.recorded_account_diffs_stack.is_some() {
1093 self.record_state_diffs(interpreter, ecx);
1094 }
1095
1096 if !self.allowed_mem_writes.is_empty() {
1098 self.check_mem_opcodes(
1099 interpreter,
1100 ecx.journaled_state.depth().try_into().expect("journaled state depth exceeds u64"),
1101 );
1102 }
1103
1104 if let Some(mapping_slots) = &mut self.mapping_slots {
1106 mapping::step(mapping_slots, interpreter);
1107 }
1108
1109 if self.gas_metering.recording {
1111 self.meter_gas_record(interpreter, ecx);
1112 }
1113 }
1114
1115 fn step_end(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1116 if self.gas_metering.paused {
1117 self.meter_gas_end(interpreter);
1118 }
1119
1120 if self.gas_metering.touched {
1121 self.meter_gas_check(interpreter);
1122 }
1123
1124 if self.arbitrary_storage.is_some() {
1126 self.arbitrary_storage_end(interpreter, ecx);
1127 }
1128 }
1129
1130 fn log(&mut self, interpreter: &mut Interpreter, _ecx: Ecx, log: Log) {
1131 if !self.expected_emits.is_empty() {
1132 expect::handle_expect_emit(self, &log, interpreter);
1133 }
1134
1135 if let Some(storage_recorded_logs) = &mut self.recorded_logs {
1137 storage_recorded_logs.push(Vm::Log {
1138 topics: log.data.topics().to_vec(),
1139 data: log.data.data.clone(),
1140 emitter: log.address,
1141 });
1142 }
1143 }
1144
1145 fn call(&mut self, ecx: Ecx, inputs: &mut CallInputs) -> Option<CallOutcome> {
1146 Self::call_with_executor(self, ecx, inputs, &mut TransparentCheatcodesExecutor)
1147 }
1148
1149 fn call_end(&mut self, ecx: Ecx, call: &CallInputs, outcome: &mut CallOutcome) {
1150 let cheatcode_call = call.target_address == CHEATCODE_ADDRESS
1151 || call.target_address == HARDHAT_CONSOLE_ADDRESS;
1152
1153 if !cheatcode_call {
1157 let curr_depth = ecx.journaled_state.depth();
1159 if let Some(prank) = &self.get_prank(curr_depth)
1160 && curr_depth == prank.depth
1161 {
1162 ecx.tx.caller = prank.prank_origin;
1163
1164 if prank.single_call {
1166 self.pranks.remove(&curr_depth);
1167 }
1168 }
1169
1170 if let Some(broadcast) = &self.broadcast
1172 && curr_depth == broadcast.depth
1173 {
1174 ecx.tx.caller = broadcast.original_origin;
1175
1176 if broadcast.single_call {
1178 let _ = self.broadcast.take();
1179 }
1180 }
1181 }
1182
1183 if let Some(assume_no_revert) = &mut self.assume_no_revert {
1185 if outcome.result.is_revert() && assume_no_revert.reverted_by.is_none() {
1188 assume_no_revert.reverted_by = Some(call.target_address);
1189 }
1190
1191 let curr_depth = ecx.journaled_state.depth();
1193 if curr_depth <= assume_no_revert.depth && !cheatcode_call {
1194 if outcome.result.is_revert() {
1197 let assume_no_revert = std::mem::take(&mut self.assume_no_revert).unwrap();
1198 return match revert_handlers::handle_assume_no_revert(
1199 &assume_no_revert,
1200 outcome.result.result,
1201 &outcome.result.output,
1202 &self.config.available_artifacts,
1203 ) {
1204 Ok(_) => {
1207 outcome.result.output = Error::from(MAGIC_ASSUME).abi_encode().into();
1208 }
1209 Err(error) => {
1212 trace!(expected=?assume_no_revert, ?error, status=?outcome.result.result, "Expected revert mismatch");
1213 outcome.result.result = InstructionResult::Revert;
1214 outcome.result.output = error.abi_encode().into();
1215 }
1216 };
1217 } else {
1218 self.assume_no_revert = None;
1220 }
1221 }
1222 }
1223
1224 if let Some(expected_revert) = &mut self.expected_revert {
1226 if outcome.result.is_revert() {
1229 if expected_revert.reverter.is_some()
1233 && (expected_revert.reverted_by.is_none() || expected_revert.count > 1)
1234 {
1235 expected_revert.reverted_by = Some(call.target_address);
1236 }
1237 }
1238
1239 let curr_depth = ecx.journaled_state.depth();
1240 if curr_depth <= expected_revert.depth {
1241 let needs_processing = match expected_revert.kind {
1242 ExpectedRevertKind::Default => !cheatcode_call,
1243 ExpectedRevertKind::Cheatcode { pending_processing } => {
1246 cheatcode_call && !pending_processing
1247 }
1248 };
1249
1250 if needs_processing {
1251 let mut expected_revert = std::mem::take(&mut self.expected_revert).unwrap();
1252 return match revert_handlers::handle_expect_revert(
1253 cheatcode_call,
1254 false,
1255 self.config.internal_expect_revert,
1256 &expected_revert,
1257 outcome.result.result,
1258 outcome.result.output.clone(),
1259 &self.config.available_artifacts,
1260 ) {
1261 Err(error) => {
1262 trace!(expected=?expected_revert, ?error, status=?outcome.result.result, "Expected revert mismatch");
1263 outcome.result.result = InstructionResult::Revert;
1264 outcome.result.output = error.abi_encode().into();
1265 }
1266 Ok((_, retdata)) => {
1267 expected_revert.actual_count += 1;
1268 if expected_revert.actual_count < expected_revert.count {
1269 self.expected_revert = Some(expected_revert.clone());
1270 }
1271 outcome.result.result = InstructionResult::Return;
1272 outcome.result.output = retdata;
1273 }
1274 };
1275 }
1276
1277 if let ExpectedRevertKind::Cheatcode { pending_processing } =
1280 &mut self.expected_revert.as_mut().unwrap().kind
1281 {
1282 *pending_processing = false;
1283 }
1284 }
1285 }
1286
1287 if cheatcode_call {
1290 return;
1291 }
1292
1293 let gas = outcome.result.gas;
1296 self.gas_metering.last_call_gas = Some(crate::Vm::Gas {
1297 gasLimit: gas.limit(),
1298 gasTotalUsed: gas.spent(),
1299 gasMemoryUsed: 0,
1300 gasRefunded: gas.refunded(),
1301 gasRemaining: gas.remaining(),
1302 });
1303
1304 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1307 if ecx.journaled_state.depth() > 0
1309 && let Some(last_recorded_depth) = &mut recorded_account_diffs_stack.pop()
1310 {
1311 if outcome.result.is_revert() {
1314 last_recorded_depth.iter_mut().for_each(|element| {
1315 element.reverted = true;
1316 element
1317 .storageAccesses
1318 .iter_mut()
1319 .for_each(|storage_access| storage_access.reverted = true);
1320 })
1321 }
1322
1323 if let Some(call_access) = last_recorded_depth.first_mut() {
1324 let curr_depth = ecx.journaled_state.depth();
1329 if call_access.depth == curr_depth as u64
1330 && let Ok(acc) = ecx.journaled_state.load_account(call.target_address)
1331 {
1332 debug_assert!(access_is_call(call_access.kind));
1333 call_access.newBalance = acc.info.balance;
1334 call_access.newNonce = acc.info.nonce;
1335 }
1336 if let Some(last) = recorded_account_diffs_stack.last_mut() {
1341 last.append(last_recorded_depth);
1342 } else {
1343 recorded_account_diffs_stack.push(last_recorded_depth.clone());
1344 }
1345 }
1346 }
1347 }
1348
1349 let should_check_emits = self
1361 .expected_emits
1362 .iter()
1363 .any(|(expected, _)| {
1364 let curr_depth = ecx.journaled_state.depth();
1365 expected.depth == curr_depth
1366 }) &&
1367 !call.is_static;
1369 if should_check_emits {
1370 let expected_counts = self
1371 .expected_emits
1372 .iter()
1373 .filter_map(|(expected, count_map)| {
1374 let count = match expected.address {
1375 Some(emitter) => match count_map.get(&emitter) {
1376 Some(log_count) => expected
1377 .log
1378 .as_ref()
1379 .map(|l| log_count.count(l))
1380 .unwrap_or_else(|| log_count.count_unchecked()),
1381 None => 0,
1382 },
1383 None => match &expected.log {
1384 Some(log) => count_map.values().map(|logs| logs.count(log)).sum(),
1385 None => count_map.values().map(|logs| logs.count_unchecked()).sum(),
1386 },
1387 };
1388
1389 if count != expected.count { Some((expected, count)) } else { None }
1390 })
1391 .collect::<Vec<_>>();
1392
1393 if let Some((expected, _)) = self
1395 .expected_emits
1396 .iter()
1397 .find(|(expected, _)| !expected.found && expected.count > 0)
1398 {
1399 outcome.result.result = InstructionResult::Revert;
1400 let error_msg = expected.mismatch_error.as_deref().unwrap_or("log != expected log");
1401 outcome.result.output = error_msg.abi_encode().into();
1402 return;
1403 }
1404
1405 if !expected_counts.is_empty() {
1406 let msg = if outcome.result.is_ok() {
1407 let (expected, count) = expected_counts.first().unwrap();
1408 format!("log emitted {count} times, expected {}", expected.count)
1409 } else {
1410 "expected an emit, but the call reverted instead. \
1411 ensure you're testing the happy path when using `expectEmit`"
1412 .to_string()
1413 };
1414
1415 outcome.result.result = InstructionResult::Revert;
1416 outcome.result.output = Error::encode(msg);
1417 return;
1418 }
1419
1420 self.expected_emits.clear()
1424 }
1425
1426 let diag = self.fork_revert_diagnostic.take();
1429
1430 if outcome.result.is_revert()
1433 && let Some(err) = diag
1434 {
1435 outcome.result.output = Error::encode(err.to_error_msg(&self.labels));
1436 return;
1437 }
1438
1439 if let TxKind::Call(test_contract) = ecx.tx.kind {
1442 if ecx.journaled_state.db().is_forked_mode()
1445 && outcome.result.result == InstructionResult::Stop
1446 && call.target_address != test_contract
1447 {
1448 let journaled_state = ecx.journaled_state.clone();
1449 self.fork_revert_diagnostic =
1450 ecx.journaled_state.db().diagnose_revert(call.target_address, &journaled_state);
1451 }
1452 }
1453
1454 if ecx.journaled_state.depth() == 0 {
1456 if outcome.result.is_revert() {
1460 return;
1461 }
1462
1463 for (address, calldatas) in &self.expected_calls {
1468 for (calldata, (expected, actual_count)) in calldatas {
1470 let ExpectedCallData { gas, min_gas, value, count, call_type } = expected;
1472
1473 let failed = match call_type {
1474 ExpectedCallType::Count => *count != *actual_count,
1478 ExpectedCallType::NonCount => *count > *actual_count,
1483 };
1484 if failed {
1485 let expected_values = [
1486 Some(format!("data {}", hex::encode_prefixed(calldata))),
1487 value.as_ref().map(|v| format!("value {v}")),
1488 gas.map(|g| format!("gas {g}")),
1489 min_gas.map(|g| format!("minimum gas {g}")),
1490 ]
1491 .into_iter()
1492 .flatten()
1493 .join(", ");
1494 let but = if outcome.result.is_ok() {
1495 let s = if *actual_count == 1 { "" } else { "s" };
1496 format!("was called {actual_count} time{s}")
1497 } else {
1498 "the call reverted instead; \
1499 ensure you're testing the happy path when using `expectCall`"
1500 .to_string()
1501 };
1502 let s = if *count == 1 { "" } else { "s" };
1503 let msg = format!(
1504 "expected call to {address} with {expected_values} \
1505 to be called {count} time{s}, but {but}"
1506 );
1507 outcome.result.result = InstructionResult::Revert;
1508 outcome.result.output = Error::encode(msg);
1509
1510 return;
1511 }
1512 }
1513 }
1514
1515 self.expected_emits.retain(|(expected, _)| expected.count > 0 && !expected.found);
1518 if !self.expected_emits.is_empty() {
1520 let msg = if outcome.result.is_ok() {
1521 "expected an emit, but no logs were emitted afterwards. \
1522 you might have mismatched events or not enough events were emitted"
1523 } else {
1524 "expected an emit, but the call reverted instead. \
1525 ensure you're testing the happy path when using `expectEmit`"
1526 };
1527 outcome.result.result = InstructionResult::Revert;
1528 outcome.result.output = Error::encode(msg);
1529 return;
1530 }
1531
1532 if let Some(expected_create) = self.expected_creates.first() {
1534 let msg = format!(
1535 "expected {} call by address {} for bytecode {} but not found",
1536 expected_create.create_scheme,
1537 hex::encode_prefixed(expected_create.deployer),
1538 hex::encode_prefixed(&expected_create.bytecode),
1539 );
1540 outcome.result.result = InstructionResult::Revert;
1541 outcome.result.output = Error::encode(msg);
1542 }
1543 }
1544 }
1545
1546 fn create(&mut self, ecx: Ecx, mut input: &mut CreateInputs) -> Option<CreateOutcome> {
1547 let gas = Gas::new(input.gas_limit());
1548 if self.intercept_next_create_call {
1550 self.intercept_next_create_call = false;
1552
1553 let output = input.init_code();
1555
1556 return Some(CreateOutcome {
1558 result: InterpreterResult { result: InstructionResult::Revert, output, gas },
1559 address: None,
1560 });
1561 }
1562
1563 let curr_depth = ecx.journaled_state.depth();
1564
1565 if let Some(prank) = &self.get_prank(curr_depth)
1567 && curr_depth >= prank.depth
1568 && input.caller() == prank.prank_caller
1569 {
1570 let mut prank_applied = false;
1571
1572 if curr_depth == prank.depth {
1574 input.set_caller(prank.new_caller);
1575 prank_applied = true;
1576 }
1577
1578 if let Some(new_origin) = prank.new_origin {
1580 ecx.tx.caller = new_origin;
1581 prank_applied = true;
1582 }
1583
1584 if prank_applied && let Some(applied_prank) = prank.first_time_applied() {
1586 self.pranks.insert(curr_depth, applied_prank);
1587 }
1588 }
1589
1590 self.apply_accesslist(ecx);
1592
1593 if let Some(broadcast) = &self.broadcast
1595 && curr_depth >= broadcast.depth
1596 && input.caller() == broadcast.original_caller
1597 {
1598 if let Err(err) = ecx.journaled_state.load_account(broadcast.new_origin) {
1599 return Some(CreateOutcome {
1600 result: InterpreterResult {
1601 result: InstructionResult::Revert,
1602 output: Error::encode(err),
1603 gas,
1604 },
1605 address: None,
1606 });
1607 }
1608
1609 ecx.tx.caller = broadcast.new_origin;
1610
1611 if curr_depth == broadcast.depth {
1612 input.set_caller(broadcast.new_origin);
1613 let is_fixed_gas_limit = check_if_fixed_gas_limit(&ecx, input.gas_limit());
1614
1615 let account = &ecx.journaled_state.inner.state()[&broadcast.new_origin];
1616 self.broadcastable_transactions.push_back(BroadcastableTransaction {
1617 rpc: ecx.journaled_state.database.active_fork_url(),
1618 transaction: TransactionRequest {
1619 from: Some(broadcast.new_origin),
1620 to: None,
1621 value: Some(input.value()),
1622 input: TransactionInput::new(input.init_code()),
1623 nonce: Some(account.info.nonce),
1624 gas: if is_fixed_gas_limit { Some(input.gas_limit()) } else { None },
1625 ..Default::default()
1626 }
1627 .into(),
1628 });
1629
1630 input.log_debug(self, &input.scheme().unwrap_or(CreateScheme::Create));
1631 }
1632 }
1633
1634 let address = input.allow_cheatcodes(self, ecx);
1636
1637 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1639 recorded_account_diffs_stack.push(vec![AccountAccess {
1640 chainInfo: crate::Vm::ChainInfo {
1641 forkId: ecx.journaled_state.db().active_fork_id().unwrap_or_default(),
1642 chainId: U256::from(ecx.cfg.chain_id),
1643 },
1644 accessor: input.caller(),
1645 account: address,
1646 kind: crate::Vm::AccountAccessKind::Create,
1647 initialized: true,
1648 oldBalance: U256::ZERO, newBalance: U256::ZERO, oldNonce: 0, newNonce: 1, value: input.value(),
1653 data: input.init_code(),
1654 reverted: false,
1655 deployedCode: Bytes::new(), storageAccesses: vec![], depth: curr_depth as u64,
1658 }]);
1659 }
1660
1661 None
1662 }
1663
1664 fn create_end(&mut self, ecx: Ecx, call: &CreateInputs, outcome: &mut CreateOutcome) {
1665 let call = Some(call);
1666 let curr_depth = ecx.journaled_state.depth();
1667
1668 if let Some(prank) = &self.get_prank(curr_depth)
1670 && curr_depth == prank.depth
1671 {
1672 ecx.tx.caller = prank.prank_origin;
1673
1674 if prank.single_call {
1676 std::mem::take(&mut self.pranks);
1677 }
1678 }
1679
1680 if let Some(broadcast) = &self.broadcast
1682 && curr_depth == broadcast.depth
1683 {
1684 ecx.tx.caller = broadcast.original_origin;
1685
1686 if broadcast.single_call {
1688 std::mem::take(&mut self.broadcast);
1689 }
1690 }
1691
1692 if let Some(expected_revert) = &self.expected_revert
1694 && curr_depth <= expected_revert.depth
1695 && matches!(expected_revert.kind, ExpectedRevertKind::Default)
1696 {
1697 let mut expected_revert = std::mem::take(&mut self.expected_revert).unwrap();
1698 return match revert_handlers::handle_expect_revert(
1699 false,
1700 true,
1701 self.config.internal_expect_revert,
1702 &expected_revert,
1703 outcome.result.result,
1704 outcome.result.output.clone(),
1705 &self.config.available_artifacts,
1706 ) {
1707 Ok((address, retdata)) => {
1708 expected_revert.actual_count += 1;
1709 if expected_revert.actual_count < expected_revert.count {
1710 self.expected_revert = Some(expected_revert.clone());
1711 }
1712
1713 outcome.result.result = InstructionResult::Return;
1714 outcome.result.output = retdata;
1715 outcome.address = address;
1716 }
1717 Err(err) => {
1718 outcome.result.result = InstructionResult::Revert;
1719 outcome.result.output = err.abi_encode().into();
1720 }
1721 };
1722 }
1723
1724 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1727 if curr_depth > 0
1729 && let Some(last_depth) = &mut recorded_account_diffs_stack.pop()
1730 {
1731 if outcome.result.is_revert() {
1734 last_depth.iter_mut().for_each(|element| {
1735 element.reverted = true;
1736 element
1737 .storageAccesses
1738 .iter_mut()
1739 .for_each(|storage_access| storage_access.reverted = true);
1740 })
1741 }
1742
1743 if let Some(create_access) = last_depth.first_mut() {
1744 let depth = ecx.journaled_state.depth();
1749 if create_access.depth == depth as u64 {
1750 debug_assert_eq!(
1751 create_access.kind as u8,
1752 crate::Vm::AccountAccessKind::Create as u8
1753 );
1754 if let Some(address) = outcome.address
1755 && let Ok(created_acc) = ecx.journaled_state.load_account(address)
1756 {
1757 create_access.newBalance = created_acc.info.balance;
1758 create_access.newNonce = created_acc.info.nonce;
1759 create_access.deployedCode =
1760 created_acc.info.code.clone().unwrap_or_default().original_bytes();
1761 }
1762 }
1763 if let Some(last) = recorded_account_diffs_stack.last_mut() {
1768 last.append(last_depth);
1769 } else {
1770 recorded_account_diffs_stack.push(last_depth.clone());
1771 }
1772 }
1773 }
1774 }
1775
1776 if !self.expected_creates.is_empty()
1778 && let (Some(address), Some(call)) = (outcome.address, call)
1779 && let Ok(created_acc) = ecx.journaled_state.load_account(address)
1780 {
1781 let bytecode = created_acc.info.code.clone().unwrap_or_default().original_bytes();
1782 if let Some((index, _)) =
1783 self.expected_creates.iter().find_position(|expected_create| {
1784 expected_create.deployer == call.caller
1785 && expected_create.create_scheme.eq(call.scheme.into())
1786 && expected_create.bytecode == bytecode
1787 })
1788 {
1789 self.expected_creates.swap_remove(index);
1790 }
1791 }
1792 }
1793}
1794
1795impl InspectorExt for Cheatcodes {
1796 fn should_use_create2_factory(&mut self, ecx: Ecx, inputs: &CreateInputs) -> bool {
1797 if let CreateScheme::Create2 { .. } = inputs.scheme {
1798 let depth = ecx.journaled_state.depth();
1799 let target_depth = if let Some(prank) = &self.get_prank(depth) {
1800 prank.depth
1801 } else if let Some(broadcast) = &self.broadcast {
1802 broadcast.depth
1803 } else {
1804 1
1805 };
1806
1807 depth == target_depth
1808 && (self.broadcast.is_some() || self.config.always_use_create_2_factory)
1809 } else {
1810 false
1811 }
1812 }
1813
1814 fn create2_deployer(&self) -> Address {
1815 self.config.evm_opts.create2_deployer
1816 }
1817}
1818
1819impl Cheatcodes {
1820 #[cold]
1821 fn meter_gas(&mut self, interpreter: &mut Interpreter) {
1822 if let Some(paused_gas) = self.gas_metering.paused_frames.last() {
1823 let memory = *interpreter.gas.memory();
1826 interpreter.gas = *paused_gas;
1827 interpreter.gas.memory_mut().words_num = memory.words_num;
1828 interpreter.gas.memory_mut().expansion_cost = memory.expansion_cost;
1829 } else {
1830 self.gas_metering.paused_frames.push(interpreter.gas);
1832 }
1833 }
1834
1835 #[cold]
1836 fn meter_gas_record(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1837 if interpreter.bytecode.action.as_ref().and_then(|i| i.instruction_result()).is_none() {
1838 self.gas_metering.gas_records.iter_mut().for_each(|record| {
1839 let curr_depth = ecx.journaled_state.depth();
1840 if curr_depth == record.depth {
1841 if self.gas_metering.last_gas_used != 0 {
1844 let gas_diff =
1845 interpreter.gas.spent().saturating_sub(self.gas_metering.last_gas_used);
1846 record.gas_used = record.gas_used.saturating_add(gas_diff);
1847 }
1848
1849 self.gas_metering.last_gas_used = interpreter.gas.spent();
1852 }
1853 });
1854 }
1855 }
1856
1857 #[cold]
1858 fn meter_gas_end(&mut self, interpreter: &mut Interpreter) {
1859 if let Some(interpreter_action) = interpreter.bytecode.action.as_ref()
1861 && will_exit(interpreter_action)
1862 {
1863 self.gas_metering.paused_frames.pop();
1864 }
1865 }
1866
1867 #[cold]
1868 fn meter_gas_reset(&mut self, interpreter: &mut Interpreter) {
1869 interpreter.gas = Gas::new(interpreter.gas.limit());
1870 self.gas_metering.reset = false;
1871 }
1872
1873 #[cold]
1874 fn meter_gas_check(&mut self, interpreter: &mut Interpreter) {
1875 if let Some(interpreter_action) = interpreter.bytecode.action.as_ref()
1876 && will_exit(interpreter_action)
1877 {
1878 if interpreter.gas.spent()
1882 < u64::try_from(interpreter.gas.refunded()).unwrap_or_default()
1883 {
1884 interpreter.gas = Gas::new(interpreter.gas.limit());
1885 }
1886 }
1887 }
1888
1889 #[cold]
1897 fn arbitrary_storage_end(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1898 let (key, target_address) = if interpreter.bytecode.opcode() == op::SLOAD {
1899 (try_or_return!(interpreter.stack.peek(0)), interpreter.input.target_address)
1900 } else {
1901 return;
1902 };
1903
1904 let Some(value) = ecx.sload(target_address, key) else {
1905 return;
1906 };
1907
1908 if (value.is_cold && value.data.is_zero())
1909 || self.should_overwrite_arbitrary_storage(&target_address, key)
1910 {
1911 if self.has_arbitrary_storage(&target_address) {
1912 let arbitrary_value = self.rng().random();
1913 self.arbitrary_storage.as_mut().unwrap().save(
1914 ecx,
1915 target_address,
1916 key,
1917 arbitrary_value,
1918 );
1919 } else if self.is_arbitrary_storage_copy(&target_address) {
1920 let arbitrary_value = self.rng().random();
1921 self.arbitrary_storage.as_mut().unwrap().copy(
1922 ecx,
1923 target_address,
1924 key,
1925 arbitrary_value,
1926 );
1927 }
1928 }
1929 }
1930
1931 #[cold]
1933 fn record_accesses(&mut self, interpreter: &mut Interpreter) {
1934 let access = &mut self.accesses;
1935 match interpreter.bytecode.opcode() {
1936 op::SLOAD => {
1937 let key = try_or_return!(interpreter.stack.peek(0));
1938 access.record_read(interpreter.input.target_address, key);
1939 }
1940 op::SSTORE => {
1941 let key = try_or_return!(interpreter.stack.peek(0));
1942 access.record_write(interpreter.input.target_address, key);
1943 }
1944 _ => {}
1945 }
1946 }
1947
1948 #[cold]
1949 fn record_state_diffs(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1950 let Some(account_accesses) = &mut self.recorded_account_diffs_stack else { return };
1951 match interpreter.bytecode.opcode() {
1952 op::SELFDESTRUCT => {
1953 let Some(last) = account_accesses.last_mut() else { return };
1955
1956 let target = try_or_return!(interpreter.stack.peek(0));
1958 let target = Address::from_word(B256::from(target));
1959 let (initialized, old_balance, old_nonce) = ecx
1960 .journaled_state
1961 .load_account(target)
1962 .map(|account| {
1963 (account.info.exists(), account.info.balance, account.info.nonce)
1964 })
1965 .unwrap_or_default();
1966
1967 let value = ecx
1969 .balance(interpreter.input.target_address)
1970 .map(|b| b.data)
1971 .unwrap_or(U256::ZERO);
1972
1973 last.push(crate::Vm::AccountAccess {
1975 chainInfo: crate::Vm::ChainInfo {
1976 forkId: ecx.journaled_state.database.active_fork_id().unwrap_or_default(),
1977 chainId: U256::from(ecx.cfg.chain_id),
1978 },
1979 accessor: interpreter.input.target_address,
1980 account: target,
1981 kind: crate::Vm::AccountAccessKind::SelfDestruct,
1982 initialized,
1983 oldBalance: old_balance,
1984 newBalance: old_balance + value,
1985 oldNonce: old_nonce,
1986 newNonce: old_nonce, value,
1988 data: Bytes::new(),
1989 reverted: false,
1990 deployedCode: Bytes::new(),
1991 storageAccesses: vec![],
1992 depth: ecx
1993 .journaled_state
1994 .depth()
1995 .try_into()
1996 .expect("journaled state depth exceeds u64"),
1997 });
1998 }
1999
2000 op::SLOAD => {
2001 let Some(last) = account_accesses.last_mut() else { return };
2002
2003 let key = try_or_return!(interpreter.stack.peek(0));
2004 let address = interpreter.input.target_address;
2005
2006 let mut present_value = U256::ZERO;
2009 if ecx.journaled_state.load_account(address).is_ok()
2011 && let Some(previous) = ecx.sload(address, key)
2012 {
2013 present_value = previous.data;
2014 }
2015 let access = crate::Vm::StorageAccess {
2016 account: interpreter.input.target_address,
2017 slot: key.into(),
2018 isWrite: false,
2019 previousValue: present_value.into(),
2020 newValue: present_value.into(),
2021 reverted: false,
2022 };
2023 let curr_depth = ecx
2024 .journaled_state
2025 .depth()
2026 .try_into()
2027 .expect("journaled state depth exceeds u64");
2028 append_storage_access(last, access, curr_depth);
2029 }
2030 op::SSTORE => {
2031 let Some(last) = account_accesses.last_mut() else { return };
2032
2033 let key = try_or_return!(interpreter.stack.peek(0));
2034 let value = try_or_return!(interpreter.stack.peek(1));
2035 let address = interpreter.input.target_address;
2036 let mut previous_value = U256::ZERO;
2039 if ecx.journaled_state.load_account(address).is_ok()
2040 && let Some(previous) = ecx.sload(address, key)
2041 {
2042 previous_value = previous.data;
2043 }
2044
2045 let access = crate::Vm::StorageAccess {
2046 account: address,
2047 slot: key.into(),
2048 isWrite: true,
2049 previousValue: previous_value.into(),
2050 newValue: value.into(),
2051 reverted: false,
2052 };
2053 let curr_depth = ecx
2054 .journaled_state
2055 .depth()
2056 .try_into()
2057 .expect("journaled state depth exceeds u64");
2058 append_storage_access(last, access, curr_depth);
2059 }
2060
2061 op::EXTCODECOPY | op::EXTCODESIZE | op::EXTCODEHASH | op::BALANCE => {
2063 let kind = match interpreter.bytecode.opcode() {
2064 op::EXTCODECOPY => crate::Vm::AccountAccessKind::Extcodecopy,
2065 op::EXTCODESIZE => crate::Vm::AccountAccessKind::Extcodesize,
2066 op::EXTCODEHASH => crate::Vm::AccountAccessKind::Extcodehash,
2067 op::BALANCE => crate::Vm::AccountAccessKind::Balance,
2068 _ => unreachable!(),
2069 };
2070 let address =
2071 Address::from_word(B256::from(try_or_return!(interpreter.stack.peek(0))));
2072 let initialized;
2073 let balance;
2074 let nonce;
2075 if let Ok(acc) = ecx.journaled_state.load_account(address) {
2076 initialized = acc.info.exists();
2077 balance = acc.info.balance;
2078 nonce = acc.info.nonce;
2079 } else {
2080 initialized = false;
2081 balance = U256::ZERO;
2082 nonce = 0;
2083 }
2084 let curr_depth = ecx
2085 .journaled_state
2086 .depth()
2087 .try_into()
2088 .expect("journaled state depth exceeds u64");
2089 let account_access = crate::Vm::AccountAccess {
2090 chainInfo: crate::Vm::ChainInfo {
2091 forkId: ecx.journaled_state.database.active_fork_id().unwrap_or_default(),
2092 chainId: U256::from(ecx.cfg.chain_id),
2093 },
2094 accessor: interpreter.input.target_address,
2095 account: address,
2096 kind,
2097 initialized,
2098 oldBalance: balance,
2099 newBalance: balance,
2100 oldNonce: nonce,
2101 newNonce: nonce, value: U256::ZERO,
2103 data: Bytes::new(),
2104 reverted: false,
2105 deployedCode: Bytes::new(),
2106 storageAccesses: vec![],
2107 depth: curr_depth,
2108 };
2109 if let Some(last) = account_accesses.last_mut() {
2112 last.push(account_access);
2113 } else {
2114 account_accesses.push(vec![account_access]);
2115 }
2116 }
2117 _ => {}
2118 }
2119 }
2120
2121 #[cold]
2126 fn check_mem_opcodes(&self, interpreter: &mut Interpreter, depth: u64) {
2127 let Some(ranges) = self.allowed_mem_writes.get(&depth) else {
2128 return;
2129 };
2130
2131 macro_rules! mem_opcode_match {
2140 ($(($opcode:ident, $offset_depth:expr, $size_depth:expr, $writes:expr)),* $(,)?) => {
2141 match interpreter.bytecode.opcode() {
2142 op::MSTORE => {
2147 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2149
2150 if !ranges.iter().any(|range| {
2153 range.contains(&offset) && range.contains(&(offset + 31))
2154 }) {
2155 let value = try_or_return!(interpreter.stack.peek(1)).to_be_bytes::<32>();
2160 if value[..SELECTOR_LEN] == stopExpectSafeMemoryCall::SELECTOR {
2161 return
2162 }
2163
2164 disallowed_mem_write(offset, 32, interpreter, ranges);
2165 return
2166 }
2167 }
2168 op::MSTORE8 => {
2169 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2171
2172 if !ranges.iter().any(|range| range.contains(&offset)) {
2175 disallowed_mem_write(offset, 1, interpreter, ranges);
2176 return
2177 }
2178 }
2179
2180 op::MLOAD => {
2185 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2187
2188 if offset >= interpreter.memory.size() as u64 && !ranges.iter().any(|range| {
2192 range.contains(&offset) && range.contains(&(offset + 31))
2193 }) {
2194 disallowed_mem_write(offset, 32, interpreter, ranges);
2195 return
2196 }
2197 }
2198
2199 op::CALL => {
2204 let dest_offset = try_or_return!(interpreter.stack.peek(5)).saturating_to::<u64>();
2206
2207 let size = try_or_return!(interpreter.stack.peek(6)).saturating_to::<u64>();
2209
2210 let fail_cond = !ranges.iter().any(|range| {
2214 range.contains(&dest_offset) &&
2215 range.contains(&(dest_offset + size.saturating_sub(1)))
2216 });
2217
2218 if fail_cond {
2221 let to = Address::from_word(try_or_return!(interpreter.stack.peek(1)).to_be_bytes::<32>().into());
2225 if to == CHEATCODE_ADDRESS {
2226 let args_offset = try_or_return!(interpreter.stack.peek(3)).saturating_to::<usize>();
2227 let args_size = try_or_return!(interpreter.stack.peek(4)).saturating_to::<usize>();
2228 let memory_word = interpreter.memory.slice_len(args_offset, args_size);
2229 if memory_word[..SELECTOR_LEN] == stopExpectSafeMemoryCall::SELECTOR {
2230 return
2231 }
2232 }
2233
2234 disallowed_mem_write(dest_offset, size, interpreter, ranges);
2235 return
2236 }
2237 }
2238
2239 $(op::$opcode => {
2240 let dest_offset = try_or_return!(interpreter.stack.peek($offset_depth)).saturating_to::<u64>();
2242
2243 let size = try_or_return!(interpreter.stack.peek($size_depth)).saturating_to::<u64>();
2245
2246 let fail_cond = !ranges.iter().any(|range| {
2250 range.contains(&dest_offset) &&
2251 range.contains(&(dest_offset + size.saturating_sub(1)))
2252 }) && ($writes ||
2253 [dest_offset, (dest_offset + size).saturating_sub(1)].into_iter().any(|offset| {
2254 offset >= interpreter.memory.size() as u64
2255 })
2256 );
2257
2258 if fail_cond {
2261 disallowed_mem_write(dest_offset, size, interpreter, ranges);
2262 return
2263 }
2264 })*
2265
2266 _ => {}
2267 }
2268 }
2269 }
2270
2271 mem_opcode_match!(
2274 (CALLDATACOPY, 0, 2, true),
2275 (CODECOPY, 0, 2, true),
2276 (RETURNDATACOPY, 0, 2, true),
2277 (EXTCODECOPY, 1, 3, true),
2278 (CALLCODE, 5, 6, true),
2279 (STATICCALL, 4, 5, true),
2280 (DELEGATECALL, 4, 5, true),
2281 (KECCAK256, 0, 1, false),
2282 (LOG0, 0, 1, false),
2283 (LOG1, 0, 1, false),
2284 (LOG2, 0, 1, false),
2285 (LOG3, 0, 1, false),
2286 (LOG4, 0, 1, false),
2287 (CREATE, 1, 2, false),
2288 (CREATE2, 1, 2, false),
2289 (RETURN, 0, 1, false),
2290 (REVERT, 0, 1, false),
2291 );
2292 }
2293}
2294
2295fn disallowed_mem_write(
2301 dest_offset: u64,
2302 size: u64,
2303 interpreter: &mut Interpreter,
2304 ranges: &[Range<u64>],
2305) {
2306 let revert_string = format!(
2307 "memory write at offset 0x{:02X} of size 0x{:02X} not allowed; safe range: {}",
2308 dest_offset,
2309 size,
2310 ranges.iter().map(|r| format!("(0x{:02X}, 0x{:02X}]", r.start, r.end)).join(" U ")
2311 );
2312
2313 interpreter.bytecode.set_action(InterpreterAction::new_return(
2314 InstructionResult::Revert,
2315 Bytes::from(revert_string.into_bytes()),
2316 interpreter.gas,
2317 ));
2318}
2319
2320fn check_if_fixed_gas_limit(ecx: &Ecx, call_gas_limit: u64) -> bool {
2323 ecx.tx.gas_limit > ecx.block.gas_limit &&
2328 call_gas_limit <= ecx.block.gas_limit
2329 && call_gas_limit > 2300
2332}
2333
2334fn access_is_call(kind: crate::Vm::AccountAccessKind) -> bool {
2336 matches!(
2337 kind,
2338 crate::Vm::AccountAccessKind::Call
2339 | crate::Vm::AccountAccessKind::StaticCall
2340 | crate::Vm::AccountAccessKind::CallCode
2341 | crate::Vm::AccountAccessKind::DelegateCall
2342 )
2343}
2344
2345fn append_storage_access(
2347 last: &mut Vec<AccountAccess>,
2348 storage_access: crate::Vm::StorageAccess,
2349 storage_depth: u64,
2350) {
2351 if !last.is_empty() && last.first().unwrap().depth < storage_depth {
2353 if last.len() == 1 {
2359 last.first_mut().unwrap().storageAccesses.push(storage_access);
2360 } else {
2361 let last_record = last.last_mut().unwrap();
2362 if last_record.kind as u8 == crate::Vm::AccountAccessKind::Resume as u8 {
2363 last_record.storageAccesses.push(storage_access);
2364 } else {
2365 let entry = last.first().unwrap();
2366 let resume_record = crate::Vm::AccountAccess {
2367 chainInfo: crate::Vm::ChainInfo {
2368 forkId: entry.chainInfo.forkId,
2369 chainId: entry.chainInfo.chainId,
2370 },
2371 accessor: entry.accessor,
2372 account: entry.account,
2373 kind: crate::Vm::AccountAccessKind::Resume,
2374 initialized: entry.initialized,
2375 storageAccesses: vec![storage_access],
2376 reverted: entry.reverted,
2377 oldBalance: U256::ZERO,
2379 newBalance: U256::ZERO,
2380 oldNonce: 0,
2381 newNonce: 0,
2382 value: U256::ZERO,
2383 data: Bytes::new(),
2384 deployedCode: Bytes::new(),
2385 depth: entry.depth,
2386 };
2387 last.push(resume_record);
2388 }
2389 }
2390 }
2391}
2392
2393fn apply_dispatch(
2395 calls: &Vm::VmCalls,
2396 ccx: &mut CheatsCtxt,
2397 executor: &mut dyn CheatcodesExecutor,
2398) -> Result {
2399 let cheat = calls_as_dyn_cheatcode(calls);
2400
2401 let _guard = debug_span!(target: "cheatcodes", "apply", id = %cheat.id()).entered();
2402 trace!(target: "cheatcodes", ?cheat, "applying");
2403
2404 if let spec::Status::Deprecated(replacement) = *cheat.status() {
2405 ccx.state.deprecated.insert(cheat.signature(), replacement);
2406 }
2407
2408 let mut result = cheat.dyn_apply(ccx, executor);
2410
2411 if let Err(e) = &mut result
2413 && e.is_str()
2414 {
2415 let name = cheat.name();
2416 if !name.contains("assert") && name != "rpcUrl" {
2420 *e = fmt_err!("vm.{name}: {e}");
2421 }
2422 }
2423
2424 trace!(
2425 target: "cheatcodes",
2426 return = %match &result {
2427 Ok(b) => hex::encode(b),
2428 Err(e) => e.to_string(),
2429 }
2430 );
2431
2432 result
2433}
2434
2435fn calls_as_dyn_cheatcode(calls: &Vm::VmCalls) -> &dyn DynCheatcode {
2436 macro_rules! as_dyn {
2437 ($($variant:ident),*) => {
2438 match calls {
2439 $(Vm::VmCalls::$variant(cheat) => cheat,)*
2440 }
2441 };
2442 }
2443 vm_calls!(as_dyn)
2444}
2445
2446fn will_exit(action: &InterpreterAction) -> bool {
2448 match action {
2449 InterpreterAction::Return(result) => {
2450 result.result.is_ok_or_revert() || result.result.is_error()
2451 }
2452 _ => false,
2453 }
2454}