alloy_sol_types/
errors.rs

1// Copyright 2015-2020 Parity Technologies
2// Copyright 2023-2023 Alloy Contributors
3
4// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7// option. This file may not be copied, modified, or distributed
8// except according to those terms.
9
10use crate::abi;
11use alloc::{borrow::Cow, boxed::Box, collections::TryReserveError, string::String};
12use alloy_primitives::{LogData, B256};
13use core::fmt;
14
15/// ABI result type.
16pub type Result<T, E = Error> = core::result::Result<T, E>;
17
18/// ABI Encoding and Decoding errors.
19#[derive(Clone, Debug, PartialEq)]
20pub enum Error {
21    /// A typecheck detected a word that does not match the data type.
22    TypeCheckFail {
23        /// The Solidity type we failed to produce.
24        expected_type: Cow<'static, str>,
25        /// Hex-encoded data.
26        data: String,
27    },
28
29    /// Overran deserialization buffer.
30    Overrun,
31
32    /// Allocation failed.
33    Reserve(TryReserveError),
34
35    /// Trailing bytes in deserialization buffer.
36    BufferNotEmpty,
37
38    /// Validation reserialization did not match input.
39    ReserMismatch,
40
41    /// ABI Decoding recursion limit exceeded.
42    RecursionLimitExceeded(u8),
43
44    /// Invalid enum value.
45    InvalidEnumValue {
46        /// The name of the enum.
47        name: &'static str,
48        /// The invalid value.
49        value: u8,
50        /// The maximum valid value.
51        max: u8,
52    },
53
54    /// Could not decode an event from log topics.
55    InvalidLog {
56        /// The name of the enum or event.
57        name: &'static str,
58        /// The invalid log.
59        log: Box<LogData>,
60    },
61
62    /// Unknown selector.
63    UnknownSelector {
64        /// The type name.
65        name: &'static str,
66        /// The unknown selector.
67        selector: alloy_primitives::FixedBytes<4>,
68    },
69
70    /// Hex error.
71    FromHexError(hex::FromHexError),
72
73    /// Other errors.
74    Other(Cow<'static, str>),
75}
76
77impl core::error::Error for Error {
78    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
79        match self {
80            Self::Reserve(e) => Some(e),
81            Self::FromHexError(e) => Some(e),
82            _ => None,
83        }
84    }
85}
86
87impl fmt::Display for Error {
88    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89        match self {
90            Self::TypeCheckFail { expected_type, data } => {
91                write!(f, "type check failed for {expected_type:?} with data: {data}",)
92            }
93            Self::Overrun
94            | Self::BufferNotEmpty
95            | Self::ReserMismatch
96            | Self::RecursionLimitExceeded(_) => {
97                f.write_str("ABI decoding failed: ")?;
98                match *self {
99                    Self::Overrun => f.write_str("buffer overrun while deserializing"),
100                    Self::BufferNotEmpty => f.write_str("buffer not empty after deserialization"),
101                    Self::ReserMismatch => f.write_str("reserialization did not match original"),
102                    Self::RecursionLimitExceeded(limit) => {
103                        write!(f, "recursion limit of {limit} exceeded during decoding")
104                    }
105                    _ => unreachable!(),
106                }
107            }
108            Self::Reserve(e) => e.fmt(f),
109            Self::InvalidEnumValue { name, value, max } => {
110                write!(f, "`{value}` is not a valid {name} enum value (max: `{max}`)")
111            }
112            Self::InvalidLog { name, log } => {
113                write!(f, "could not decode {name} from log: {log:?}")
114            }
115            Self::UnknownSelector { name, selector } => {
116                write!(f, "unknown selector `{selector}` for {name}")
117            }
118            Self::FromHexError(e) => e.fmt(f),
119            Self::Other(e) => f.write_str(e),
120        }
121    }
122}
123
124impl Error {
125    /// Instantiates a new error with a static str.
126    #[cold]
127    pub fn custom(s: impl Into<Cow<'static, str>>) -> Self {
128        Self::Other(s.into())
129    }
130
131    /// Instantiates a new [`Error::TypeCheckFail`] with the provided data.
132    #[cold]
133    pub fn type_check_fail_sig(mut data: &[u8], signature: &'static str) -> Self {
134        if data.len() > 4 {
135            data = &data[..4];
136        }
137        let expected_type = signature.split('(').next().unwrap();
138        Self::type_check_fail(data, expected_type)
139    }
140
141    /// Instantiates a new [`Error::TypeCheckFail`] with the provided token.
142    #[cold]
143    pub fn type_check_fail_token<T: crate::SolType>(token: &T::Token<'_>) -> Self {
144        Self::type_check_fail(&abi::encode(token), T::SOL_NAME)
145    }
146
147    /// Instantiates a new [`Error::TypeCheckFail`] with the provided data.
148    #[cold]
149    pub fn type_check_fail(data: &[u8], expected_type: impl Into<Cow<'static, str>>) -> Self {
150        Self::TypeCheckFail { expected_type: expected_type.into(), data: hex::encode(data) }
151    }
152
153    /// Instantiates a new [`Error::UnknownSelector`] with the provided data.
154    #[cold]
155    pub fn unknown_selector(name: &'static str, selector: [u8; 4]) -> Self {
156        Self::UnknownSelector { name, selector: selector.into() }
157    }
158
159    #[doc(hidden)] // Not public API.
160    #[cold]
161    pub fn invalid_event_signature_hash(name: &'static str, got: B256, expected: B256) -> Self {
162        Self::custom(format!(
163            "invalid signature hash for event {name:?}: got {got}, expected {expected}"
164        ))
165    }
166}
167
168impl From<hex::FromHexError> for Error {
169    #[inline]
170    fn from(value: hex::FromHexError) -> Self {
171        Self::FromHexError(value)
172    }
173}
174
175impl From<TryReserveError> for Error {
176    #[inline]
177    fn from(value: TryReserveError) -> Self {
178        Self::Reserve(value)
179    }
180}