Skip to main content

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::{B256, LogData, hex};
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    /// Invalid event signature hash.
55    InvalidEventSignatureHash {
56        /// The event signature.
57        name: &'static str,
58        /// The received signature hash.
59        got: B256,
60        /// The expected signature hash.
61        expected: B256,
62    },
63
64    /// Could not decode an event from log topics.
65    InvalidLog {
66        /// The name of the enum or event.
67        name: &'static str,
68        /// The invalid log.
69        log: Box<LogData>,
70    },
71
72    /// Unknown selector.
73    UnknownSelector {
74        /// The type name.
75        name: &'static str,
76        /// The unknown selector.
77        selector: alloy_primitives::FixedBytes<4>,
78    },
79
80    /// Hex error.
81    FromHexError(hex::FromHexError),
82
83    /// Other errors.
84    Other(Cow<'static, str>),
85}
86
87impl core::error::Error for Error {
88    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
89        match self {
90            Self::Reserve(e) => Some(e),
91            Self::FromHexError(e) => Some(e),
92            _ => None,
93        }
94    }
95}
96
97impl fmt::Display for Error {
98    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99        match self {
100            Self::TypeCheckFail { expected_type, data } => {
101                write!(f, "type check failed for {expected_type:?} with data: {data}",)
102            }
103            Self::Overrun
104            | Self::BufferNotEmpty
105            | Self::ReserMismatch
106            | Self::RecursionLimitExceeded(_) => {
107                f.write_str("ABI decoding failed: ")?;
108                match *self {
109                    Self::Overrun => f.write_str("buffer overrun while deserializing"),
110                    Self::BufferNotEmpty => f.write_str("buffer not empty after deserialization"),
111                    Self::ReserMismatch => f.write_str("reserialization did not match original"),
112                    Self::RecursionLimitExceeded(limit) => {
113                        write!(f, "recursion limit of {limit} exceeded during decoding")
114                    }
115                    _ => unreachable!(),
116                }
117            }
118            Self::Reserve(e) => e.fmt(f),
119            Self::InvalidEnumValue { name, value, max } => {
120                write!(f, "`{value}` is not a valid {name} enum value (max: `{max}`)")
121            }
122            Self::InvalidEventSignatureHash { name, got, expected } => {
123                write!(
124                    f,
125                    "invalid signature hash for event {name:?}: got {got}, expected {expected}"
126                )
127            }
128            Self::InvalidLog { name, log } => {
129                write!(f, "could not decode {name} from log: {log:?}")
130            }
131            Self::UnknownSelector { name, selector } => {
132                write!(f, "unknown selector `{selector}` for {name}")
133            }
134            Self::FromHexError(e) => e.fmt(f),
135            Self::Other(e) => f.write_str(e),
136        }
137    }
138}
139
140impl Error {
141    /// Instantiates a new error with a static str.
142    #[cold]
143    pub fn custom(s: impl Into<Cow<'static, str>>) -> Self {
144        Self::Other(s.into())
145    }
146
147    /// Instantiates a new [`Error::TypeCheckFail`] with the provided data.
148    #[cold]
149    pub fn type_check_fail_sig(mut data: &[u8], signature: &'static str) -> Self {
150        if data.len() > 4 {
151            data = &data[..4];
152        }
153        let expected_type = signature.split('(').next().unwrap();
154        Self::type_check_fail(data, expected_type)
155    }
156
157    /// Instantiates a new [`Error::TypeCheckFail`] with the provided token.
158    #[cold]
159    pub fn type_check_fail_token<T: crate::SolType>(token: &T::Token<'_>) -> Self {
160        Self::type_check_fail(&abi::encode(token), T::SOL_NAME)
161    }
162
163    /// Instantiates a new [`Error::TypeCheckFail`] with the provided data.
164    #[cold]
165    pub fn type_check_fail(data: &[u8], expected_type: impl Into<Cow<'static, str>>) -> Self {
166        Self::TypeCheckFail { expected_type: expected_type.into(), data: hex::encode(data) }
167    }
168
169    /// Instantiates a new [`Error::UnknownSelector`] with the provided data.
170    #[cold]
171    pub fn unknown_selector(name: &'static str, selector: [u8; 4]) -> Self {
172        Self::UnknownSelector { name, selector: selector.into() }
173    }
174
175    #[doc(hidden)] // Not public API.
176    #[cold]
177    pub const fn invalid_event_signature_hash(
178        name: &'static str,
179        got: B256,
180        expected: B256,
181    ) -> Self {
182        Self::InvalidEventSignatureHash { name, got, expected }
183    }
184}
185
186impl From<hex::FromHexError> for Error {
187    #[inline]
188    fn from(value: hex::FromHexError) -> Self {
189        Self::FromHexError(value)
190    }
191}
192
193impl From<TryReserveError> for Error {
194    #[inline]
195    fn from(value: TryReserveError) -> Self {
196        Self::Reserve(value)
197    }
198}