1use crate::{
4 CheatsConfig, CheatsCtxt, DynCheatcode, Error, Result,
5 Vm::{self, AccountAccess},
6 evm::{
7 DealRecord, GasRecord, RecordAccess,
8 mapping::{self, MappingSlots},
9 mock::{MockCallDataContext, MockCallReturnData},
10 prank::Prank,
11 },
12 script::{Broadcast, Wallets},
13 strategy::CheatcodeInspectorStrategy,
14 test::{
15 assume::AssumeNoRevert,
16 expect::{
17 self, ExpectedCallData, ExpectedCallTracker, ExpectedCallType, ExpectedCreate,
18 ExpectedEmitTracker, ExpectedRevert, ExpectedRevertKind,
19 },
20 revert_handlers,
21 },
22 utils::IgnoredTraces,
23};
24use alloy_consensus::BlobTransactionSidecar;
25use alloy_evm::eth::EthEvmContext;
26use alloy_primitives::{
27 Address, B256, Bytes, Log, TxKind, U256, hex,
28 map::{AddressHashMap, HashMap, HashSet},
29};
30use alloy_rpc_types::{AccessList, TransactionInput, TransactionRequest};
31use alloy_sol_types::{SolCall, SolInterface, SolValue};
32use foundry_common::{SELECTOR_LEN, TransactionMaybeSigned, evm::Breakpoints};
33use foundry_evm_core::{
34 InspectorExt,
35 abi::Vm::stopExpectSafeMemoryCall,
36 backend::{DatabaseError, DatabaseExt, RevertDiagnostic},
37 constants::{CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS, MAGIC_ASSUME},
38 evm::{FoundryEvm, new_evm_with_existing_context},
39};
40use foundry_evm_traces::TracingInspectorConfig;
41use foundry_wallets::multi_wallet::MultiWallet;
42use itertools::Itertools;
43use proptest::test_runner::{RngAlgorithm, TestRng, TestRunner};
44use rand::Rng;
45use revive_utils::TraceCollector;
46use revm::{
47 Inspector, Journal,
48 bytecode::opcode as op,
49 context::{BlockEnv, JournalTr, LocalContext, TransactionType, result::EVMError},
50 context_interface::{CreateScheme, transaction::SignedAuthorization},
51 handler::FrameResult,
52 interpreter::{
53 CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, FrameInput, Gas, Host,
54 InstructionResult, Interpreter, InterpreterAction, InterpreterResult,
55 interpreter_types::{Jumps, LoopControl, MemoryTr},
56 },
57 state::EvmStorageSlot,
58};
59use serde_json::Value;
60use std::{
61 cmp::max,
62 collections::{BTreeMap, VecDeque},
63 fs::File,
64 io::BufReader,
65 ops::Range,
66 path::PathBuf,
67 sync::Arc,
68};
69
70mod utils;
71pub use utils::CommonCreateInput;
72
73pub type Ecx<'a, 'b, 'c> = &'a mut EthEvmContext<&'b mut (dyn DatabaseExt + 'c)>;
74
75pub trait CheatcodesExecutor {
81 fn get_inspector<'a>(&'a mut self, cheats: &'a mut Cheatcodes) -> Box<dyn InspectorExt + 'a>;
84
85 fn exec_create(
87 &mut self,
88 inputs: CreateInputs,
89 ccx: &mut CheatsCtxt,
90 ) -> Result<CreateOutcome, EVMError<DatabaseError>> {
91 with_evm(self, ccx, |evm| {
92 evm.inner.ctx.journaled_state.depth += 1;
93
94 let frame = FrameInput::Create(Box::new(inputs));
95
96 let outcome = match evm.run_execution(frame)? {
97 FrameResult::Call(_) => unreachable!(),
98 FrameResult::Create(create) => create,
99 };
100
101 evm.inner.ctx.journaled_state.depth -= 1;
102
103 Ok(outcome)
104 })
105 }
106
107 fn console_log(&mut self, ccx: &mut CheatsCtxt, msg: &str) {
108 self.get_inspector(ccx.state).console_log(msg);
109 }
110
111 fn tracing_inspector(&mut self) -> Option<&mut Option<TraceCollector>> {
113 None
114 }
115
116 fn trace_revive(
117 &mut self,
118 ccx_state: &mut Cheatcodes,
119 ecx: Ecx,
120 call_traces: Box<dyn std::any::Any>,
121 ) {
122 let mut inspector = self.get_inspector(ccx_state);
123 inspector.trace_revive(ecx, call_traces, false);
124 }
125}
126
127fn with_evm<E, F, O>(
129 executor: &mut E,
130 ccx: &mut CheatsCtxt,
131 f: F,
132) -> Result<O, EVMError<DatabaseError>>
133where
134 E: CheatcodesExecutor + ?Sized,
135 F: for<'a, 'b> FnOnce(
136 &mut FoundryEvm<'a, &'b mut dyn InspectorExt>,
137 ) -> Result<O, EVMError<DatabaseError>>,
138{
139 let mut inspector = executor.get_inspector(ccx.state);
140 let error = std::mem::replace(&mut ccx.ecx.error, Ok(()));
141
142 let ctx = EthEvmContext {
143 block: ccx.ecx.block.clone(),
144 cfg: ccx.ecx.cfg.clone(),
145 tx: ccx.ecx.tx.clone(),
146 journaled_state: Journal {
147 inner: ccx.ecx.journaled_state.inner.clone(),
148 database: &mut *ccx.ecx.journaled_state.database as &mut dyn DatabaseExt,
149 },
150 local: LocalContext::default(),
151 chain: (),
152 error,
153 };
154
155 let mut evm = new_evm_with_existing_context(ctx, &mut *inspector);
156
157 let res = f(&mut evm)?;
158
159 ccx.ecx.journaled_state.inner = evm.inner.ctx.journaled_state.inner;
160 ccx.ecx.block = evm.inner.ctx.block;
161 ccx.ecx.tx = evm.inner.ctx.tx;
162 ccx.ecx.cfg = evm.inner.ctx.cfg;
163 ccx.ecx.error = evm.inner.ctx.error;
164
165 Ok(res)
166}
167
168#[derive(Debug, Default, Clone, Copy)]
171struct TransparentCheatcodesExecutor;
172
173impl CheatcodesExecutor for TransparentCheatcodesExecutor {
174 fn get_inspector<'a>(&'a mut self, cheats: &'a mut Cheatcodes) -> Box<dyn InspectorExt + 'a> {
175 Box::new(cheats)
176 }
177}
178
179macro_rules! try_or_return {
180 ($e:expr) => {
181 match $e {
182 Ok(v) => v,
183 Err(_) => return,
184 }
185 };
186}
187
188#[derive(Debug, Default)]
190pub struct TestContext {
191 pub opened_read_files: HashMap<PathBuf, BufReader<File>>,
193}
194
195impl Clone for TestContext {
197 fn clone(&self) -> Self {
198 Default::default()
199 }
200}
201
202impl TestContext {
203 #[inline]
205 pub fn clear(&mut self) {
206 self.opened_read_files.clear();
207 }
208}
209
210#[derive(Clone, Debug)]
212pub struct BroadcastableTransaction {
213 pub rpc: Option<String>,
215 pub transaction: TransactionMaybeSigned,
217}
218
219#[derive(Clone, Debug, Copy)]
220pub struct RecordDebugStepInfo {
221 pub start_node_idx: usize,
223 pub original_tracer_config: TracingInspectorConfig,
225}
226
227#[derive(Clone, Debug, Default)]
229pub struct GasMetering {
230 pub paused: bool,
232 pub touched: bool,
235 pub reset: bool,
237 pub paused_frames: Vec<Gas>,
239
240 pub active_gas_snapshot: Option<(String, String)>,
242
243 pub last_call_gas: Option<crate::Vm::Gas>,
246
247 pub recording: bool,
249 pub last_gas_used: u64,
251 pub gas_records: Vec<GasRecord>,
253}
254
255impl GasMetering {
256 pub fn start(&mut self) {
258 self.recording = true;
259 }
260
261 pub fn stop(&mut self) {
263 self.recording = false;
264 }
265
266 pub fn resume(&mut self) {
268 if self.paused {
269 self.paused = false;
270 self.touched = true;
271 }
272 self.paused_frames.clear();
273 }
274
275 pub fn reset(&mut self) {
277 self.paused = false;
278 self.touched = true;
279 self.reset = true;
280 self.paused_frames.clear();
281 }
282}
283
284#[derive(Clone, Debug, Default)]
286pub struct ArbitraryStorage {
287 pub values: HashMap<Address, HashMap<U256, U256>>,
291 pub copies: HashMap<Address, Address>,
293 pub overwrites: HashSet<Address>,
295}
296
297impl ArbitraryStorage {
298 pub fn mark_arbitrary(&mut self, address: &Address, overwrite: bool) {
300 self.values.insert(*address, HashMap::default());
301 if overwrite {
302 self.overwrites.insert(*address);
303 } else {
304 self.overwrites.remove(address);
305 }
306 }
307
308 pub fn mark_copy(&mut self, from: &Address, to: &Address) {
310 if self.values.contains_key(from) {
311 self.copies.insert(*to, *from);
312 }
313 }
314
315 pub fn save(&mut self, ecx: Ecx, address: Address, slot: U256, data: U256) {
319 self.values.get_mut(&address).expect("missing arbitrary address entry").insert(slot, data);
320 if let Ok(mut account) = ecx.journaled_state.load_account(address) {
321 account.storage.insert(slot, EvmStorageSlot::new(data, 0));
322 }
323 }
324
325 pub fn copy(&mut self, ecx: Ecx, target: Address, slot: U256, new_value: U256) -> U256 {
331 let source = self.copies.get(&target).expect("missing arbitrary copy target entry");
332 let storage_cache = self.values.get_mut(source).expect("missing arbitrary source storage");
333 let value = match storage_cache.get(&slot) {
334 Some(value) => *value,
335 None => {
336 storage_cache.insert(slot, new_value);
337 if let Ok(mut source_account) = ecx.journaled_state.load_account(*source) {
339 source_account.storage.insert(slot, EvmStorageSlot::new(new_value, 0));
340 }
341 new_value
342 }
343 };
344 if let Ok(mut target_account) = ecx.journaled_state.load_account(target) {
346 target_account.storage.insert(slot, EvmStorageSlot::new(value, 0));
347 }
348 value
349 }
350}
351
352pub type BroadcastableTransactions = VecDeque<BroadcastableTransaction>;
354
355#[derive(Clone, Debug)]
373pub struct Cheatcodes {
374 pub block: Option<BlockEnv>,
379
380 pub active_delegations: Vec<SignedAuthorization>,
384
385 pub active_blob_sidecar: Option<BlobTransactionSidecar>,
387
388 pub gas_price: Option<u128>,
393
394 pub labels: AddressHashMap<String>,
396
397 pub pranks: BTreeMap<usize, Prank>,
399
400 pub expected_revert: Option<ExpectedRevert>,
402
403 pub assume_no_revert: Option<AssumeNoRevert>,
405
406 pub fork_revert_diagnostic: Option<RevertDiagnostic>,
408
409 pub accesses: RecordAccess,
411
412 pub recording_accesses: bool,
414
415 pub recorded_account_diffs_stack: Option<Vec<Vec<AccountAccess>>>,
421
422 pub record_debug_steps_info: Option<RecordDebugStepInfo>,
424
425 pub recorded_logs: Option<Vec<crate::Vm::Log>>,
427
428 pub mocked_calls: HashMap<Address, BTreeMap<MockCallDataContext, VecDeque<MockCallReturnData>>>,
431
432 pub mocked_functions: HashMap<Address, HashMap<Bytes, Address>>,
434
435 pub expected_calls: ExpectedCallTracker,
437 pub expected_emits: ExpectedEmitTracker,
439 pub expected_creates: Vec<ExpectedCreate>,
441
442 pub allowed_mem_writes: HashMap<u64, Vec<Range<u64>>>,
444
445 pub broadcast: Option<Broadcast>,
447
448 pub broadcastable_transactions: BroadcastableTransactions,
450
451 pub access_list: Option<AccessList>,
453
454 pub config: Arc<CheatsConfig>,
456
457 pub test_context: TestContext,
459
460 pub fs_commit: bool,
463
464 pub serialized_jsons: BTreeMap<String, BTreeMap<String, Value>>,
467
468 pub eth_deals: Vec<DealRecord>,
470
471 pub gas_metering: GasMetering,
473
474 pub gas_snapshots: BTreeMap<String, BTreeMap<String, String>>,
477
478 pub mapping_slots: Option<AddressHashMap<MappingSlots>>,
480
481 pub pc: usize,
483 pub breakpoints: Breakpoints,
486
487 pub intercept_next_create_call: bool,
489
490 test_runner: Option<TestRunner>,
493
494 pub ignored_traces: IgnoredTraces,
496
497 pub arbitrary_storage: Option<ArbitraryStorage>,
499
500 pub deprecated: HashMap<&'static str, Option<&'static str>>,
502 pub wallets: Option<Wallets>,
504
505 pub strategy: CheatcodeInspectorStrategy,
507}
508
509impl Default for Cheatcodes {
513 fn default() -> Self {
514 Self::new(Arc::default())
515 }
516}
517
518impl Cheatcodes {
519 pub fn new(config: Arc<CheatsConfig>) -> Self {
521 Self {
522 strategy: config.strategy.clone(),
523 fs_commit: true,
524 labels: config.labels.clone(),
525 config,
526 block: Default::default(),
527 active_delegations: Default::default(),
528 active_blob_sidecar: Default::default(),
529 gas_price: Default::default(),
530 pranks: Default::default(),
531 expected_revert: Default::default(),
532 assume_no_revert: Default::default(),
533 fork_revert_diagnostic: Default::default(),
534 accesses: Default::default(),
535 recording_accesses: Default::default(),
536 recorded_account_diffs_stack: Default::default(),
537 recorded_logs: Default::default(),
538 record_debug_steps_info: Default::default(),
539 mocked_calls: Default::default(),
540 mocked_functions: Default::default(),
541 expected_calls: Default::default(),
542 expected_emits: Default::default(),
543 expected_creates: Default::default(),
544 allowed_mem_writes: Default::default(),
545 broadcast: Default::default(),
546 broadcastable_transactions: Default::default(),
547 access_list: Default::default(),
548 test_context: Default::default(),
549 serialized_jsons: Default::default(),
550 eth_deals: Default::default(),
551 gas_metering: Default::default(),
552 gas_snapshots: Default::default(),
553 mapping_slots: Default::default(),
554 pc: Default::default(),
555 breakpoints: Default::default(),
556 intercept_next_create_call: Default::default(),
557 test_runner: Default::default(),
558 ignored_traces: Default::default(),
559 arbitrary_storage: Default::default(),
560 deprecated: Default::default(),
561 wallets: Default::default(),
562 }
563 }
564
565 pub fn get_prank(&self, depth: usize) -> Option<&Prank> {
569 self.pranks.range(..=depth).last().map(|(_, prank)| prank)
570 }
571
572 pub fn wallets(&mut self) -> &Wallets {
574 self.wallets.get_or_insert_with(|| Wallets::new(MultiWallet::default(), None))
575 }
576
577 pub fn set_wallets(&mut self, wallets: Wallets) {
579 self.wallets = Some(wallets);
580 }
581
582 pub fn add_delegation(&mut self, authorization: SignedAuthorization) {
584 self.active_delegations.push(authorization);
585 }
586
587 fn apply_cheatcode(
589 &mut self,
590 ecx: Ecx,
591 call: &CallInputs,
592 executor: &mut dyn CheatcodesExecutor,
593 ) -> Result {
594 let decoded = Vm::VmCalls::abi_decode(&call.input.bytes(ecx)).map_err(|e| {
596 if let alloy_sol_types::Error::UnknownSelector { name: _, selector } = e {
597 let msg = format!(
598 "unknown cheatcode with selector {selector}; \
599 you may have a mismatch between the `Vm` interface (likely in `forge-std`) \
600 and the `forge` version"
601 );
602 return alloy_sol_types::Error::Other(std::borrow::Cow::Owned(msg));
603 }
604 e
605 })?;
606
607 let caller = call.caller;
608
609 ecx.journaled_state.database.ensure_cheatcode_access_forking_mode(&caller)?;
612
613 apply_dispatch(
614 &decoded,
615 &mut CheatsCtxt { state: self, ecx, gas_limit: call.gas_limit, caller },
616 executor,
617 )
618 }
619
620 fn allow_cheatcodes_on_create(&self, ecx: Ecx, caller: Address, created_address: Address) {
626 if ecx.journaled_state.depth <= 1
627 || ecx.journaled_state.database.has_cheatcode_access(&caller)
628 {
629 ecx.journaled_state.database.allow_cheatcode_access(created_address);
630 }
631 }
632
633 fn apply_accesslist(&mut self, ecx: Ecx) {
639 if let Some(access_list) = &self.access_list {
640 ecx.tx.access_list = access_list.clone();
641
642 if ecx.tx.tx_type == TransactionType::Legacy as u8 {
643 ecx.tx.tx_type = TransactionType::Eip2930 as u8;
644 }
645 }
646 }
647
648 pub fn on_revert(&mut self, ecx: Ecx) {
653 trace!(deals=?self.eth_deals.len(), "rolling back deals");
654
655 if self.expected_revert.is_some() {
657 return;
658 }
659
660 if ecx.journaled_state.depth() > 0 {
662 return;
663 }
664
665 while let Some(record) = self.eth_deals.pop() {
669 if let Some(acc) = ecx.journaled_state.state.get_mut(&record.address) {
670 acc.info.balance = record.old_balance;
671 }
672 }
673 }
674
675 pub fn create_with_executor(
676 &mut self,
677 ecx: Ecx,
678 call: &mut CreateInputs,
679 _executor: &mut impl CheatcodesExecutor,
680 ) -> Option<CreateOutcome> {
681 self.create_common(ecx, call)
682 }
683
684 fn create_common<Input>(&mut self, ecx: Ecx, mut input: Input) -> Option<CreateOutcome>
686 where
687 Input: CommonCreateInput,
688 {
689 let gas = Gas::new(input.gas_limit());
690 if self.intercept_next_create_call {
692 self.intercept_next_create_call = false;
694
695 let output = input.init_code();
697
698 return Some(CreateOutcome {
700 result: InterpreterResult { result: InstructionResult::Revert, output, gas },
701 address: None,
702 });
703 }
704
705 let curr_depth = ecx.journaled_state.depth();
706
707 if let Some(prank) = &self.get_prank(curr_depth)
709 && curr_depth >= prank.depth
710 && input.caller() == prank.prank_caller
711 {
712 let mut prank_applied = false;
713
714 if curr_depth == prank.depth {
716 input.set_caller(prank.new_caller);
717 prank_applied = true;
718 }
719
720 if let Some(new_origin) = prank.new_origin {
722 ecx.tx.caller = new_origin;
723 prank_applied = true;
724 }
725
726 if prank_applied && let Some(applied_prank) = prank.first_time_applied() {
728 self.pranks.insert(curr_depth, applied_prank);
729 }
730 }
731
732 self.apply_accesslist(ecx);
734
735 if let Some(broadcast) = &self.broadcast
737 && curr_depth >= broadcast.depth
738 && input.caller() == broadcast.original_caller
739 {
740 if let Err(err) = ecx.journaled_state.load_account(broadcast.new_origin) {
741 return Some(CreateOutcome {
742 result: InterpreterResult {
743 result: InstructionResult::Revert,
744 output: Error::encode(err),
745 gas,
746 },
747 address: None,
748 });
749 }
750
751 ecx.tx.caller = broadcast.new_origin;
752
753 if curr_depth == broadcast.depth {
754 input.set_caller(broadcast.new_origin);
755 ecx.journaled_state.touch(broadcast.new_origin);
756
757 self.strategy.runner.record_broadcastable_create_transactions(
758 self.strategy.context.as_mut(),
759 self.config.clone(),
760 &input,
761 ecx,
762 broadcast,
763 &mut self.broadcastable_transactions,
764 );
765
766 input.log_debug(self, &input.scheme().unwrap_or(CreateScheme::Create));
767 }
768 }
769
770 let address = input.allow_cheatcodes(self, ecx);
772
773 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
775 recorded_account_diffs_stack.push(vec![AccountAccess {
776 chainInfo: crate::Vm::ChainInfo {
777 forkId: ecx.journaled_state.database.active_fork_id().unwrap_or_default(),
778 chainId: U256::from(ecx.cfg.chain_id),
779 },
780 accessor: input.caller(),
781 account: address,
782 kind: crate::Vm::AccountAccessKind::Create,
783 initialized: true,
784 oldBalance: U256::ZERO, newBalance: U256::ZERO, value: input.value(),
787 data: input.init_code(),
788 reverted: false,
789 deployedCode: Bytes::new(), storageAccesses: vec![], depth: curr_depth as u64,
792 }]);
793 }
794
795 if let Some(result) = self.strategy.runner.revive_try_create(
796 self,
797 ecx,
798 &input,
799 &mut TransparentCheatcodesExecutor,
800 ) {
801 return Some(result);
802 }
803
804 None
805 }
806
807 fn create_end_common(
809 &mut self,
810 ecx: Ecx,
811 call: Option<&CreateInputs>,
812 outcome: &mut CreateOutcome,
813 ) {
814 let curr_depth = ecx.journaled_state.depth();
815
816 if let Some(prank) = &self.get_prank(curr_depth)
818 && curr_depth == prank.depth
819 {
820 ecx.tx.caller = prank.prank_origin;
821
822 if prank.single_call {
824 std::mem::take(&mut self.pranks);
825 }
826 }
827
828 if let Some(broadcast) = &self.broadcast
830 && curr_depth == broadcast.depth
831 {
832 ecx.tx.caller = broadcast.original_origin;
833
834 if broadcast.single_call {
836 std::mem::take(&mut self.broadcast);
837 }
838 }
839
840 if let Some(expected_revert) = &self.expected_revert
842 && curr_depth <= expected_revert.depth
843 && matches!(expected_revert.kind, ExpectedRevertKind::Default)
844 {
845 let mut expected_revert = std::mem::take(&mut self.expected_revert).unwrap();
846 return match revert_handlers::handle_expect_revert(
847 false,
848 true,
849 self.config.internal_expect_revert,
850 &expected_revert,
851 outcome.result.result,
852 outcome.result.output.clone(),
853 &self.config.available_artifacts,
854 ) {
855 Ok((address, retdata)) => {
856 expected_revert.actual_count += 1;
857 if expected_revert.actual_count < expected_revert.count {
858 self.expected_revert = Some(expected_revert.clone());
859 }
860
861 outcome.result.result = InstructionResult::Return;
862 outcome.result.output = retdata;
863 outcome.address = address;
864 }
865 Err(err) => {
866 outcome.result.result = InstructionResult::Revert;
867 outcome.result.output = err.abi_encode().into();
868 }
869 };
870 }
871
872 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
875 if curr_depth > 0
877 && let Some(last_depth) = &mut recorded_account_diffs_stack.pop()
878 {
879 if outcome.result.is_revert() {
882 last_depth.iter_mut().for_each(|element| {
883 element.reverted = true;
884 element
885 .storageAccesses
886 .iter_mut()
887 .for_each(|storage_access| storage_access.reverted = true);
888 })
889 }
890
891 if let Some(create_access) = last_depth.first_mut() {
892 if create_access.depth == (ecx.journaled_state.depth() as u64) {
897 debug_assert_eq!(
898 create_access.kind as u8,
899 crate::Vm::AccountAccessKind::Create as u8
900 );
901 if let Some(address) = outcome.address
902 && let Ok(created_acc) = ecx.journaled_state.load_account(address)
903 {
904 create_access.newBalance = created_acc.info.balance;
905 create_access.deployedCode =
906 created_acc.info.code.clone().unwrap_or_default().original_bytes();
907 }
908 }
909 if let Some(last) = recorded_account_diffs_stack.last_mut() {
914 last.append(last_depth);
915 } else {
916 recorded_account_diffs_stack.push(last_depth.clone());
917 }
918 }
919 }
920 }
921
922 if !self.expected_creates.is_empty()
924 && let (Some(address), Some(call)) = (outcome.address, call)
925 && let Ok(created_acc) = ecx.journaled_state.load_account(address)
926 {
927 let bytecode = created_acc.info.code.clone().unwrap_or_default().original_bytes();
928 if let Some((index, _)) =
929 self.expected_creates.iter().find_position(|expected_create| {
930 expected_create.deployer == call.caller
931 && expected_create.create_scheme.eq(call.scheme.into())
932 && expected_create.bytecode == bytecode
933 })
934 {
935 self.expected_creates.swap_remove(index);
936 }
937 }
938
939 self.strategy.runner.revive_remove_duplicate_account_access(self);
940 self.strategy.runner.revive_record_create_address(self, outcome);
941 }
942
943 pub fn is_pvm_enabled(&mut self) -> bool {
945 self.strategy.runner.is_pvm_enabled(self)
946 }
947
948 pub fn call_with_executor(
949 &mut self,
950 ecx: Ecx,
951 call: &mut CallInputs,
952 executor: &mut dyn CheatcodesExecutor,
953 ) -> Option<CallOutcome> {
954 let gas = Gas::new(call.gas_limit);
955 let curr_depth = ecx.journaled_state.depth();
956
957 if curr_depth == 0 {
961 let sender = ecx.tx.caller;
962 let account = match super::evm::journaled_account(ecx, sender) {
963 Ok(account) => account,
964 Err(err) => {
965 return Some(CallOutcome {
966 result: InterpreterResult {
967 result: InstructionResult::Revert,
968 output: err.abi_encode().into(),
969 gas,
970 },
971 memory_offset: call.return_memory_offset.clone(),
972 });
973 }
974 };
975 let prev = account.info.nonce;
976 account.info.nonce = prev.saturating_sub(1);
977
978 trace!(target: "cheatcodes", %sender, nonce=account.info.nonce, prev, "corrected nonce");
979 }
980
981 if call.target_address == CHEATCODE_ADDRESS {
982 return match self.apply_cheatcode(ecx, call, executor) {
983 Ok(retdata) => Some(CallOutcome {
984 result: InterpreterResult {
985 result: InstructionResult::Return,
986 output: retdata.into(),
987 gas,
988 },
989 memory_offset: call.return_memory_offset.clone(),
990 }),
991 Err(err) => Some(CallOutcome {
992 result: InterpreterResult {
993 result: InstructionResult::Revert,
994 output: err.abi_encode().into(),
995 gas,
996 },
997 memory_offset: call.return_memory_offset.clone(),
998 }),
999 };
1000 }
1001
1002 if call.target_address == HARDHAT_CONSOLE_ADDRESS {
1003 return None;
1004 }
1005
1006 if !self.is_pvm_enabled() {
1008 if let Some(expected_calls_for_target) =
1010 self.expected_calls.get_mut(&call.bytecode_address)
1011 {
1012 for (calldata, (expected, actual_count)) in expected_calls_for_target {
1014 if calldata.len() <= call.input.len() &&
1017 *calldata == call.input.bytes(ecx)[..calldata.len()] &&
1019 expected
1021 .value.is_none_or(|value| Some(value) == call.transfer_value()) &&
1022 expected.gas.is_none_or(|gas| gas == call.gas_limit) &&
1024 expected.min_gas.is_none_or(|min_gas| min_gas <= call.gas_limit)
1026 {
1027 *actual_count += 1;
1028 }
1029 }
1030 }
1031 }
1032
1033 if !self.is_pvm_enabled() {
1041 if let Some(mocks) = self.mocked_calls.get_mut(&call.bytecode_address) {
1043 let ctx = MockCallDataContext {
1044 calldata: call.input.bytes(ecx),
1045 value: call.transfer_value(),
1046 };
1047
1048 if let Some(return_data_queue) = match mocks.get_mut(&ctx) {
1049 Some(queue) => Some(queue),
1050 None => mocks
1051 .iter_mut()
1052 .find(|(mock, _)| {
1053 call.input.bytes(ecx).get(..mock.calldata.len())
1054 == Some(&mock.calldata[..])
1055 && mock
1056 .value
1057 .is_none_or(|value| Some(value) == call.transfer_value())
1058 })
1059 .map(|(_, v)| v),
1060 } && let Some(return_data) = if return_data_queue.len() == 1 {
1061 return_data_queue.front().map(|x| x.to_owned())
1063 } else {
1064 return_data_queue.pop_front()
1066 } {
1067 return Some(CallOutcome {
1068 result: InterpreterResult {
1069 result: return_data.ret_type,
1070 output: return_data.data,
1071 gas,
1072 },
1073 memory_offset: call.return_memory_offset.clone(),
1074 });
1075 }
1076 }
1077 }
1078 if let Some(prank) = &self.get_prank(curr_depth) {
1080 if prank.delegate_call
1082 && curr_depth == prank.depth
1083 && let CallScheme::DelegateCall = call.scheme
1084 {
1085 call.target_address = prank.new_caller;
1086 call.caller = prank.new_caller;
1087 if let Some(new_origin) = prank.new_origin {
1088 ecx.tx.caller = new_origin;
1089 }
1090 }
1091
1092 if curr_depth >= prank.depth && call.caller == prank.prank_caller {
1093 let mut prank_applied = false;
1094
1095 if curr_depth == prank.depth {
1097 call.caller = prank.new_caller;
1098 prank_applied = true;
1099 }
1100
1101 if let Some(new_origin) = prank.new_origin {
1103 ecx.tx.caller = new_origin;
1104 prank_applied = true;
1105 }
1106
1107 if prank_applied && let Some(applied_prank) = prank.first_time_applied() {
1109 self.pranks.insert(curr_depth, applied_prank);
1110 }
1111 }
1112 }
1113
1114 self.apply_accesslist(ecx);
1116
1117 if let Some(broadcast) = &self.broadcast {
1119 if curr_depth == broadcast.depth && call.caller == broadcast.original_caller {
1124 ecx.tx.caller = broadcast.new_origin;
1128
1129 call.caller = broadcast.new_origin;
1130 if !call.is_static {
1135 if let Err(err) = ecx.journaled_state.load_account(broadcast.new_origin) {
1136 return Some(CallOutcome {
1137 result: InterpreterResult {
1138 result: InstructionResult::Revert,
1139 output: Error::encode(err),
1140 gas,
1141 },
1142 memory_offset: call.return_memory_offset.clone(),
1143 });
1144 }
1145 ecx.journaled_state.touch(broadcast.new_origin);
1147
1148 let active_delegations = std::mem::take(&mut self.active_delegations);
1149 if self.active_blob_sidecar.is_some() && !active_delegations.is_empty() {
1151 let msg = "both delegation and blob are active; `attachBlob` and `attachDelegation` are not compatible";
1152 return Some(CallOutcome {
1153 result: InterpreterResult {
1154 result: InstructionResult::Revert,
1155 output: Error::encode(msg),
1156 gas,
1157 },
1158 memory_offset: call.return_memory_offset.clone(),
1159 });
1160 }
1161
1162 self.strategy.runner.record_broadcastable_call_transactions(
1163 self.strategy.context.as_mut(),
1164 self.config.clone(),
1165 call,
1166 ecx,
1167 broadcast,
1168 &mut self.broadcastable_transactions,
1169 active_delegations,
1170 self.active_blob_sidecar.take(),
1171 );
1172
1173 let account =
1174 ecx.journaled_state.inner.state().get_mut(&broadcast.new_origin).unwrap();
1175
1176 if !self.config.evm_opts.isolate {
1178 let prev = account.info.nonce;
1179 account.info.nonce += 1;
1180 debug!(target: "cheatcodes", address=%broadcast.new_origin, nonce=prev+1, prev, "incremented nonce");
1181 }
1182 } else if broadcast.single_call {
1183 let msg = "`staticcall`s are not allowed after `broadcast`; use `startBroadcast` instead";
1184 return Some(CallOutcome {
1185 result: InterpreterResult {
1186 result: InstructionResult::Revert,
1187 output: Error::encode(msg),
1188 gas,
1189 },
1190 memory_offset: call.return_memory_offset.clone(),
1191 });
1192 }
1193 }
1194 }
1195
1196 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1198 let initialized;
1201 let old_balance;
1202 if let Ok(acc) = ecx.journaled_state.load_account(call.target_address) {
1203 initialized = acc.info.exists();
1204 old_balance = acc.info.balance;
1205 } else {
1206 initialized = false;
1207 old_balance = U256::ZERO;
1208 }
1209 let kind = match call.scheme {
1210 CallScheme::Call => crate::Vm::AccountAccessKind::Call,
1211 CallScheme::CallCode => crate::Vm::AccountAccessKind::CallCode,
1212 CallScheme::DelegateCall => crate::Vm::AccountAccessKind::DelegateCall,
1213 CallScheme::StaticCall => crate::Vm::AccountAccessKind::StaticCall,
1214 };
1215 recorded_account_diffs_stack.push(vec![AccountAccess {
1221 chainInfo: crate::Vm::ChainInfo {
1222 forkId: ecx.journaled_state.db().active_fork_id().unwrap_or_default(),
1223 chainId: U256::from(ecx.cfg.chain_id),
1224 },
1225 accessor: call.caller,
1226 account: call.bytecode_address,
1227 kind,
1228 initialized,
1229 oldBalance: old_balance,
1230 newBalance: U256::ZERO, value: call.call_value(),
1232 data: call.input.bytes(ecx),
1233 reverted: false,
1234 deployedCode: Bytes::new(),
1235 storageAccesses: vec![], depth: ecx
1237 .journaled_state
1238 .depth()
1239 .try_into()
1240 .expect("journaled state depth exceeds u64"),
1241 }]);
1242 }
1243
1244 if let Some(result) = self.strategy.runner.revive_try_call(self, ecx, call, executor) {
1245 return Some(result);
1246 }
1247
1248 None
1249 }
1250
1251 pub fn rng(&mut self) -> &mut impl Rng {
1252 self.test_runner().rng()
1253 }
1254
1255 pub fn test_runner(&mut self) -> &mut TestRunner {
1256 self.test_runner.get_or_insert_with(|| match self.config.seed {
1257 Some(seed) => TestRunner::new_with_rng(
1258 proptest::test_runner::Config::default(),
1259 TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>()),
1260 ),
1261 None => TestRunner::new(proptest::test_runner::Config::default()),
1262 })
1263 }
1264
1265 pub fn set_seed(&mut self, seed: U256) {
1266 self.test_runner = Some(TestRunner::new_with_rng(
1267 proptest::test_runner::Config::default(),
1268 TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>()),
1269 ));
1270 }
1271
1272 pub fn arbitrary_storage(&mut self) -> &mut ArbitraryStorage {
1275 self.arbitrary_storage.get_or_insert_with(ArbitraryStorage::default)
1276 }
1277
1278 pub fn has_arbitrary_storage(&self, address: &Address) -> bool {
1280 match &self.arbitrary_storage {
1281 Some(storage) => storage.values.contains_key(address),
1282 None => false,
1283 }
1284 }
1285
1286 pub fn should_overwrite_arbitrary_storage(
1290 &self,
1291 address: &Address,
1292 storage_slot: U256,
1293 ) -> bool {
1294 match &self.arbitrary_storage {
1295 Some(storage) => {
1296 storage.overwrites.contains(address)
1297 && storage
1298 .values
1299 .get(address)
1300 .and_then(|arbitrary_values| arbitrary_values.get(&storage_slot))
1301 .is_none()
1302 }
1303 None => false,
1304 }
1305 }
1306
1307 pub fn is_arbitrary_storage_copy(&self, address: &Address) -> bool {
1309 match &self.arbitrary_storage {
1310 Some(storage) => storage.copies.contains_key(address),
1311 None => false,
1312 }
1313 }
1314}
1315
1316impl Inspector<EthEvmContext<&mut dyn DatabaseExt>> for Cheatcodes {
1317 #[inline]
1318 fn initialize_interp(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1319 self.strategy.runner.pre_initialize_interp(
1320 self.strategy.context.as_mut(),
1321 interpreter,
1322 ecx,
1323 );
1324
1325 if let Some(block) = self.block.take() {
1328 ecx.block = block;
1329 }
1330 if let Some(gas_price) = self.gas_price.take() {
1331 ecx.tx.gas_price = gas_price;
1332 }
1333
1334 if self.gas_metering.paused {
1336 self.gas_metering.paused_frames.push(interpreter.gas);
1337 }
1338
1339 if let Some(expected) = &mut self.expected_revert {
1341 expected.max_depth = max(ecx.journaled_state.depth(), expected.max_depth);
1342 }
1343
1344 self.strategy.runner.post_initialize_interp(
1345 self.strategy.context.as_mut(),
1346 interpreter,
1347 ecx,
1348 );
1349 }
1350
1351 fn step(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1352 self.pc = interpreter.bytecode.pc();
1353
1354 if self.gas_metering.paused {
1356 self.meter_gas(interpreter);
1357 }
1358
1359 if self.gas_metering.reset {
1361 self.meter_gas_reset(interpreter);
1362 }
1363
1364 if self.recording_accesses {
1366 self.record_accesses(interpreter);
1367 }
1368
1369 if self.recorded_account_diffs_stack.is_some() {
1371 self.record_state_diffs(interpreter, ecx);
1372 }
1373
1374 if !self.allowed_mem_writes.is_empty() {
1376 self.check_mem_opcodes(
1377 interpreter,
1378 ecx.journaled_state.depth().try_into().expect("journaled state depth exceeds u64"),
1379 );
1380 }
1381
1382 if let Some(mapping_slots) = &mut self.mapping_slots {
1384 mapping::step(mapping_slots, interpreter);
1385 }
1386
1387 if self.gas_metering.recording {
1389 self.meter_gas_record(interpreter, ecx);
1390 }
1391 }
1392
1393 fn step_end(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1394 if self.strategy.runner.pre_step_end(self.strategy.context.as_mut(), interpreter, ecx) {
1395 return;
1396 }
1397
1398 if self.gas_metering.paused {
1399 self.meter_gas_end(interpreter);
1400 }
1401
1402 if self.gas_metering.touched {
1403 self.meter_gas_check(interpreter);
1404 }
1405
1406 if self.arbitrary_storage.is_some() {
1408 self.arbitrary_storage_end(interpreter, ecx);
1409 }
1410 }
1411
1412 fn log(&mut self, interpreter: &mut Interpreter, _ecx: Ecx, log: Log) {
1413 if !self.expected_emits.is_empty() {
1414 expect::handle_expect_emit(self, &log, interpreter);
1415 }
1416
1417 if let Some(storage_recorded_logs) = &mut self.recorded_logs {
1419 storage_recorded_logs.push(Vm::Log {
1420 topics: log.data.topics().to_vec(),
1421 data: log.data.data.clone(),
1422 emitter: log.address,
1423 });
1424 }
1425 }
1426
1427 fn call(&mut self, ecx: Ecx, inputs: &mut CallInputs) -> Option<CallOutcome> {
1428 Self::call_with_executor(self, ecx, inputs, &mut TransparentCheatcodesExecutor)
1429 }
1430
1431 fn call_end(&mut self, ecx: Ecx, call: &CallInputs, outcome: &mut CallOutcome) {
1432 let cheatcode_call = call.target_address == CHEATCODE_ADDRESS
1433 || call.target_address == HARDHAT_CONSOLE_ADDRESS;
1434
1435 if !cheatcode_call {
1439 let curr_depth = ecx.journaled_state.depth();
1441 if let Some(prank) = &self.get_prank(curr_depth)
1442 && curr_depth == prank.depth
1443 {
1444 ecx.tx.caller = prank.prank_origin;
1445
1446 if prank.single_call {
1448 self.pranks.remove(&curr_depth);
1449 }
1450 }
1451
1452 if let Some(broadcast) = &self.broadcast
1454 && curr_depth == broadcast.depth
1455 {
1456 ecx.tx.caller = broadcast.original_origin;
1457
1458 if broadcast.single_call {
1460 let _ = self.broadcast.take();
1461 }
1462 }
1463 }
1464
1465 if let Some(assume_no_revert) = &mut self.assume_no_revert {
1467 if outcome.result.is_revert() && assume_no_revert.reverted_by.is_none() {
1470 assume_no_revert.reverted_by = Some(call.target_address);
1471 }
1472
1473 let curr_depth = ecx.journaled_state.depth();
1475 if curr_depth <= assume_no_revert.depth && !cheatcode_call {
1476 if outcome.result.is_revert() {
1479 let assume_no_revert = std::mem::take(&mut self.assume_no_revert).unwrap();
1480 return match revert_handlers::handle_assume_no_revert(
1481 &assume_no_revert,
1482 outcome.result.result,
1483 &outcome.result.output,
1484 &self.config.available_artifacts,
1485 ) {
1486 Ok(_) => {
1489 outcome.result.output = Error::from(MAGIC_ASSUME).abi_encode().into();
1490 }
1491 Err(error) => {
1494 trace!(expected=?assume_no_revert, ?error, status=?outcome.result.result, "Expected revert mismatch");
1495 outcome.result.result = InstructionResult::Revert;
1496 outcome.result.output = error.abi_encode().into();
1497 }
1498 };
1499 } else {
1500 self.assume_no_revert = None;
1502 }
1503 }
1504 }
1505
1506 if let Some(expected_revert) = &mut self.expected_revert {
1508 if outcome.result.is_revert() {
1511 if expected_revert.reverter.is_some()
1515 && (expected_revert.reverted_by.is_none() || expected_revert.count > 1)
1516 {
1517 expected_revert.reverted_by = Some(call.target_address);
1518 }
1519 }
1520
1521 let curr_depth = ecx.journaled_state.depth();
1522 if curr_depth <= expected_revert.depth {
1523 let needs_processing = match expected_revert.kind {
1524 ExpectedRevertKind::Default => !cheatcode_call,
1525 ExpectedRevertKind::Cheatcode { pending_processing } => {
1528 cheatcode_call && !pending_processing
1529 }
1530 };
1531
1532 if needs_processing {
1533 let mut expected_revert = std::mem::take(&mut self.expected_revert).unwrap();
1534 return match revert_handlers::handle_expect_revert(
1535 cheatcode_call,
1536 false,
1537 self.config.internal_expect_revert,
1538 &expected_revert,
1539 outcome.result.result,
1540 outcome.result.output.clone(),
1541 &self.config.available_artifacts,
1542 ) {
1543 Err(error) => {
1544 trace!(expected=?expected_revert, ?error, status=?outcome.result.result, "Expected revert mismatch");
1545 outcome.result.result = InstructionResult::Revert;
1546 outcome.result.output = error.abi_encode().into();
1547 }
1548 Ok((_, retdata)) => {
1549 expected_revert.actual_count += 1;
1550 if expected_revert.actual_count < expected_revert.count {
1551 self.expected_revert = Some(expected_revert.clone());
1552 }
1553 outcome.result.result = InstructionResult::Return;
1554 outcome.result.output = retdata;
1555 }
1556 };
1557 }
1558
1559 if let ExpectedRevertKind::Cheatcode { pending_processing } =
1562 &mut self.expected_revert.as_mut().unwrap().kind
1563 {
1564 *pending_processing = false;
1565 }
1566 }
1567 }
1568
1569 if cheatcode_call {
1572 return;
1573 }
1574
1575 let gas = outcome.result.gas;
1578 self.gas_metering.last_call_gas = Some(crate::Vm::Gas {
1579 gasLimit: gas.limit(),
1580 gasTotalUsed: gas.spent(),
1581 gasMemoryUsed: 0,
1582 gasRefunded: gas.refunded(),
1583 gasRemaining: gas.remaining(),
1584 });
1585
1586 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1589 if ecx.journaled_state.depth() > 0
1591 && let Some(last_recorded_depth) = &mut recorded_account_diffs_stack.pop()
1592 {
1593 if outcome.result.is_revert() {
1596 last_recorded_depth.iter_mut().for_each(|element| {
1597 element.reverted = true;
1598 element
1599 .storageAccesses
1600 .iter_mut()
1601 .for_each(|storage_access| storage_access.reverted = true);
1602 })
1603 }
1604
1605 if let Some(call_access) = last_recorded_depth.first_mut() {
1606 let curr_depth = ecx.journaled_state.depth();
1611 if call_access.depth == curr_depth as u64
1612 && let Ok(acc) = ecx.journaled_state.load_account(call.target_address)
1613 {
1614 debug_assert!(access_is_call(call_access.kind));
1615 call_access.newBalance = acc.info.balance;
1616 }
1617 if let Some(last) = recorded_account_diffs_stack.last_mut() {
1622 last.append(last_recorded_depth);
1623 } else {
1624 recorded_account_diffs_stack.push(last_recorded_depth.clone());
1625 }
1626 }
1627 }
1628 }
1629
1630 self.strategy.runner.revive_remove_duplicate_account_access(self);
1631
1632 let should_check_emits = self
1644 .expected_emits
1645 .iter()
1646 .any(|(expected, _)| {
1647 let curr_depth = ecx.journaled_state.depth();
1648 expected.depth == curr_depth
1649 }) &&
1650 !call.is_static;
1652 if should_check_emits {
1653 let expected_counts = self
1654 .expected_emits
1655 .iter()
1656 .filter_map(|(expected, count_map)| {
1657 let count = match expected.address {
1658 Some(emitter) => match count_map.get(&emitter) {
1659 Some(log_count) => expected
1660 .log
1661 .as_ref()
1662 .map(|l| log_count.count(l))
1663 .unwrap_or_else(|| log_count.count_unchecked()),
1664 None => 0,
1665 },
1666 None => match &expected.log {
1667 Some(log) => count_map.values().map(|logs| logs.count(log)).sum(),
1668 None => count_map.values().map(|logs| logs.count_unchecked()).sum(),
1669 },
1670 };
1671
1672 if count != expected.count { Some((expected, count)) } else { None }
1673 })
1674 .collect::<Vec<_>>();
1675
1676 if self.expected_emits.iter().any(|(expected, _)| !expected.found && expected.count > 0)
1678 {
1679 outcome.result.result = InstructionResult::Revert;
1680 outcome.result.output = "log != expected log".abi_encode().into();
1681 return;
1682 }
1683
1684 if !expected_counts.is_empty() {
1685 let msg = if outcome.result.is_ok() {
1686 let (expected, count) = expected_counts.first().unwrap();
1687 format!("log emitted {count} times, expected {}", expected.count)
1688 } else {
1689 "expected an emit, but the call reverted instead. \
1690 ensure you're testing the happy path when using `expectEmit`"
1691 .to_string()
1692 };
1693
1694 outcome.result.result = InstructionResult::Revert;
1695 outcome.result.output = Error::encode(msg);
1696 return;
1697 }
1698
1699 self.expected_emits.clear()
1703 }
1704
1705 let diag = self.fork_revert_diagnostic.take();
1708
1709 if outcome.result.is_revert()
1712 && let Some(err) = diag
1713 {
1714 outcome.result.output = Error::encode(err.to_error_msg(&self.labels));
1715 return;
1716 }
1717
1718 if let TxKind::Call(test_contract) = ecx.tx.kind {
1721 if ecx.journaled_state.db().is_forked_mode()
1724 && outcome.result.result == InstructionResult::Stop
1725 && call.target_address != test_contract
1726 {
1727 let journaled_state = ecx.journaled_state.clone();
1728 self.fork_revert_diagnostic =
1729 ecx.journaled_state.db().diagnose_revert(call.target_address, &journaled_state);
1730 }
1731 }
1732
1733 if ecx.journaled_state.depth() == 0 {
1735 if outcome.result.is_revert() {
1739 return;
1740 }
1741
1742 for (address, calldatas) in &self.expected_calls {
1747 for (calldata, (expected, actual_count)) in calldatas {
1749 let ExpectedCallData { gas, min_gas, value, count, call_type } = expected;
1751
1752 let failed = match call_type {
1753 ExpectedCallType::Count => *count != *actual_count,
1757 ExpectedCallType::NonCount => *count > *actual_count,
1762 };
1763 if failed {
1764 let expected_values = [
1765 Some(format!("data {}", hex::encode_prefixed(calldata))),
1766 value.as_ref().map(|v| format!("value {v}")),
1767 gas.map(|g| format!("gas {g}")),
1768 min_gas.map(|g| format!("minimum gas {g}")),
1769 ]
1770 .into_iter()
1771 .flatten()
1772 .join(", ");
1773 let but = if outcome.result.is_ok() {
1774 let s = if *actual_count == 1 { "" } else { "s" };
1775 format!("was called {actual_count} time{s}")
1776 } else {
1777 "the call reverted instead; \
1778 ensure you're testing the happy path when using `expectCall`"
1779 .to_string()
1780 };
1781 let s = if *count == 1 { "" } else { "s" };
1782 let msg = format!(
1783 "expected call to {address} with {expected_values} \
1784 to be called {count} time{s}, but {but}"
1785 );
1786 outcome.result.result = InstructionResult::Revert;
1787 outcome.result.output = Error::encode(msg);
1788
1789 return;
1790 }
1791 }
1792 }
1793
1794 self.expected_emits.retain(|(expected, _)| expected.count > 0 && !expected.found);
1797 if !self.expected_emits.is_empty() {
1799 let msg = if outcome.result.is_ok() {
1800 "expected an emit, but no logs were emitted afterwards. \
1801 you might have mismatched events or not enough events were emitted"
1802 } else {
1803 "expected an emit, but the call reverted instead. \
1804 ensure you're testing the happy path when using `expectEmit`"
1805 };
1806 outcome.result.result = InstructionResult::Revert;
1807 outcome.result.output = Error::encode(msg);
1808 return;
1809 }
1810
1811 if let Some(expected_create) = self.expected_creates.first() {
1813 let msg = format!(
1814 "expected {} call by address {} for bytecode {} but not found",
1815 expected_create.create_scheme,
1816 hex::encode_prefixed(expected_create.deployer),
1817 hex::encode_prefixed(&expected_create.bytecode),
1818 );
1819 outcome.result.result = InstructionResult::Revert;
1820 outcome.result.output = Error::encode(msg);
1821 }
1822 }
1823
1824 if outcome.result.is_ok() {
1825 self.strategy.runner.revive_call_end(self, ecx, call);
1826 }
1827 }
1828
1829 fn create(&mut self, ecx: Ecx, mut input: &mut CreateInputs) -> Option<CreateOutcome> {
1830 let gas = Gas::new(input.gas_limit());
1831 if self.intercept_next_create_call {
1833 self.intercept_next_create_call = false;
1835
1836 let output = input.init_code();
1838
1839 return Some(CreateOutcome {
1841 result: InterpreterResult { result: InstructionResult::Revert, output, gas },
1842 address: None,
1843 });
1844 }
1845
1846 let curr_depth = ecx.journaled_state.depth();
1847
1848 if let Some(prank) = &self.get_prank(curr_depth)
1850 && curr_depth >= prank.depth
1851 && input.caller() == prank.prank_caller
1852 {
1853 let mut prank_applied = false;
1854
1855 if curr_depth == prank.depth {
1857 input.set_caller(prank.new_caller);
1858 prank_applied = true;
1859 }
1860
1861 if let Some(new_origin) = prank.new_origin {
1863 ecx.tx.caller = new_origin;
1864 prank_applied = true;
1865 }
1866
1867 if prank_applied && let Some(applied_prank) = prank.first_time_applied() {
1869 self.pranks.insert(curr_depth, applied_prank);
1870 }
1871 }
1872
1873 self.apply_accesslist(ecx);
1875
1876 if let Some(broadcast) = &self.broadcast
1878 && curr_depth >= broadcast.depth
1879 && input.caller() == broadcast.original_caller
1880 {
1881 if let Err(err) = ecx.journaled_state.load_account(broadcast.new_origin) {
1882 return Some(CreateOutcome {
1883 result: InterpreterResult {
1884 result: InstructionResult::Revert,
1885 output: Error::encode(err),
1886 gas,
1887 },
1888 address: None,
1889 });
1890 }
1891
1892 ecx.tx.caller = broadcast.new_origin;
1893
1894 if curr_depth == broadcast.depth {
1895 input.set_caller(broadcast.new_origin);
1896 let is_fixed_gas_limit = check_if_fixed_gas_limit(&ecx, input.gas_limit());
1897 ecx.journaled_state.touch(broadcast.new_origin);
1899
1900 let account = &ecx.journaled_state.inner.state()[&broadcast.new_origin];
1901 self.broadcastable_transactions.push_back(BroadcastableTransaction {
1902 rpc: ecx.journaled_state.database.active_fork_url(),
1903 transaction: TransactionRequest {
1904 from: Some(broadcast.new_origin),
1905 to: None,
1906 value: Some(input.value()),
1907 input: TransactionInput::new(input.init_code()),
1908 nonce: Some(account.info.nonce),
1909 gas: if is_fixed_gas_limit { Some(input.gas_limit()) } else { None },
1910 ..Default::default()
1911 }
1912 .into(),
1913 });
1914
1915 input.log_debug(self, &input.scheme().unwrap_or(CreateScheme::Create));
1916 }
1917 }
1918
1919 let address = input.allow_cheatcodes(self, ecx);
1921
1922 if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1924 recorded_account_diffs_stack.push(vec![AccountAccess {
1925 chainInfo: crate::Vm::ChainInfo {
1926 forkId: ecx.journaled_state.db().active_fork_id().unwrap_or_default(),
1927 chainId: U256::from(ecx.cfg.chain_id),
1928 },
1929 accessor: input.caller(),
1930 account: address,
1931 kind: crate::Vm::AccountAccessKind::Create,
1932 initialized: true,
1933 oldBalance: U256::ZERO, newBalance: U256::ZERO, value: input.value(),
1936 data: input.init_code(),
1937 reverted: false,
1938 deployedCode: Bytes::new(), storageAccesses: vec![], depth: curr_depth as u64,
1941 }]);
1942 }
1943
1944 None
1945 }
1946
1947 fn create_end(&mut self, ecx: Ecx, call: &CreateInputs, outcome: &mut CreateOutcome) {
1948 self.create_end_common(ecx, Some(call), outcome);
1949 }
1950}
1951
1952impl InspectorExt for Cheatcodes {
1953 fn should_use_create2_factory(&mut self, ecx: Ecx, inputs: &CreateInputs) -> bool {
1954 if let CreateScheme::Create2 { .. } = inputs.scheme {
1955 let depth = ecx.journaled_state.depth();
1956 let target_depth = if let Some(prank) = &self.get_prank(depth) {
1957 prank.depth
1958 } else if let Some(broadcast) = &self.broadcast {
1959 broadcast.depth
1960 } else {
1961 1
1962 };
1963
1964 depth == target_depth
1965 && (self.broadcast.is_some() || self.config.always_use_create_2_factory)
1966 } else {
1967 false
1968 }
1969 }
1970
1971 fn create2_deployer(&self) -> Address {
1972 self.config.evm_opts.create2_deployer
1973 }
1974}
1975
1976impl Cheatcodes {
1977 #[cold]
1978 fn meter_gas(&mut self, interpreter: &mut Interpreter) {
1979 if let Some(paused_gas) = self.gas_metering.paused_frames.last() {
1980 let memory = *interpreter.gas.memory();
1983 interpreter.gas = *paused_gas;
1984 interpreter.gas.memory_mut().words_num = memory.words_num;
1985 interpreter.gas.memory_mut().expansion_cost = memory.expansion_cost;
1986 } else {
1987 self.gas_metering.paused_frames.push(interpreter.gas);
1989 }
1990 }
1991
1992 #[cold]
1993 fn meter_gas_record(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
1994 if interpreter.bytecode.action.as_ref().and_then(|i| i.instruction_result()).is_none() {
1995 self.gas_metering.gas_records.iter_mut().for_each(|record| {
1996 let curr_depth = ecx.journaled_state.depth();
1997 if curr_depth == record.depth {
1998 if self.gas_metering.last_gas_used != 0 {
2001 let gas_diff =
2002 interpreter.gas.spent().saturating_sub(self.gas_metering.last_gas_used);
2003 record.gas_used = record.gas_used.saturating_add(gas_diff);
2004 }
2005
2006 self.gas_metering.last_gas_used = interpreter.gas.spent();
2009 }
2010 });
2011 }
2012 }
2013
2014 #[cold]
2015 fn meter_gas_end(&mut self, interpreter: &mut Interpreter) {
2016 if let Some(interpreter_action) = interpreter.bytecode.action.as_ref()
2018 && will_exit(interpreter_action)
2019 {
2020 self.gas_metering.paused_frames.pop();
2021 }
2022 }
2023
2024 #[cold]
2025 fn meter_gas_reset(&mut self, interpreter: &mut Interpreter) {
2026 interpreter.gas = Gas::new(interpreter.gas.limit());
2027 self.gas_metering.reset = false;
2028 }
2029
2030 #[cold]
2031 fn meter_gas_check(&mut self, interpreter: &mut Interpreter) {
2032 if let Some(interpreter_action) = interpreter.bytecode.action.as_ref()
2033 && will_exit(interpreter_action)
2034 {
2035 if interpreter.gas.spent()
2039 < u64::try_from(interpreter.gas.refunded()).unwrap_or_default()
2040 {
2041 interpreter.gas = Gas::new(interpreter.gas.limit());
2042 }
2043 }
2044 }
2045
2046 #[cold]
2054 fn arbitrary_storage_end(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
2055 let (key, target_address) = if interpreter.bytecode.opcode() == op::SLOAD {
2056 (try_or_return!(interpreter.stack.peek(0)), interpreter.input.target_address)
2057 } else {
2058 return;
2059 };
2060
2061 let Some(value) = ecx.sload(target_address, key) else {
2062 return;
2063 };
2064
2065 if (value.is_cold && value.data.is_zero())
2066 || self.should_overwrite_arbitrary_storage(&target_address, key)
2067 {
2068 if self.has_arbitrary_storage(&target_address) {
2069 let arbitrary_value = self.rng().random();
2070 self.arbitrary_storage.as_mut().unwrap().save(
2071 ecx,
2072 target_address,
2073 key,
2074 arbitrary_value,
2075 );
2076 } else if self.is_arbitrary_storage_copy(&target_address) {
2077 let arbitrary_value = self.rng().random();
2078 self.arbitrary_storage.as_mut().unwrap().copy(
2079 ecx,
2080 target_address,
2081 key,
2082 arbitrary_value,
2083 );
2084 }
2085 }
2086 }
2087
2088 #[cold]
2090 fn record_accesses(&mut self, interpreter: &mut Interpreter) {
2091 let access = &mut self.accesses;
2092 match interpreter.bytecode.opcode() {
2093 op::SLOAD => {
2094 let key = try_or_return!(interpreter.stack.peek(0));
2095 access.record_read(interpreter.input.target_address, key);
2096 }
2097 op::SSTORE => {
2098 let key = try_or_return!(interpreter.stack.peek(0));
2099 access.record_write(interpreter.input.target_address, key);
2100 }
2101 _ => {}
2102 }
2103 }
2104
2105 #[cold]
2106 fn record_state_diffs(&mut self, interpreter: &mut Interpreter, ecx: Ecx) {
2107 let Some(account_accesses) = &mut self.recorded_account_diffs_stack else { return };
2108 match interpreter.bytecode.opcode() {
2109 op::SELFDESTRUCT => {
2110 let Some(last) = account_accesses.last_mut() else { return };
2112
2113 let target = try_or_return!(interpreter.stack.peek(0));
2115 let target = Address::from_word(B256::from(target));
2116 let (initialized, old_balance) = ecx
2117 .journaled_state
2118 .load_account(target)
2119 .map(|account| (account.info.exists(), account.info.balance))
2120 .unwrap_or_default();
2121
2122 let value = ecx
2124 .balance(interpreter.input.target_address)
2125 .map(|b| b.data)
2126 .unwrap_or(U256::ZERO);
2127
2128 last.push(crate::Vm::AccountAccess {
2130 chainInfo: crate::Vm::ChainInfo {
2131 forkId: ecx.journaled_state.database.active_fork_id().unwrap_or_default(),
2132 chainId: U256::from(ecx.cfg.chain_id),
2133 },
2134 accessor: interpreter.input.target_address,
2135 account: target,
2136 kind: crate::Vm::AccountAccessKind::SelfDestruct,
2137 initialized,
2138 oldBalance: old_balance,
2139 newBalance: old_balance + value,
2140 value,
2141 data: Bytes::new(),
2142 reverted: false,
2143 deployedCode: Bytes::new(),
2144 storageAccesses: vec![],
2145 depth: ecx
2146 .journaled_state
2147 .depth()
2148 .try_into()
2149 .expect("journaled state depth exceeds u64"),
2150 });
2151 }
2152
2153 op::SLOAD => {
2154 let Some(last) = account_accesses.last_mut() else { return };
2155
2156 let key = try_or_return!(interpreter.stack.peek(0));
2157 let address = interpreter.input.target_address;
2158
2159 let mut present_value = U256::ZERO;
2162 if ecx.journaled_state.load_account(address).is_ok()
2164 && let Some(previous) = ecx.sload(address, key)
2165 {
2166 present_value = previous.data;
2167 }
2168 let access = crate::Vm::StorageAccess {
2169 account: interpreter.input.target_address,
2170 slot: key.into(),
2171 isWrite: false,
2172 previousValue: present_value.into(),
2173 newValue: present_value.into(),
2174 reverted: false,
2175 };
2176 let curr_depth = ecx
2177 .journaled_state
2178 .depth()
2179 .try_into()
2180 .expect("journaled state depth exceeds u64");
2181 append_storage_access(last, access, curr_depth);
2182 }
2183 op::SSTORE => {
2184 let Some(last) = account_accesses.last_mut() else { return };
2185
2186 let key = try_or_return!(interpreter.stack.peek(0));
2187 let value = try_or_return!(interpreter.stack.peek(1));
2188 let address = interpreter.input.target_address;
2189 let mut previous_value = U256::ZERO;
2192 if ecx.journaled_state.load_account(address).is_ok()
2193 && let Some(previous) = ecx.sload(address, key)
2194 {
2195 previous_value = previous.data;
2196 }
2197
2198 let access = crate::Vm::StorageAccess {
2199 account: address,
2200 slot: key.into(),
2201 isWrite: true,
2202 previousValue: previous_value.into(),
2203 newValue: value.into(),
2204 reverted: false,
2205 };
2206 let curr_depth = ecx
2207 .journaled_state
2208 .depth()
2209 .try_into()
2210 .expect("journaled state depth exceeds u64");
2211 append_storage_access(last, access, curr_depth);
2212 }
2213
2214 op::EXTCODECOPY | op::EXTCODESIZE | op::EXTCODEHASH | op::BALANCE => {
2216 let kind = match interpreter.bytecode.opcode() {
2217 op::EXTCODECOPY => crate::Vm::AccountAccessKind::Extcodecopy,
2218 op::EXTCODESIZE => crate::Vm::AccountAccessKind::Extcodesize,
2219 op::EXTCODEHASH => crate::Vm::AccountAccessKind::Extcodehash,
2220 op::BALANCE => crate::Vm::AccountAccessKind::Balance,
2221 _ => unreachable!(),
2222 };
2223 let address =
2224 Address::from_word(B256::from(try_or_return!(interpreter.stack.peek(0))));
2225 let initialized;
2226 let balance;
2227 if let Ok(acc) = ecx.journaled_state.load_account(address) {
2228 initialized = acc.info.exists();
2229 balance = acc.info.balance;
2230 } else {
2231 initialized = false;
2232 balance = U256::ZERO;
2233 }
2234 let curr_depth = ecx
2235 .journaled_state
2236 .depth()
2237 .try_into()
2238 .expect("journaled state depth exceeds u64");
2239 let account_access = crate::Vm::AccountAccess {
2240 chainInfo: crate::Vm::ChainInfo {
2241 forkId: ecx.journaled_state.database.active_fork_id().unwrap_or_default(),
2242 chainId: U256::from(ecx.cfg.chain_id),
2243 },
2244 accessor: interpreter.input.target_address,
2245 account: address,
2246 kind,
2247 initialized,
2248 oldBalance: balance,
2249 newBalance: balance,
2250 value: U256::ZERO,
2251 data: Bytes::new(),
2252 reverted: false,
2253 deployedCode: Bytes::new(),
2254 storageAccesses: vec![],
2255 depth: curr_depth,
2256 };
2257 if let Some(last) = account_accesses.last_mut() {
2260 last.push(account_access);
2261 } else {
2262 account_accesses.push(vec![account_access]);
2263 }
2264 }
2265 _ => {}
2266 }
2267 }
2268
2269 #[cold]
2274 fn check_mem_opcodes(&self, interpreter: &mut Interpreter, depth: u64) {
2275 let Some(ranges) = self.allowed_mem_writes.get(&depth) else {
2276 return;
2277 };
2278
2279 macro_rules! mem_opcode_match {
2288 ($(($opcode:ident, $offset_depth:expr, $size_depth:expr, $writes:expr)),* $(,)?) => {
2289 match interpreter.bytecode.opcode() {
2290 op::MSTORE => {
2295 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2297
2298 if !ranges.iter().any(|range| {
2301 range.contains(&offset) && range.contains(&(offset + 31))
2302 }) {
2303 let value = try_or_return!(interpreter.stack.peek(1)).to_be_bytes::<32>();
2308 if value[..SELECTOR_LEN] == stopExpectSafeMemoryCall::SELECTOR {
2309 return
2310 }
2311
2312 disallowed_mem_write(offset, 32, interpreter, ranges);
2313 return
2314 }
2315 }
2316 op::MSTORE8 => {
2317 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2319
2320 if !ranges.iter().any(|range| range.contains(&offset)) {
2323 disallowed_mem_write(offset, 1, interpreter, ranges);
2324 return
2325 }
2326 }
2327
2328 op::MLOAD => {
2333 let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2335
2336 if offset >= interpreter.memory.size() as u64 && !ranges.iter().any(|range| {
2340 range.contains(&offset) && range.contains(&(offset + 31))
2341 }) {
2342 disallowed_mem_write(offset, 32, interpreter, ranges);
2343 return
2344 }
2345 }
2346
2347 op::CALL => {
2352 let dest_offset = try_or_return!(interpreter.stack.peek(5)).saturating_to::<u64>();
2354
2355 let size = try_or_return!(interpreter.stack.peek(6)).saturating_to::<u64>();
2357
2358 let fail_cond = !ranges.iter().any(|range| {
2362 range.contains(&dest_offset) &&
2363 range.contains(&(dest_offset + size.saturating_sub(1)))
2364 });
2365
2366 if fail_cond {
2369 let to = Address::from_word(try_or_return!(interpreter.stack.peek(1)).to_be_bytes::<32>().into());
2373 if to == CHEATCODE_ADDRESS {
2374 let args_offset = try_or_return!(interpreter.stack.peek(3)).saturating_to::<usize>();
2375 let args_size = try_or_return!(interpreter.stack.peek(4)).saturating_to::<usize>();
2376 let memory_word = interpreter.memory.slice_len(args_offset, args_size);
2377 if memory_word[..SELECTOR_LEN] == stopExpectSafeMemoryCall::SELECTOR {
2378 return
2379 }
2380 }
2381
2382 disallowed_mem_write(dest_offset, size, interpreter, ranges);
2383 return
2384 }
2385 }
2386
2387 $(op::$opcode => {
2388 let dest_offset = try_or_return!(interpreter.stack.peek($offset_depth)).saturating_to::<u64>();
2390
2391 let size = try_or_return!(interpreter.stack.peek($size_depth)).saturating_to::<u64>();
2393
2394 let fail_cond = !ranges.iter().any(|range| {
2398 range.contains(&dest_offset) &&
2399 range.contains(&(dest_offset + size.saturating_sub(1)))
2400 }) && ($writes ||
2401 [dest_offset, (dest_offset + size).saturating_sub(1)].into_iter().any(|offset| {
2402 offset >= interpreter.memory.size() as u64
2403 })
2404 );
2405
2406 if fail_cond {
2409 disallowed_mem_write(dest_offset, size, interpreter, ranges);
2410 return
2411 }
2412 })*
2413
2414 _ => {}
2415 }
2416 }
2417 }
2418
2419 mem_opcode_match!(
2422 (CALLDATACOPY, 0, 2, true),
2423 (CODECOPY, 0, 2, true),
2424 (RETURNDATACOPY, 0, 2, true),
2425 (EXTCODECOPY, 1, 3, true),
2426 (CALLCODE, 5, 6, true),
2427 (STATICCALL, 4, 5, true),
2428 (DELEGATECALL, 4, 5, true),
2429 (KECCAK256, 0, 1, false),
2430 (LOG0, 0, 1, false),
2431 (LOG1, 0, 1, false),
2432 (LOG2, 0, 1, false),
2433 (LOG3, 0, 1, false),
2434 (LOG4, 0, 1, false),
2435 (CREATE, 1, 2, false),
2436 (CREATE2, 1, 2, false),
2437 (RETURN, 0, 1, false),
2438 (REVERT, 0, 1, false),
2439 );
2440 }
2441}
2442
2443fn disallowed_mem_write(
2449 dest_offset: u64,
2450 size: u64,
2451 interpreter: &mut Interpreter,
2452 ranges: &[Range<u64>],
2453) {
2454 let revert_string = format!(
2455 "memory write at offset 0x{:02X} of size 0x{:02X} not allowed; safe range: {}",
2456 dest_offset,
2457 size,
2458 ranges.iter().map(|r| format!("(0x{:02X}, 0x{:02X}]", r.start, r.end)).join(" U ")
2459 );
2460
2461 interpreter.bytecode.set_action(InterpreterAction::new_return(
2462 InstructionResult::Revert,
2463 Bytes::from(revert_string.into_bytes()),
2464 interpreter.gas,
2465 ));
2466}
2467
2468pub fn check_if_fixed_gas_limit(ecx: &Ecx, call_gas_limit: u64) -> bool {
2471 ecx.tx.gas_limit > ecx.block.gas_limit &&
2476 call_gas_limit <= ecx.block.gas_limit
2477 && call_gas_limit > 2300
2480}
2481
2482fn access_is_call(kind: crate::Vm::AccountAccessKind) -> bool {
2484 matches!(
2485 kind,
2486 crate::Vm::AccountAccessKind::Call
2487 | crate::Vm::AccountAccessKind::StaticCall
2488 | crate::Vm::AccountAccessKind::CallCode
2489 | crate::Vm::AccountAccessKind::DelegateCall
2490 )
2491}
2492
2493fn append_storage_access(
2495 last: &mut Vec<AccountAccess>,
2496 storage_access: crate::Vm::StorageAccess,
2497 storage_depth: u64,
2498) {
2499 if !last.is_empty() && last.first().unwrap().depth < storage_depth {
2501 if last.len() == 1 {
2507 last.first_mut().unwrap().storageAccesses.push(storage_access);
2508 } else {
2509 let last_record = last.last_mut().unwrap();
2510 if last_record.kind as u8 == crate::Vm::AccountAccessKind::Resume as u8 {
2511 last_record.storageAccesses.push(storage_access);
2512 } else {
2513 let entry = last.first().unwrap();
2514 let resume_record = crate::Vm::AccountAccess {
2515 chainInfo: crate::Vm::ChainInfo {
2516 forkId: entry.chainInfo.forkId,
2517 chainId: entry.chainInfo.chainId,
2518 },
2519 accessor: entry.accessor,
2520 account: entry.account,
2521 kind: crate::Vm::AccountAccessKind::Resume,
2522 initialized: entry.initialized,
2523 storageAccesses: vec![storage_access],
2524 reverted: entry.reverted,
2525 oldBalance: U256::ZERO,
2527 newBalance: U256::ZERO,
2528 value: U256::ZERO,
2529 data: Bytes::new(),
2530 deployedCode: Bytes::new(),
2531 depth: entry.depth,
2532 };
2533 last.push(resume_record);
2534 }
2535 }
2536 }
2537}
2538
2539fn apply_dispatch(
2541 calls: &Vm::VmCalls,
2542 ccx: &mut CheatsCtxt,
2543 executor: &mut dyn CheatcodesExecutor,
2544) -> Result {
2545 let cheat = calls_as_dyn_cheatcode(calls);
2546
2547 let _guard = debug_span!(target: "cheatcodes", "apply", id = %cheat.id()).entered();
2548 trace!(target: "cheatcodes", cheat = ?cheat.as_debug(), "applying");
2549
2550 if let spec::Status::Deprecated(replacement) = *cheat.status() {
2551 ccx.state.deprecated.insert(cheat.signature(), replacement);
2552 }
2553
2554 let mut result = ccx.state.strategy.runner.apply_full(cheat, ccx, executor);
2556
2557 if let Err(e) = &mut result
2559 && e.is_str()
2560 {
2561 let name = cheat.name();
2562 if !name.contains("assert") && name != "rpcUrl" {
2566 *e = fmt_err!("vm.{name}: {e}");
2567 }
2568 }
2569
2570 trace!(
2571 target: "cheatcodes",
2572 return = %match &result {
2573 Ok(b) => hex::encode(b),
2574 Err(e) => e.to_string(),
2575 }
2576 );
2577
2578 result
2579}
2580
2581fn calls_as_dyn_cheatcode(calls: &Vm::VmCalls) -> &dyn DynCheatcode {
2582 macro_rules! as_dyn {
2583 ($($variant:ident),*) => {
2584 match calls {
2585 $(Vm::VmCalls::$variant(cheat) => cheat,)*
2586 }
2587 };
2588 }
2589 vm_calls!(as_dyn)
2590}
2591
2592fn will_exit(action: &InterpreterAction) -> bool {
2594 match action {
2595 InterpreterAction::Return(result) => {
2596 result.result.is_ok_or_revert() || result.result.is_error()
2597 }
2598 _ => false,
2599 }
2600}