foundry_cheatcodes_spec/
cheatcode.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
use super::Function;
use alloy_sol_types::SolCall;
use serde::{Deserialize, Serialize};

/// Cheatcode definition trait. Implemented by all [`Vm`](crate::Vm) functions.
pub trait CheatcodeDef: std::fmt::Debug + Clone + SolCall {
    /// The static cheatcode definition.
    const CHEATCODE: &'static Cheatcode<'static>;
}

/// Specification of a single cheatcode. Extends [`Function`] with additional metadata.
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[serde(deny_unknown_fields, rename_all = "camelCase")]
#[non_exhaustive]
pub struct Cheatcode<'a> {
    // Automatically-generated fields.
    /// The Solidity function declaration.
    #[serde(borrow)]
    pub func: Function<'a>,

    // Manually-specified fields.
    /// The group that the cheatcode belongs to.
    pub group: Group,
    /// The current status of the cheatcode. E.g. whether it is stable or experimental, etc.
    pub status: Status<'a>,
    /// Whether the cheatcode is safe to use inside of scripts. E.g. it does not change state in an
    /// unexpected way.
    pub safety: Safety,
}

/// The status of a cheatcode.
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub enum Status<'a> {
    /// The cheatcode and its API is currently stable.
    Stable,
    /// The cheatcode is unstable, meaning it may contain bugs and may break its API on any
    /// release.
    ///
    /// Use of experimental cheatcodes will result in a warning.
    Experimental,
    /// The cheatcode has been deprecated, meaning it will be removed in a future release.
    ///
    /// Contains the optional reason for deprecation.
    ///
    /// Use of deprecated cheatcodes is discouraged and will result in a warning.
    Deprecated(Option<&'a str>),
    /// The cheatcode has been removed and is no longer available for use.
    ///
    /// Use of removed cheatcodes will result in a hard error.
    Removed,
    /// The cheatcode is only used internally for foundry testing and may be changed or removed at
    /// any time.
    ///
    /// Use of internal cheatcodes is discouraged and will result in a warning.
    Internal,
}

/// Cheatcode groups.
/// Initially derived and modified from inline comments in [`forge-std`'s `Vm.sol`][vmsol].
///
/// [vmsol]: https://github.com/foundry-rs/forge-std/blob/dcb0d52bc4399d37a6545848e3b8f9d03c77b98d/src/Vm.sol
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub enum Group {
    /// Cheatcodes that read from, or write to the current EVM execution state.
    ///
    /// Examples: any of the `record` cheatcodes, `chainId`, `coinbase`.
    ///
    /// Safety: ambiguous, depends on whether the cheatcode is read-only or not.
    Evm,
    /// Cheatcodes that interact with how a test is run.
    ///
    /// Examples: `assume`, `skip`, `expectRevert`.
    ///
    /// Safety: ambiguous, depends on whether the cheatcode is read-only or not.
    Testing,
    /// Cheatcodes that interact with how a script is run.
    ///
    /// Examples: `broadcast`, `startBroadcast`, `stopBroadcast`.
    ///
    /// Safety: safe.
    Scripting,
    /// Cheatcodes that interact with the OS or filesystem.
    ///
    /// Examples: `ffi`, `projectRoot`, `writeFile`.
    ///
    /// Safety: safe.
    Filesystem,
    /// Cheatcodes that interact with the program's environment variables.
    ///
    /// Examples: `setEnv`, `envBool`, `envOr`.
    ///
    /// Safety: safe.
    Environment,
    /// Utility cheatcodes that deal with string parsing and manipulation.
    ///
    /// Examples: `toString`. `parseBytes`.
    ///
    /// Safety: safe.
    String,
    /// Utility cheatcodes that deal with parsing values from and converting values to JSON.
    ///
    /// Examples: `serializeJson`, `parseJsonUint`, `writeJson`.
    ///
    /// Safety: safe.
    Json,
    /// Utility cheatcodes that deal with parsing values from and converting values to TOML.
    ///
    /// Examples: `parseToml`, `writeToml`.
    ///
    /// Safety: safe.
    Toml,
    /// Cryptography-related cheatcodes.
    ///
    /// Examples: `sign*`.
    ///
    /// Safety: safe.
    Crypto,
    /// Generic, uncategorized utilities.
    ///
    /// Examples: `toString`, `parse*`, `serialize*`.
    ///
    /// Safety: safe.
    Utilities,
}

impl Group {
    /// Returns the safety of this cheatcode group.
    ///
    /// Some groups are inherently safe or unsafe, while others are ambiguous and will return
    /// `None`.
    #[inline]
    pub const fn safety(self) -> Option<Safety> {
        match self {
            Self::Evm | Self::Testing => None,
            Self::Scripting |
            Self::Filesystem |
            Self::Environment |
            Self::String |
            Self::Json |
            Self::Toml |
            Self::Crypto |
            Self::Utilities => Some(Safety::Safe),
        }
    }

    /// Returns this value as a string.
    #[inline]
    pub const fn as_str(self) -> &'static str {
        match self {
            Self::Evm => "evm",
            Self::Testing => "testing",
            Self::Scripting => "scripting",
            Self::Filesystem => "filesystem",
            Self::Environment => "environment",
            Self::String => "string",
            Self::Json => "json",
            Self::Toml => "toml",
            Self::Crypto => "crypto",
            Self::Utilities => "utilities",
        }
    }
}

// TODO: Find a better name for this
/// Cheatcode safety.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub enum Safety {
    /// The cheatcode is not safe to use in scripts.
    Unsafe,
    /// The cheatcode is safe to use in scripts.
    #[default]
    Safe,
}

impl Safety {
    /// Returns this value as a string.
    #[inline]
    pub const fn as_str(self) -> &'static str {
        match self {
            Self::Safe => "safe",
            Self::Unsafe => "unsafe",
        }
    }

    /// Returns whether this value is safe.
    #[inline]
    pub const fn is_safe(self) -> bool {
        matches!(self, Self::Safe)
    }
}