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_evm::eth::EthEvmContext;
19use alloy_primitives::Address;
20use foundry_evm_core::backend::DatabaseExt;
21use spec::Status;
22
23pub use Vm::ForgeContext;
24pub use config::CheatsConfig;
25pub use error::{Error, ErrorKind, Result};
26pub use inspector::{
27    BroadcastableTransaction, BroadcastableTransactions, Cheatcodes, CheatcodesExecutor,
28};
29pub use spec::{CheatcodeDef, Vm};
30
31#[macro_use]
32mod error;
33
34mod base64;
35
36mod config;
37
38mod crypto;
39
40mod version;
41
42mod env;
43pub use env::set_execution_context;
44
45mod evm;
46
47mod fs;
48
49mod inspector;
50pub use inspector::CheatcodeAnalysis;
51
52mod json;
53
54mod script;
55pub use script::{Wallets, WalletsInner};
56
57mod string;
58
59mod test;
60pub use test::expect::ExpectedCallTracker;
61
62mod toml;
63
64mod utils;
65
66/// Cheatcode implementation.
67pub(crate) trait Cheatcode: CheatcodeDef + DynCheatcode {
68    /// Applies this cheatcode to the given state.
69    ///
70    /// Implement this function if you don't need access to the EVM data.
71    fn apply(&self, state: &mut Cheatcodes) -> Result {
72        let _ = state;
73        unimplemented!("{}", Self::CHEATCODE.func.id)
74    }
75
76    /// Applies this cheatcode to the given context.
77    ///
78    /// Implement this function if you need access to the EVM data.
79    #[inline(always)]
80    fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result {
81        self.apply(ccx.state)
82    }
83
84    /// Applies this cheatcode to the given context and executor.
85    ///
86    /// Implement this function if you need access to the executor.
87    #[inline(always)]
88    fn apply_full(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result {
89        let _ = executor;
90        self.apply_stateful(ccx)
91    }
92}
93
94pub(crate) trait DynCheatcode: 'static + std::fmt::Debug {
95    fn cheatcode(&self) -> &'static spec::Cheatcode<'static>;
96
97    fn dyn_apply(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result;
98}
99
100impl<T: Cheatcode> DynCheatcode for T {
101    fn cheatcode(&self) -> &'static spec::Cheatcode<'static> {
102        Self::CHEATCODE
103    }
104
105    fn dyn_apply(&self, ccx: &mut CheatsCtxt, executor: &mut dyn CheatcodesExecutor) -> Result {
106        self.apply_full(ccx, executor)
107    }
108}
109
110impl dyn DynCheatcode {
111    pub(crate) fn name(&self) -> &'static str {
112        self.cheatcode().func.signature.split('(').next().unwrap()
113    }
114
115    pub(crate) fn id(&self) -> &'static str {
116        self.cheatcode().func.id
117    }
118
119    pub(crate) fn signature(&self) -> &'static str {
120        self.cheatcode().func.signature
121    }
122
123    pub(crate) fn status(&self) -> &Status<'static> {
124        &self.cheatcode().status
125    }
126}
127
128/// The cheatcode context, used in `Cheatcode`.
129pub struct CheatsCtxt<'cheats, 'evm, 'db, 'db2> {
130    /// The cheatcodes inspector state.
131    pub(crate) state: &'cheats mut Cheatcodes,
132    /// The EVM data.
133    pub(crate) ecx: &'evm mut EthEvmContext<&'db mut (dyn DatabaseExt + 'db2)>,
134    /// The original `msg.sender`.
135    pub(crate) caller: Address,
136    /// Gas limit of the current cheatcode call.
137    pub(crate) gas_limit: u64,
138}
139
140impl<'db, 'db2> std::ops::Deref for CheatsCtxt<'_, '_, 'db, 'db2> {
141    type Target = EthEvmContext<&'db mut (dyn DatabaseExt + 'db2)>;
142
143    #[inline(always)]
144    fn deref(&self) -> &Self::Target {
145        self.ecx
146    }
147}
148
149impl std::ops::DerefMut for CheatsCtxt<'_, '_, '_, '_> {
150    #[inline(always)]
151    fn deref_mut(&mut self) -> &mut Self::Target {
152        &mut *self.ecx
153    }
154}
155
156impl CheatsCtxt<'_, '_, '_, '_> {
157    pub(crate) fn ensure_not_precompile(&self, address: &Address) -> Result<()> {
158        if self.is_precompile(address) { Err(precompile_error(address)) } else { Ok(()) }
159    }
160
161    pub(crate) fn is_precompile(&self, address: &Address) -> bool {
162        self.ecx.journaled_state.warm_addresses.precompiles().contains(address)
163    }
164}
165
166#[cold]
167fn precompile_error(address: &Address) -> Error {
168    fmt_err!("cannot use precompile {address} as an argument")
169}