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}