foundry_cheatcodes/
error.rs
1use crate::Vm;
2use alloy_primitives::{hex, Address, Bytes};
3use alloy_signer::Error as SignerError;
4use alloy_signer_local::LocalSignerError;
5use alloy_sol_types::SolError;
6use foundry_common::errors::FsPathError;
7use foundry_config::UnresolvedEnvVarError;
8use foundry_evm_core::backend::{BackendError, DatabaseError};
9use foundry_wallets::error::WalletSignerError;
10use k256::ecdsa::signature::Error as SignatureError;
11use revm::primitives::EVMError;
12use std::{borrow::Cow, fmt};
13
14pub type Result<T = Vec<u8>, E = Error> = std::result::Result<T, E>;
18
19macro_rules! fmt_err {
20 ($msg:literal $(,)?) => {
21 $crate::Error::fmt(::std::format_args!($msg))
22 };
23 ($err:expr $(,)?) => {
24 <$crate::Error as ::std::convert::From<_>>::from($err)
25 };
26 ($fmt:expr, $($arg:tt)*) => {
27 $crate::Error::fmt(::std::format_args!($fmt, $($arg)*))
28 };
29}
30
31macro_rules! bail {
32 ($msg:literal $(,)?) => {
33 return ::std::result::Result::Err(fmt_err!($msg))
34 };
35 ($err:expr $(,)?) => {
36 return ::std::result::Result::Err(fmt_err!($err))
37 };
38 ($fmt:expr, $($arg:tt)*) => {
39 return ::std::result::Result::Err(fmt_err!($fmt, $($arg)*))
40 };
41}
42
43macro_rules! ensure {
44 ($cond:expr $(,)?) => {
45 if !$cond {
46 return ::std::result::Result::Err($crate::Error::custom(
47 ::std::concat!("Condition failed: `", ::std::stringify!($cond), "`")
48 ));
49 }
50 };
51 ($cond:expr, $msg:literal $(,)?) => {
52 if !$cond {
53 return ::std::result::Result::Err(fmt_err!($msg));
54 }
55 };
56 ($cond:expr, $err:expr $(,)?) => {
57 if !$cond {
58 return ::std::result::Result::Err(fmt_err!($err));
59 }
60 };
61 ($cond:expr, $fmt:expr, $($arg:tt)*) => {
62 if !$cond {
63 return ::std::result::Result::Err(fmt_err!($fmt, $($arg)*));
64 }
65 };
66}
67
68macro_rules! ensure_not_precompile {
69 ($address:expr, $ctxt:expr) => {
70 if $ctxt.is_precompile($address) {
71 return Err($crate::error::precompile_error($address));
72 }
73 };
74}
75
76#[cold]
77pub(crate) fn precompile_error(address: &Address) -> Error {
78 fmt_err!("cannot use precompile {address} as an argument")
79}
80
81pub struct Error {
85 is_str: bool,
87 drop: bool,
90 data: *const [u8],
92}
93
94impl std::error::Error for Error {}
95
96impl fmt::Debug for Error {
97 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98 f.write_str("Error::")?;
99 self.kind().fmt(f)
100 }
101}
102
103impl fmt::Display for Error {
104 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105 self.kind().fmt(f)
106 }
107}
108
109#[derive(Debug)]
113pub enum ErrorKind<'a> {
114 String(&'a str),
116 Bytes(&'a [u8]),
118}
119
120impl fmt::Display for ErrorKind<'_> {
121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122 match *self {
123 Self::String(ss) => f.write_str(ss),
124 Self::Bytes(b) => f.write_str(&hex::encode_prefixed(b)),
125 }
126 }
127}
128
129impl Error {
130 pub fn encode(error: impl Into<Self>) -> Bytes {
132 error.into().abi_encode().into()
133 }
134
135 pub fn display(msg: impl fmt::Display) -> Self {
137 Self::fmt(format_args!("{msg}"))
138 }
139
140 pub fn fmt(args: fmt::Arguments<'_>) -> Self {
142 match args.as_str() {
143 Some(s) => Self::new_str(s),
144 None => Self::new_string(std::fmt::format(args)),
145 }
146 }
147
148 pub fn abi_encode(&self) -> Vec<u8> {
151 match self.kind() {
152 ErrorKind::String(string) => Vm::CheatcodeError { message: string.into() }.abi_encode(),
153 ErrorKind::Bytes(bytes) => bytes.into(),
154 }
155 }
156
157 #[inline]
159 pub fn kind(&self) -> ErrorKind<'_> {
160 let data = self.data();
161 if self.is_str {
162 debug_assert!(std::str::from_utf8(data).is_ok());
163 ErrorKind::String(unsafe { std::str::from_utf8_unchecked(data) })
164 } else {
165 ErrorKind::Bytes(data)
166 }
167 }
168
169 #[inline]
171 pub fn data(&self) -> &[u8] {
172 unsafe { &*self.data }
173 }
174
175 #[inline]
177 pub fn is_str(&self) -> bool {
178 self.is_str
179 }
180
181 #[inline]
182 fn new_str(data: &'static str) -> Self {
183 Self::_new(true, false, data.as_bytes())
184 }
185
186 #[inline]
187 fn new_string(data: String) -> Self {
188 Self::_new(true, true, Box::into_raw(data.into_boxed_str().into_boxed_bytes()))
189 }
190
191 #[inline]
192 fn new_bytes(data: &'static [u8]) -> Self {
193 Self::_new(false, false, data)
194 }
195
196 #[inline]
197 fn new_vec(data: Vec<u8>) -> Self {
198 Self::_new(false, true, Box::into_raw(data.into_boxed_slice()))
199 }
200
201 #[inline]
202 fn _new(is_str: bool, drop: bool, data: *const [u8]) -> Self {
203 debug_assert!(!data.is_null());
204 Self { is_str, drop, data }
205 }
206}
207
208impl Drop for Error {
209 fn drop(&mut self) {
210 if self.drop {
211 drop(unsafe { Box::<[u8]>::from_raw(self.data.cast_mut()) });
212 }
213 }
214}
215
216impl From<Cow<'static, str>> for Error {
217 fn from(value: Cow<'static, str>) -> Self {
218 match value {
219 Cow::Borrowed(str) => Self::new_str(str),
220 Cow::Owned(string) => Self::new_string(string),
221 }
222 }
223}
224
225impl From<String> for Error {
226 fn from(value: String) -> Self {
227 Self::new_string(value)
228 }
229}
230
231impl From<&'static str> for Error {
232 fn from(value: &'static str) -> Self {
233 Self::new_str(value)
234 }
235}
236
237impl From<Cow<'static, [u8]>> for Error {
238 fn from(value: Cow<'static, [u8]>) -> Self {
239 match value {
240 Cow::Borrowed(bytes) => Self::new_bytes(bytes),
241 Cow::Owned(vec) => Self::new_vec(vec),
242 }
243 }
244}
245
246impl From<&'static [u8]> for Error {
247 fn from(value: &'static [u8]) -> Self {
248 Self::new_bytes(value)
249 }
250}
251
252impl<const N: usize> From<&'static [u8; N]> for Error {
253 fn from(value: &'static [u8; N]) -> Self {
254 Self::new_bytes(value)
255 }
256}
257
258impl From<Vec<u8>> for Error {
259 fn from(value: Vec<u8>) -> Self {
260 Self::new_vec(value)
261 }
262}
263
264impl From<Bytes> for Error {
265 #[inline]
266 fn from(value: Bytes) -> Self {
267 Self::new_vec(value.into())
268 }
269}
270
271macro_rules! impl_from {
273 ($($t:ty),* $(,)?) => {$(
274 impl From<$t> for Error {
275 fn from(value: $t) -> Self {
276 Self::display(value)
277 }
278 }
279 )*};
280}
281
282impl_from!(
283 alloy_sol_types::Error,
284 alloy_dyn_abi::Error,
285 alloy_primitives::SignatureError,
286 eyre::Report,
287 FsPathError,
288 hex::FromHexError,
289 BackendError,
290 DatabaseError,
291 jsonpath_lib::JsonPathError,
292 serde_json::Error,
293 SignatureError,
294 std::io::Error,
295 std::num::TryFromIntError,
296 std::str::Utf8Error,
297 std::string::FromUtf8Error,
298 UnresolvedEnvVarError,
299 LocalSignerError,
300 SignerError,
301 WalletSignerError,
302);
303
304impl<T: Into<BackendError>> From<EVMError<T>> for Error {
305 fn from(err: EVMError<T>) -> Self {
306 Self::display(BackendError::from(err))
307 }
308}
309
310#[cfg(test)]
311mod tests {
312 use super::*;
313
314 #[test]
315 fn encode() {
316 let error = Vm::CheatcodeError { message: "hello".into() }.abi_encode();
317 assert_eq!(Error::from("hello").abi_encode(), error);
318 assert_eq!(Error::encode("hello"), error);
319
320 assert_eq!(Error::from(b"hello").abi_encode(), b"hello");
321 assert_eq!(Error::encode(b"hello"), b"hello"[..]);
322 }
323}