1use crate::{
2 abi::token::{PackedSeqToken, Token, TokenSeq, WordToken},
3 types::interface::RevertReason,
4 Result, SolType, Word,
5};
6use alloc::{string::String, vec::Vec};
7use alloy_primitives::U256;
8use core::{borrow::Borrow, fmt};
9
10pub trait SolError: Sized {
18 type Parameters<'a>: SolType<Token<'a> = Self::Token<'a>>;
22
23 type Token<'a>: TokenSeq<'a>;
25
26 const SIGNATURE: &'static str;
28
29 const SELECTOR: [u8; 4];
31
32 fn new(tuple: <Self::Parameters<'_> as SolType>::RustType) -> Self;
34
35 fn tokenize(&self) -> Self::Token<'_>;
37
38 #[inline]
41 fn abi_encoded_size(&self) -> usize {
42 if let Some(size) = <Self::Parameters<'_> as SolType>::ENCODED_SIZE {
43 return size;
44 }
45
46 let offset = <<Self::Parameters<'_> as SolType>::Token<'_> as Token>::DYNAMIC as usize * 32;
48 (self.tokenize().total_words() * Word::len_bytes()).saturating_sub(offset)
49 }
50
51 #[inline]
54 fn abi_decode_raw(data: &[u8], validate: bool) -> Result<Self> {
55 <Self::Parameters<'_> as SolType>::abi_decode_sequence(data, validate).map(Self::new)
56 }
57
58 #[inline]
61 fn abi_decode(data: &[u8], validate: bool) -> Result<Self> {
62 let data = data
63 .strip_prefix(&Self::SELECTOR)
64 .ok_or_else(|| crate::Error::type_check_fail_sig(data, Self::SIGNATURE))?;
65 Self::abi_decode_raw(data, validate)
66 }
67
68 #[inline]
70 fn abi_encode_raw(&self, out: &mut Vec<u8>) {
71 out.reserve(self.abi_encoded_size());
72 out.extend(crate::abi::encode_sequence(&self.tokenize()));
73 }
74
75 #[inline]
77 fn abi_encode(&self) -> Vec<u8> {
78 let mut out = Vec::with_capacity(4 + self.abi_encoded_size());
79 out.extend(&Self::SELECTOR);
80 self.abi_encode_raw(&mut out);
81 out
82 }
83}
84
85#[derive(Clone, PartialEq, Eq, Hash)]
88pub struct Revert {
89 pub reason: String,
91}
92
93impl fmt::Debug for Revert {
94 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95 f.debug_tuple("Revert").field(&self.reason).finish()
96 }
97}
98
99impl fmt::Display for Revert {
100 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101 f.write_str("revert: ")?;
102 f.write_str(self.reason())
103 }
104}
105
106impl core::error::Error for Revert {}
107
108impl AsRef<str> for Revert {
109 #[inline]
110 fn as_ref(&self) -> &str {
111 &self.reason
112 }
113}
114
115impl Borrow<str> for Revert {
116 #[inline]
117 fn borrow(&self) -> &str {
118 &self.reason
119 }
120}
121
122impl From<Revert> for String {
123 #[inline]
124 fn from(value: Revert) -> Self {
125 value.reason
126 }
127}
128
129impl From<String> for Revert {
130 #[inline]
131 fn from(reason: String) -> Self {
132 Self { reason }
133 }
134}
135
136impl From<&str> for Revert {
137 #[inline]
138 fn from(value: &str) -> Self {
139 Self { reason: value.into() }
140 }
141}
142
143impl SolError for Revert {
144 type Parameters<'a> = (crate::sol_data::String,);
145 type Token<'a> = (PackedSeqToken<'a>,);
146
147 const SIGNATURE: &'static str = "Error(string)";
148 const SELECTOR: [u8; 4] = [0x08, 0xc3, 0x79, 0xa0];
149
150 #[inline]
151 fn new(tuple: <Self::Parameters<'_> as SolType>::RustType) -> Self {
152 Self { reason: tuple.0 }
153 }
154
155 #[inline]
156 fn tokenize(&self) -> Self::Token<'_> {
157 (PackedSeqToken::from(self.reason.as_bytes()),)
158 }
159
160 #[inline]
161 fn abi_encoded_size(&self) -> usize {
162 64 + crate::utils::next_multiple_of_32(self.reason.len())
163 }
164}
165
166impl Revert {
167 #[inline]
169 pub fn reason(&self) -> &str {
170 if self.reason.is_empty() {
171 "<empty>"
172 } else {
173 &self.reason
174 }
175 }
176}
177
178#[derive(Clone, Copy, Default, PartialEq, Eq, Hash)]
187pub struct Panic {
188 pub code: U256,
192}
193
194impl AsRef<U256> for Panic {
195 #[inline]
196 fn as_ref(&self) -> &U256 {
197 &self.code
198 }
199}
200
201impl Borrow<U256> for Panic {
202 #[inline]
203 fn borrow(&self) -> &U256 {
204 &self.code
205 }
206}
207
208impl From<PanicKind> for Panic {
209 #[inline]
210 fn from(value: PanicKind) -> Self {
211 Self { code: U256::from(value as u64) }
212 }
213}
214
215impl From<u64> for Panic {
216 #[inline]
217 fn from(value: u64) -> Self {
218 Self { code: U256::from(value) }
219 }
220}
221
222impl From<Panic> for U256 {
223 #[inline]
224 fn from(value: Panic) -> Self {
225 value.code
226 }
227}
228
229impl From<U256> for Panic {
230 #[inline]
231 fn from(value: U256) -> Self {
232 Self { code: value }
233 }
234}
235
236impl fmt::Debug for Panic {
237 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
238 let mut debug = f.debug_tuple("Panic");
239 if let Some(kind) = self.kind() {
240 debug.field(&kind);
241 } else {
242 debug.field(&self.code);
243 }
244 debug.finish()
245 }
246}
247
248impl fmt::Display for Panic {
249 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
250 f.write_str("panic: ")?;
251
252 let kind = self.kind();
253 let msg = kind.map(PanicKind::as_str).unwrap_or("unknown code");
254 f.write_str(msg)?;
255
256 f.write_str(" (0x")?;
257 if let Some(kind) = kind {
258 write!(f, "{:02x}", kind as u32)
259 } else {
260 write!(f, "{:x}", self.code)
261 }?;
262 f.write_str(")")
263 }
264}
265
266impl core::error::Error for Panic {}
267
268impl SolError for Panic {
269 type Parameters<'a> = (crate::sol_data::Uint<256>,);
270 type Token<'a> = (WordToken,);
271
272 const SIGNATURE: &'static str = "Panic(uint256)";
273 const SELECTOR: [u8; 4] = [0x4e, 0x48, 0x7b, 0x71];
274
275 #[inline]
276 fn new(tuple: <Self::Parameters<'_> as SolType>::RustType) -> Self {
277 Self { code: tuple.0 }
278 }
279
280 #[inline]
281 fn tokenize(&self) -> Self::Token<'_> {
282 (WordToken::from(self.code),)
283 }
284
285 #[inline]
286 fn abi_encoded_size(&self) -> usize {
287 32
288 }
289}
290
291impl Panic {
292 pub fn kind(&self) -> Option<PanicKind> {
297 u32::try_from(&self.code).ok().and_then(PanicKind::from_number)
299 }
300}
301
302#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
308#[repr(u32)]
309#[non_exhaustive]
310pub enum PanicKind {
311 #[default]
316 Generic = 0x00,
317 Assert = 0x01,
322 UnderOverflow = 0x11,
327 DivisionByZero = 0x12,
331 EnumConversionError = 0x21,
336 StorageEncodingError = 0x22,
340 EmptyArrayPop = 0x31,
344 ArrayOutOfBounds = 0x32,
350 ResourceError = 0x41,
355 InvalidInternalFunction = 0x51,
360}
361
362impl fmt::Display for PanicKind {
363 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
364 f.write_str(self.as_str())
365 }
366}
367
368impl PanicKind {
369 pub const fn from_number(value: u32) -> Option<Self> {
371 match value {
372 0x00 => Some(Self::Generic),
373 0x01 => Some(Self::Assert),
374 0x11 => Some(Self::UnderOverflow),
375 0x12 => Some(Self::DivisionByZero),
376 0x21 => Some(Self::EnumConversionError),
377 0x22 => Some(Self::StorageEncodingError),
378 0x31 => Some(Self::EmptyArrayPop),
379 0x32 => Some(Self::ArrayOutOfBounds),
380 0x41 => Some(Self::ResourceError),
381 0x51 => Some(Self::InvalidInternalFunction),
382 _ => None,
383 }
384 }
385
386 pub const fn as_str(self) -> &'static str {
388 match self {
391 Self::Generic => "generic/unspecified error",
392 Self::Assert => "assertion failed",
393 Self::UnderOverflow => "arithmetic underflow or overflow",
394 Self::DivisionByZero => "division or modulo by zero",
395 Self::EnumConversionError => "failed to convert value into enum type",
396 Self::StorageEncodingError => "storage byte array incorrectly encoded",
397 Self::EmptyArrayPop => "called `.pop()` on an empty array",
398 Self::ArrayOutOfBounds => "array out-of-bounds access",
399 Self::ResourceError => "memory allocation error",
400 Self::InvalidInternalFunction => "called an invalid internal function",
401 }
402 }
403}
404
405pub fn decode_revert_reason(out: &[u8]) -> Option<String> {
414 RevertReason::decode(out).map(|x| x.to_string())
415}
416
417#[cfg(test)]
418mod tests {
419 use super::*;
420 use crate::{sol, types::interface::SolInterface};
421 use alloc::string::ToString;
422 use alloy_primitives::{address, hex, keccak256};
423
424 #[test]
425 fn revert_encoding() {
426 let revert = Revert::from("test");
427 let encoded = revert.abi_encode();
428 let decoded = Revert::abi_decode(&encoded, true).unwrap();
429 assert_eq!(encoded.len(), revert.abi_encoded_size() + 4);
430 assert_eq!(encoded.len(), 100);
431 assert_eq!(revert, decoded);
432 }
433
434 #[test]
435 fn panic_encoding() {
436 let panic = Panic { code: U256::ZERO };
437 assert_eq!(panic.kind(), Some(PanicKind::Generic));
438 let encoded = panic.abi_encode();
439 let decoded = Panic::abi_decode(&encoded, true).unwrap();
440
441 assert_eq!(encoded.len(), panic.abi_encoded_size() + 4);
442 assert_eq!(encoded.len(), 36);
443 assert_eq!(panic, decoded);
444 }
445
446 #[test]
447 fn selectors() {
448 assert_eq!(
449 Revert::SELECTOR,
450 &keccak256(b"Error(string)")[..4],
451 "Revert selector is incorrect"
452 );
453 assert_eq!(
454 Panic::SELECTOR,
455 &keccak256(b"Panic(uint256)")[..4],
456 "Panic selector is incorrect"
457 );
458 }
459
460 #[test]
461 fn decode_solidity_revert_reason() {
462 let revert = Revert::from("test_revert_reason");
463 let encoded = revert.abi_encode();
464 let decoded = decode_revert_reason(&encoded).unwrap();
465 assert_eq!(decoded, revert.to_string());
466 }
467
468 #[test]
469 fn decode_uniswap_revert() {
470 let bytes = hex!("08c379a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024556e697377617056323a20494e53554646494349454e545f494e5055545f414d4f554e5400000000000000000000000000000000000000000000000000000080");
474
475 Revert::abi_decode(&bytes, true).unwrap_err();
476
477 let decoded = Revert::abi_decode(&bytes, false).unwrap();
478 assert_eq!(decoded.reason, "UniswapV2: INSUFFICIENT_INPUT_AMOUNT");
479
480 let decoded = decode_revert_reason(&bytes).unwrap();
481 assert_eq!(decoded, "revert: UniswapV2: INSUFFICIENT_INPUT_AMOUNT");
482 }
483
484 #[test]
485 fn decode_random_revert_reason() {
486 let revert_reason = String::from("test_revert_reason");
487 let decoded = decode_revert_reason(revert_reason.as_bytes()).unwrap();
488 assert_eq!(decoded, "test_revert_reason");
489 }
490
491 #[test]
492 fn decode_non_utf8_revert_reason() {
493 let revert_reason = [0xFF];
494 let decoded = decode_revert_reason(&revert_reason);
495 assert_eq!(decoded, None);
496 }
497
498 #[test]
500 fn decode_solidity_no_interface() {
501 sol! {
502 interface C {
503 #[derive(Debug, PartialEq)]
504 error SenderAddressError(address);
505 }
506 }
507
508 let data = hex!("8758782b000000000000000000000000a48388222c7ee7daefde5d0b9c99319995c4a990");
509 assert_eq!(decode_revert_reason(&data), None);
510
511 let C::CErrors::SenderAddressError(decoded) = C::CErrors::abi_decode(&data, true).unwrap();
512 assert_eq!(
513 decoded,
514 C::SenderAddressError { _0: address!("0xa48388222c7ee7daefde5d0b9c99319995c4a990") }
515 );
516 }
517}