foundry_cheatcodes_spec/
cheatcode.rs

1use super::Function;
2use alloy_sol_types::SolCall;
3use serde::{Deserialize, Serialize};
4
5/// Cheatcode definition trait. Implemented by all [`Vm`](crate::Vm) functions.
6pub trait CheatcodeDef: std::fmt::Debug + Clone + SolCall {
7    /// The static cheatcode definition.
8    const CHEATCODE: &'static Cheatcode<'static>;
9}
10
11/// Specification of a single cheatcode. Extends [`Function`] with additional metadata.
12#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
13#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
14#[serde(deny_unknown_fields, rename_all = "camelCase")]
15#[non_exhaustive]
16pub struct Cheatcode<'a> {
17    // Automatically-generated fields.
18    /// The Solidity function declaration.
19    #[serde(borrow)]
20    pub func: Function<'a>,
21
22    // Manually-specified fields.
23    /// The group that the cheatcode belongs to.
24    pub group: Group,
25    /// The current status of the cheatcode. E.g. whether it is stable or experimental, etc.
26    pub status: Status<'a>,
27    /// Whether the cheatcode is safe to use inside of scripts. E.g. it does not change state in an
28    /// unexpected way.
29    pub safety: Safety,
30}
31
32/// The status of a cheatcode.
33#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
34#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
35#[serde(rename_all = "camelCase")]
36#[non_exhaustive]
37pub enum Status<'a> {
38    /// The cheatcode and its API is currently stable.
39    Stable,
40    /// The cheatcode is unstable, meaning it may contain bugs and may break its API on any
41    /// release.
42    ///
43    /// Use of experimental cheatcodes will result in a warning.
44    Experimental,
45    /// The cheatcode has been deprecated, meaning it will be removed in a future release.
46    ///
47    /// Contains the optional reason for deprecation.
48    ///
49    /// Use of deprecated cheatcodes is discouraged and will result in a warning.
50    Deprecated(Option<&'a str>),
51    /// The cheatcode has been removed and is no longer available for use.
52    ///
53    /// Use of removed cheatcodes will result in a hard error.
54    Removed,
55    /// The cheatcode is only used internally for foundry testing and may be changed or removed at
56    /// any time.
57    ///
58    /// Use of internal cheatcodes is discouraged and will result in a warning.
59    Internal,
60}
61
62/// Cheatcode groups.
63/// Initially derived and modified from inline comments in [`forge-std`'s `Vm.sol`][vmsol].
64///
65/// [vmsol]: https://github.com/foundry-rs/forge-std/blob/dcb0d52bc4399d37a6545848e3b8f9d03c77b98d/src/Vm.sol
66#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
67#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
68#[serde(rename_all = "camelCase")]
69#[non_exhaustive]
70pub enum Group {
71    /// Cheatcodes that read from, or write to the current EVM execution state.
72    ///
73    /// Examples: any of the `record` cheatcodes, `chainId`, `coinbase`.
74    ///
75    /// Safety: ambiguous, depends on whether the cheatcode is read-only or not.
76    Evm,
77    /// Cheatcodes that interact with how a test is run.
78    ///
79    /// Examples: `assume`, `skip`, `expectRevert`.
80    ///
81    /// Safety: ambiguous, depends on whether the cheatcode is read-only or not.
82    Testing,
83    /// Cheatcodes that interact with how a script is run.
84    ///
85    /// Examples: `broadcast`, `startBroadcast`, `stopBroadcast`.
86    ///
87    /// Safety: safe.
88    Scripting,
89    /// Cheatcodes that interact with the OS or filesystem.
90    ///
91    /// Examples: `ffi`, `projectRoot`, `writeFile`.
92    ///
93    /// Safety: safe.
94    Filesystem,
95    /// Cheatcodes that interact with the program's environment variables.
96    ///
97    /// Examples: `setEnv`, `envBool`, `envOr`.
98    ///
99    /// Safety: safe.
100    Environment,
101    /// Utility cheatcodes that deal with string parsing and manipulation.
102    ///
103    /// Examples: `toString`. `parseBytes`.
104    ///
105    /// Safety: safe.
106    String,
107    /// Utility cheatcodes that deal with parsing values from and converting values to JSON.
108    ///
109    /// Examples: `serializeJson`, `parseJsonUint`, `writeJson`.
110    ///
111    /// Safety: safe.
112    Json,
113    /// Utility cheatcodes that deal with parsing values from and converting values to TOML.
114    ///
115    /// Examples: `parseToml`, `writeToml`.
116    ///
117    /// Safety: safe.
118    Toml,
119    /// Cryptography-related cheatcodes.
120    ///
121    /// Examples: `sign*`.
122    ///
123    /// Safety: safe.
124    Crypto,
125    /// Generic, uncategorized utilities.
126    ///
127    /// Examples: `toString`, `parse*`, `serialize*`.
128    ///
129    /// Safety: safe.
130    Utilities,
131}
132
133impl Group {
134    /// Returns the safety of this cheatcode group.
135    ///
136    /// Some groups are inherently safe or unsafe, while others are ambiguous and will return
137    /// `None`.
138    #[inline]
139    pub const fn safety(self) -> Option<Safety> {
140        match self {
141            Self::Evm | Self::Testing => None,
142            Self::Scripting |
143            Self::Filesystem |
144            Self::Environment |
145            Self::String |
146            Self::Json |
147            Self::Toml |
148            Self::Crypto |
149            Self::Utilities => Some(Safety::Safe),
150        }
151    }
152
153    /// Returns this value as a string.
154    #[inline]
155    pub const fn as_str(self) -> &'static str {
156        match self {
157            Self::Evm => "evm",
158            Self::Testing => "testing",
159            Self::Scripting => "scripting",
160            Self::Filesystem => "filesystem",
161            Self::Environment => "environment",
162            Self::String => "string",
163            Self::Json => "json",
164            Self::Toml => "toml",
165            Self::Crypto => "crypto",
166            Self::Utilities => "utilities",
167        }
168    }
169}
170
171// TODO: Find a better name for this
172/// Cheatcode safety.
173#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
174#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
175#[serde(rename_all = "camelCase")]
176#[non_exhaustive]
177pub enum Safety {
178    /// The cheatcode is not safe to use in scripts.
179    Unsafe,
180    /// The cheatcode is safe to use in scripts.
181    #[default]
182    Safe,
183}
184
185impl Safety {
186    /// Returns this value as a string.
187    #[inline]
188    pub const fn as_str(self) -> &'static str {
189        match self {
190            Self::Safe => "safe",
191            Self::Unsafe => "unsafe",
192        }
193    }
194
195    /// Returns whether this value is safe.
196    #[inline]
197    pub const fn is_safe(self) -> bool {
198        matches!(self, Self::Safe)
199    }
200}