Skip to main content

foundry_cheatcodes/
lib.rs

1//! # foundry-cheatcodes
2//!
3//! Foundry cheatcodes implementations.
4
5#![cfg_attr(not(test), warn(unused_crate_dependencies))]
6#![cfg_attr(docsrs, feature(doc_cfg))]
7#![allow(elided_lifetimes_in_paths)] // Cheats context uses 3 lifetimes
8
9#[macro_use]
10extern crate foundry_common;
11
12#[macro_use]
13pub extern crate foundry_cheatcodes_spec as spec;
14
15#[macro_use]
16extern crate tracing;
17
18use alloy_primitives::{Address, B256, U256};
19use foundry_evm_core::{
20    FoundryTransaction,
21    backend::{DatabaseExt, LocalForkId},
22    env::FoundryContextExt,
23    evm::{FoundryContextFor, FoundryEvmNetwork, SpecFor},
24    fork::CreateFork,
25};
26use revm::{
27    context::{Block, Cfg, ContextTr, JournalTr, Transaction},
28    interpreter::Gas,
29};
30
31pub use Vm::ForgeContext;
32pub use config::CheatsConfig;
33pub use error::{Error, ErrorKind, Result};
34pub use foundry_evm_core::evm::NestedEvmClosureFor;
35pub use inspector::{
36    BroadcastableTransaction, BroadcastableTransactions, Cheatcodes, CheatcodesExecutor,
37};
38pub use spec::{CheatcodeDef, Vm};
39
40#[macro_use]
41mod error;
42
43mod base64;
44
45mod config;
46
47mod crypto;
48
49mod version;
50
51mod env;
52pub use env::{current_execution_context, set_execution_context};
53
54mod evm;
55
56mod expected_emit;
57
58mod external_storage;
59
60mod fs;
61
62mod inspector;
63pub use inspector::CheatcodeAnalysis;
64
65mod json;
66
67#[cfg(feature = "monad")]
68mod monad;
69
70mod script;
71pub use script::{Wallets, WalletsInner};
72
73mod state;
74pub use state::CheatcodesCore;
75
76mod string;
77
78mod tempo;
79
80mod test;
81pub use test::{
82    expect::ExpectedCallTracker,
83    revert_handlers::{DUMMY_CALL_OUTPUT, DUMMY_CREATE_ADDRESS},
84};
85
86mod toml;
87
88mod utils;
89
90/// Cheatcode implementation.
91pub(crate) trait Cheatcode: CheatcodeDef {
92    /// Applies this cheatcode to the given state.
93    ///
94    /// Implement this function if you need cheatcode state but no access to the EVM context.
95    fn apply_context(&self, _state: &mut CheatcodesCore) -> Result {
96        self.apply_context_free()
97    }
98
99    /// Applies this cheatcode to the given context.
100    ///
101    /// Implement this function if you need access to the EVM data.
102    #[inline(always)]
103    fn apply_stateful<FEN: FoundryEvmNetwork>(&self, ccx: &mut CheatsCtxt<'_, '_, FEN>) -> Result {
104        self.apply_context(&mut ccx.state.core)
105    }
106
107    /// Applies this cheatcode to the given context and executor.
108    ///
109    /// Implement this function if you need access to the executor.
110    #[inline(always)]
111    fn apply_full<FEN: FoundryEvmNetwork>(
112        &self,
113        ccx: &mut CheatsCtxt<'_, '_, FEN>,
114        executor: &mut dyn CheatcodesExecutor<FEN>,
115    ) -> Result {
116        let _ = executor;
117        self.apply_stateful(ccx)
118    }
119
120    /// Applies this cheatcode without accessing cheatcode state or the EVM context.
121    ///
122    /// This may still read or modify external host state, such as environment variables.
123    fn apply_context_free(&self) -> Result {
124        unimplemented!("{}", Self::CHEATCODE.func.id)
125    }
126}
127
128/// The cheatcode context.
129pub struct CheatsCtxt<'a, 'db, FEN: FoundryEvmNetwork> {
130    /// The cheatcodes inspector state.
131    pub(crate) state: &'a mut Cheatcodes<FEN>,
132    /// The EVM context.
133    pub(crate) ecx: &'a mut FoundryContextFor<'db, FEN>,
134    /// The original `msg.sender`.
135    pub(crate) caller: Address,
136    /// Gas of the current cheatcode call, returned to the caller.
137    pub(crate) gas: &'a mut Gas,
138    /// Whether the current cheatcode call is static.
139    pub(crate) is_static: bool,
140}
141
142impl<FEN: FoundryEvmNetwork> CheatsCtxt<'_, '_, FEN> {
143    pub(crate) fn ensure_not_precompile(&self, address: &Address) -> Result<()> {
144        if self.is_precompile(address) { Err(precompile_error(address)) } else { Ok(()) }
145    }
146
147    pub(crate) fn is_precompile(&self, address: &Address) -> bool {
148        self.ecx.journal().precompile_addresses().contains(address)
149    }
150
151    /// Returns the current call depth.
152    #[inline]
153    pub(crate) fn depth(&self) -> usize {
154        self.ecx.journal().depth()
155    }
156
157    /// Returns the active hardfork.
158    #[inline]
159    pub(crate) fn spec(&self) -> SpecFor<FEN> {
160        self.ecx.cfg().spec()
161    }
162
163    /// Returns the chain ID.
164    #[inline]
165    pub(crate) fn chain_id(&self) -> u64 {
166        self.ecx.cfg().chain_id()
167    }
168
169    /// Returns the maximum initcode size.
170    #[inline]
171    pub(crate) fn max_initcode_size(&self) -> usize {
172        self.ecx.cfg().max_initcode_size()
173    }
174
175    /// Returns the configured contract code size limit, if any.
176    #[inline]
177    pub(crate) fn limit_contract_code_size(&self) -> Option<usize> {
178        self.ecx.cfg_env().limit_contract_code_size
179    }
180
181    /// Returns the block number.
182    #[inline]
183    pub(crate) fn block_number(&self) -> U256 {
184        self.ecx.block().number()
185    }
186
187    /// Returns the block timestamp.
188    #[inline]
189    pub(crate) fn timestamp(&self) -> U256 {
190        self.ecx.block().timestamp()
191    }
192
193    /// Returns the block base fee.
194    #[inline]
195    pub(crate) fn basefee(&self) -> u64 {
196        self.ecx.block().basefee()
197    }
198
199    /// Returns the block slot number.
200    #[inline]
201    pub(crate) fn slot_num(&self) -> u64 {
202        self.ecx.block().slot_num()
203    }
204
205    /// Returns the block blob gas price (`block.blobbasefee`), if any.
206    #[inline]
207    pub(crate) fn blob_gasprice(&self) -> Option<u128> {
208        self.ecx.block().blob_gasprice()
209    }
210
211    /// Returns the transaction caller (`tx.origin`).
212    #[inline]
213    pub(crate) fn tx_caller(&self) -> Address {
214        self.ecx.tx().caller()
215    }
216
217    /// Returns the transaction gas price.
218    #[inline]
219    pub(crate) fn tx_gas_price(&self) -> u128 {
220        self.ecx.tx().gas_price()
221    }
222
223    /// Returns the transaction type.
224    #[inline]
225    pub(crate) fn tx_type(&self) -> u8 {
226        self.ecx.tx().tx_type()
227    }
228
229    /// Returns the transaction blob versioned hashes.
230    #[inline]
231    pub(crate) fn tx_blob_hashes(&self) -> &[B256] {
232        self.ecx.tx().blob_versioned_hashes()
233    }
234
235    /// Returns the transaction fee token, if any.
236    #[inline]
237    pub(crate) fn tx_fee_token(&self) -> Option<Address> {
238        self.ecx.tx().fee_token()
239    }
240
241    /// Returns the active fork ID, if any.
242    #[inline]
243    pub(crate) fn active_fork_id(&self) -> Option<LocalForkId> {
244        self.ecx.db().active_fork_id()
245    }
246
247    /// Returns the active fork URL, if any.
248    #[inline]
249    pub(crate) fn active_fork_url(&self) -> Option<String> {
250        self.ecx.db().active_fork_url()
251    }
252
253    /// Returns the active fork block number, if any.
254    #[inline]
255    pub(crate) fn active_fork_block_number(&self) -> Option<u64> {
256        self.ecx.db().active_fork_block_number()
257    }
258
259    /// Returns the active fork options, if any.
260    #[inline]
261    pub(crate) fn active_fork_options(&self) -> Option<CreateFork> {
262        self.ecx.db().active_fork_options()
263    }
264
265    /// Returns whether a fork is active.
266    #[inline]
267    pub(crate) fn is_forked_mode(&self) -> bool {
268        self.ecx.db().is_forked_mode()
269    }
270
271    /// Returns whether `account` persists across forks.
272    #[inline]
273    pub(crate) fn is_persistent(&self, account: &Address) -> bool {
274        self.ecx.db().is_persistent(account)
275    }
276}
277
278#[cold]
279fn precompile_error(address: &Address) -> Error {
280    fmt_err!("cannot use precompile {address} as an argument")
281}