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