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