1use crate::{
2 Result, SolType, Word,
3 abi::token::{PackedSeqToken, Token, TokenSeq, WordToken},
4 types::interface::RevertReason,
5};
6use alloc::{borrow::Cow, format, 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]) -> Result<Self> {
55 <Self::Parameters<'_> as SolType>::abi_decode_sequence(data).map(Self::new)
56 }
57
58 #[inline]
64 fn abi_decode_raw_validate(data: &[u8]) -> Result<Self> {
65 <Self::Parameters<'_> as SolType>::abi_decode_sequence_validate(data).map(Self::new)
66 }
67
68 #[inline]
71 fn abi_decode(data: &[u8]) -> Result<Self> {
72 let data = data
73 .strip_prefix(&Self::SELECTOR)
74 .ok_or_else(|| crate::Error::type_check_fail_sig(data, Self::SIGNATURE))?;
75 Self::abi_decode_raw(data)
76 }
77
78 #[inline]
84 fn abi_decode_validate(data: &[u8]) -> Result<Self> {
85 let data = data
86 .strip_prefix(&Self::SELECTOR)
87 .ok_or_else(|| crate::Error::type_check_fail_sig(data, Self::SIGNATURE))?;
88 Self::abi_decode_raw_validate(data)
89 }
90
91 #[inline]
93 fn abi_encode_raw(&self, out: &mut Vec<u8>) {
94 out.reserve(self.abi_encoded_size());
95 out.extend(crate::abi::encode_sequence(&self.tokenize()));
96 }
97
98 #[inline]
100 fn abi_encode(&self) -> Vec<u8> {
101 let mut out = Vec::with_capacity(4 + self.abi_encoded_size());
102 out.extend(&Self::SELECTOR);
103 self.abi_encode_raw(&mut out);
104 out
105 }
106}
107
108#[derive(Clone, PartialEq, Eq, Hash)]
111pub struct Revert {
112 pub reason: String,
114}
115
116impl fmt::Debug for Revert {
117 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118 f.debug_tuple("Revert").field(&self.reason).finish()
119 }
120}
121
122impl fmt::Display for Revert {
123 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124 f.write_str("revert: ")?;
125 f.write_str(self.reason())
126 }
127}
128
129impl core::error::Error for Revert {}
130
131impl AsRef<str> for Revert {
132 #[inline]
133 fn as_ref(&self) -> &str {
134 &self.reason
135 }
136}
137
138impl Borrow<str> for Revert {
139 #[inline]
140 fn borrow(&self) -> &str {
141 &self.reason
142 }
143}
144
145impl From<Revert> for String {
146 #[inline]
147 fn from(value: Revert) -> Self {
148 value.reason
149 }
150}
151
152impl From<String> for Revert {
153 #[inline]
154 fn from(reason: String) -> Self {
155 Self { reason }
156 }
157}
158
159impl From<&str> for Revert {
160 #[inline]
161 fn from(value: &str) -> Self {
162 Self { reason: value.into() }
163 }
164}
165
166impl SolError for Revert {
167 type Parameters<'a> = (crate::sol_data::String,);
168 type Token<'a> = (PackedSeqToken<'a>,);
169
170 const SIGNATURE: &'static str = "Error(string)";
171 const SELECTOR: [u8; 4] = [0x08, 0xc3, 0x79, 0xa0];
172
173 #[inline]
174 fn new(tuple: <Self::Parameters<'_> as SolType>::RustType) -> Self {
175 Self { reason: tuple.0 }
176 }
177
178 #[inline]
179 fn tokenize(&self) -> Self::Token<'_> {
180 (PackedSeqToken::from(self.reason.as_bytes()),)
181 }
182
183 #[inline]
184 fn abi_encoded_size(&self) -> usize {
185 64 + crate::utils::next_multiple_of_32(self.reason.len())
186 }
187
188 #[inline]
189 fn abi_decode_raw_validate(data: &[u8]) -> Result<Self> {
190 Self::abi_decode_raw(data)
191 }
192}
193
194impl Revert {
195 #[inline]
197 pub fn reason(&self) -> &str {
198 if self.reason.is_empty() { "<empty>" } else { &self.reason }
199 }
200}
201
202#[derive(Clone, Copy, Default, PartialEq, Eq, Hash)]
211pub struct Panic {
212 pub code: U256,
216}
217
218impl AsRef<U256> for Panic {
219 #[inline]
220 fn as_ref(&self) -> &U256 {
221 &self.code
222 }
223}
224
225impl Borrow<U256> for Panic {
226 #[inline]
227 fn borrow(&self) -> &U256 {
228 &self.code
229 }
230}
231
232impl From<PanicKind> for Panic {
233 #[inline]
234 fn from(value: PanicKind) -> Self {
235 Self { code: U256::from(value as u64) }
236 }
237}
238
239impl From<u64> for Panic {
240 #[inline]
241 fn from(value: u64) -> Self {
242 Self { code: U256::from(value) }
243 }
244}
245
246impl From<Panic> for U256 {
247 #[inline]
248 fn from(value: Panic) -> Self {
249 value.code
250 }
251}
252
253impl From<U256> for Panic {
254 #[inline]
255 fn from(value: U256) -> Self {
256 Self { code: value }
257 }
258}
259
260impl fmt::Debug for Panic {
261 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
262 let mut debug = f.debug_tuple("Panic");
263 if let Some(kind) = self.kind() {
264 debug.field(&kind);
265 } else {
266 debug.field(&self.code);
267 }
268 debug.finish()
269 }
270}
271
272impl fmt::Display for Panic {
273 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
274 f.write_str("panic: ")?;
275
276 let kind = self.kind();
277 let msg = kind.map(PanicKind::as_str).unwrap_or("unknown code");
278 f.write_str(msg)?;
279
280 f.write_str(" (0x")?;
281 if let Some(kind) = kind {
282 write!(f, "{:02x}", kind as u32)
283 } else {
284 write!(f, "{:x}", self.code)
285 }?;
286 f.write_str(")")
287 }
288}
289
290impl core::error::Error for Panic {}
291
292impl SolError for Panic {
293 type Parameters<'a> = (crate::sol_data::Uint<256>,);
294 type Token<'a> = (WordToken,);
295
296 const SIGNATURE: &'static str = "Panic(uint256)";
297 const SELECTOR: [u8; 4] = [0x4e, 0x48, 0x7b, 0x71];
298
299 #[inline]
300 fn new(tuple: <Self::Parameters<'_> as SolType>::RustType) -> Self {
301 Self { code: tuple.0 }
302 }
303
304 #[inline]
305 fn tokenize(&self) -> Self::Token<'_> {
306 (WordToken::from(self.code),)
307 }
308
309 #[inline]
310 fn abi_encoded_size(&self) -> usize {
311 32
312 }
313}
314
315impl Panic {
316 pub fn kind(&self) -> Option<PanicKind> {
321 u32::try_from(&self.code).ok().and_then(PanicKind::from_number)
323 }
324
325 pub fn as_geth_str(&self) -> Cow<'static, str> {
327 if let Some(kind) = self.kind() {
328 Cow::Borrowed(kind.as_geth_str())
329 } else {
330 Cow::Owned(format!("unknown panic code: {:#x}", self.code))
331 }
332 }
333}
334
335#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
341#[repr(u32)]
342#[non_exhaustive]
343pub enum PanicKind {
344 #[default]
349 Generic = 0x00,
350 Assert = 0x01,
355 UnderOverflow = 0x11,
360 DivisionByZero = 0x12,
364 EnumConversionError = 0x21,
369 StorageEncodingError = 0x22,
373 EmptyArrayPop = 0x31,
377 ArrayOutOfBounds = 0x32,
383 ResourceError = 0x41,
388 InvalidInternalFunction = 0x51,
393}
394
395impl fmt::Display for PanicKind {
396 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
397 f.write_str(self.as_str())
398 }
399}
400
401impl PanicKind {
402 pub const fn from_number(value: u32) -> Option<Self> {
404 match value {
405 0x00 => Some(Self::Generic),
406 0x01 => Some(Self::Assert),
407 0x11 => Some(Self::UnderOverflow),
408 0x12 => Some(Self::DivisionByZero),
409 0x21 => Some(Self::EnumConversionError),
410 0x22 => Some(Self::StorageEncodingError),
411 0x31 => Some(Self::EmptyArrayPop),
412 0x32 => Some(Self::ArrayOutOfBounds),
413 0x41 => Some(Self::ResourceError),
414 0x51 => Some(Self::InvalidInternalFunction),
415 _ => None,
416 }
417 }
418
419 pub const fn as_str(self) -> &'static str {
421 match self {
424 Self::Generic => "generic/unspecified error",
425 Self::Assert => "assertion failed",
426 Self::UnderOverflow => "arithmetic underflow or overflow",
427 Self::DivisionByZero => "division or modulo by zero",
428 Self::EnumConversionError => "failed to convert value into enum type",
429 Self::StorageEncodingError => "storage byte array incorrectly encoded",
430 Self::EmptyArrayPop => "called `.pop()` on an empty array",
431 Self::ArrayOutOfBounds => "array out-of-bounds access",
432 Self::ResourceError => "memory allocation error",
433 Self::InvalidInternalFunction => "called an invalid internal function",
434 }
435 }
436
437 pub const fn as_geth_str(self) -> &'static str {
442 match self {
443 Self::Generic => "generic panic",
444 Self::Assert => "assert(false)",
445 Self::UnderOverflow => "arithmetic underflow or overflow",
446 Self::DivisionByZero => "division or modulo by zero",
447 Self::EnumConversionError => "enum overflow",
448 Self::StorageEncodingError => "invalid encoded storage byte array accessed",
449 Self::EmptyArrayPop => "out-of-bounds array access; popping on an empty array",
450 Self::ArrayOutOfBounds => "out-of-bounds access of an array or bytesN",
451 Self::ResourceError => "out of memory",
452 Self::InvalidInternalFunction => "uninitialized function",
453 }
454 }
455}
456
457pub fn decode_revert_reason(out: &[u8]) -> Option<String> {
466 RevertReason::decode(out).map(|x| x.to_string())
467}
468
469#[cfg(test)]
470mod tests {
471 use super::*;
472 use crate::{sol, types::interface::SolInterface};
473 use alloc::string::ToString;
474 use alloy_primitives::{address, hex, keccak256};
475
476 #[test]
477 fn revert_encoding() {
478 let revert = Revert::from("test");
479 let encoded = revert.abi_encode();
480 let decoded = Revert::abi_decode(&encoded).unwrap();
481 assert_eq!(encoded.len(), revert.abi_encoded_size() + 4);
482 assert_eq!(encoded.len(), 100);
483 assert_eq!(revert, decoded);
484 }
485
486 #[test]
487 fn panic_encoding() {
488 let panic = Panic { code: U256::ZERO };
489 assert_eq!(panic.kind(), Some(PanicKind::Generic));
490 let encoded = panic.abi_encode();
491 let decoded = Panic::abi_decode(&encoded).unwrap();
492
493 assert_eq!(encoded.len(), panic.abi_encoded_size() + 4);
494 assert_eq!(encoded.len(), 36);
495 assert_eq!(panic, decoded);
496 }
497
498 #[test]
499 fn selectors() {
500 assert_eq!(
501 Revert::SELECTOR,
502 &keccak256(b"Error(string)")[..4],
503 "Revert selector is incorrect"
504 );
505 assert_eq!(
506 Panic::SELECTOR,
507 &keccak256(b"Panic(uint256)")[..4],
508 "Panic selector is incorrect"
509 );
510 }
511
512 #[test]
513 fn decode_solidity_revert_reason() {
514 let revert = Revert::from("test_revert_reason");
515 let encoded = revert.abi_encode();
516 let decoded = decode_revert_reason(&encoded).unwrap();
517 assert_eq!(decoded, revert.to_string());
518 }
519
520 #[test]
521 fn decode_uniswap_revert() {
522 let bytes = hex!(
526 "08c379a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024556e697377617056323a20494e53554646494349454e545f494e5055545f414d4f554e5400000000000000000000000000000000000000000000000000000080"
527 );
528
529 let decoded = Revert::abi_decode(&bytes).unwrap();
530 assert_eq!(decoded.reason, "UniswapV2: INSUFFICIENT_INPUT_AMOUNT");
531
532 let decoded = decode_revert_reason(&bytes).unwrap();
533 assert_eq!(decoded, "revert: UniswapV2: INSUFFICIENT_INPUT_AMOUNT");
534 }
535
536 #[test]
537 fn decode_random_revert_reason() {
538 let revert_reason = String::from("test_revert_reason");
539 let decoded = decode_revert_reason(revert_reason.as_bytes()).unwrap();
540 assert_eq!(decoded, "test_revert_reason");
541 }
542
543 #[test]
544 fn decode_non_utf8_revert_reason() {
545 let revert_reason = [0xFF];
546 let decoded = decode_revert_reason(&revert_reason);
547 assert_eq!(decoded, None);
548 }
549
550 #[test]
552 fn decode_solidity_no_interface() {
553 sol! {
554 interface C {
555 #[derive(Debug, PartialEq)]
556 error SenderAddressError(address);
557 }
558 }
559
560 let data = hex!("8758782b000000000000000000000000a48388222c7ee7daefde5d0b9c99319995c4a990");
561 assert_eq!(decode_revert_reason(&data), None);
562
563 let C::CErrors::SenderAddressError(decoded) =
564 C::CErrors::abi_decode_validate(&data).unwrap();
565 assert_eq!(
566 decoded,
567 C::SenderAddressError(address!("0xa48388222c7ee7daefde5d0b9c99319995c4a990"))
568 );
569 }
570
571 #[test]
572 fn panic_kind_display() {
573 let panic = Panic::from(PanicKind::Assert);
574 assert_eq!(panic.to_string(), "panic: assertion failed (0x01)");
575
576 let panic = Panic::from(PanicKind::UnderOverflow);
577 assert_eq!(panic.to_string(), "panic: arithmetic underflow or overflow (0x11)");
578
579 let panic = Panic::from(PanicKind::DivisionByZero);
580 assert_eq!(panic.to_string(), "panic: division or modulo by zero (0x12)");
581
582 let panic = Panic { code: U256::from(0x32) };
583 assert_eq!(panic.to_string(), "panic: array out-of-bounds access (0x32)");
584 }
585
586 #[test]
587 fn panic_geth_string() {
588 let panic = Panic::from(PanicKind::Assert);
589 assert_eq!(panic.as_geth_str(), "assert(false)");
590
591 let panic = Panic::from(PanicKind::UnderOverflow);
592 assert_eq!(panic.as_geth_str(), "arithmetic underflow or overflow");
593
594 let panic = Panic::from(PanicKind::DivisionByZero);
595 assert_eq!(panic.as_geth_str(), "division or modulo by zero");
596
597 let panic = Panic { code: U256::from(0x32) };
598 assert_eq!(panic.as_geth_str(), "out-of-bounds access of an array or bytesN");
599 }
600
601 #[test]
602 fn panic_unknown_code_display() {
603 let panic = Panic { code: U256::from(0x99) };
604 assert_eq!(panic.to_string(), "panic: unknown code (0x99)");
605
606 let panic = Panic { code: U256::from(0xFF) };
607 assert_eq!(panic.to_string(), "panic: unknown code (0xff)");
608
609 let panic = Panic { code: U256::from(0x05) };
610 assert_eq!(panic.to_string(), "panic: unknown code (0x5)");
611
612 let panic = Panic { code: U256::from(0x1234) };
613 assert_eq!(panic.to_string(), "panic: unknown code (0x1234)");
614 }
615
616 #[test]
617 fn panic_unknown_code_geth_string() {
618 let panic = Panic { code: U256::from(0x99) };
619 assert_eq!(panic.as_geth_str(), "unknown panic code: 0x99");
620
621 let panic = Panic { code: U256::from(0xFF) };
622 assert_eq!(panic.as_geth_str(), "unknown panic code: 0xff");
623
624 let panic = Panic { code: U256::from(0x05) };
625 assert_eq!(panic.as_geth_str(), "unknown panic code: 0x5");
626
627 let panic = Panic { code: U256::from(0x1234) };
628 assert_eq!(panic.as_geth_str(), "unknown panic code: 0x1234");
629 }
630}