alloy_sol_types/
errors.rs1use alloc::{borrow::Cow, boxed::Box, collections::TryReserveError, string::String};
11use alloy_primitives::{B256, LogData, hex};
12use core::{cmp, fmt};
13
14const MAX_TYPE_CHECK_FAIL_DATA_LEN: usize = 128;
15
16pub type Result<T, E = Error> = core::result::Result<T, E>;
18
19#[derive(Clone, Debug, PartialEq)]
21pub enum Error {
22 TypeCheckFail {
24 expected_type: Cow<'static, str>,
26 data: String,
28 },
29
30 Overrun,
32
33 Reserve(TryReserveError),
35
36 BufferNotEmpty,
38
39 ReserMismatch,
41
42 RecursionLimitExceeded(usize),
44
45 MemoryLimitExceeded(usize),
47
48 InvalidEnumValue {
50 name: &'static str,
52 value: u8,
54 max: u8,
56 },
57
58 InvalidEventSignatureHash {
60 name: &'static str,
62 got: B256,
64 expected: B256,
66 },
67
68 InvalidLog {
70 name: &'static str,
72 log: Box<LogData>,
74 },
75
76 UnknownSelector {
78 name: &'static str,
80 selector: alloy_primitives::FixedBytes<4>,
82 },
83
84 FromHexError(hex::FromHexError),
86
87 Other(Cow<'static, str>),
89}
90
91impl core::error::Error for Error {
92 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
93 match self {
94 Self::Reserve(e) => Some(e),
95 Self::FromHexError(e) => Some(e),
96 _ => None,
97 }
98 }
99}
100
101impl fmt::Display for Error {
102 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103 match self {
104 Self::TypeCheckFail { expected_type, data } => {
105 write!(f, "type check failed for {expected_type:?}")?;
106 if !data.is_empty() {
107 write!(f, " with data: {data}")?;
108 }
109 Ok(())
110 }
111 Self::Overrun
112 | Self::BufferNotEmpty
113 | Self::ReserMismatch
114 | Self::RecursionLimitExceeded(_)
115 | Self::MemoryLimitExceeded(_) => {
116 f.write_str("ABI decoding failed: ")?;
117 match *self {
118 Self::Overrun => f.write_str("buffer overrun while deserializing"),
119 Self::BufferNotEmpty => f.write_str("buffer not empty after deserialization"),
120 Self::ReserMismatch => f.write_str("reserialization did not match original"),
121 Self::RecursionLimitExceeded(limit) => {
122 write!(f, "recursion limit of {limit} exceeded during decoding")
123 }
124 Self::MemoryLimitExceeded(limit) => {
125 write!(f, "memory limit of {limit} bytes exceeded during decoding")
126 }
127 _ => unreachable!(),
128 }
129 }
130 Self::Reserve(e) => e.fmt(f),
131 Self::InvalidEnumValue { name, value, max } => {
132 write!(f, "`{value}` is not a valid {name} enum value (max: `{max}`)")
133 }
134 Self::InvalidEventSignatureHash { name, got, expected } => {
135 write!(
136 f,
137 "invalid signature hash for event {name:?}: got {got}, expected {expected}"
138 )
139 }
140 Self::InvalidLog { name, log } => {
141 write!(f, "could not decode {name} from log: {log:?}")
142 }
143 Self::UnknownSelector { name, selector } => {
144 write!(f, "unknown selector `{selector}` for {name}")
145 }
146 Self::FromHexError(e) => e.fmt(f),
147 Self::Other(e) => f.write_str(e),
148 }
149 }
150}
151
152impl Error {
153 #[cold]
155 pub fn custom(s: impl Into<Cow<'static, str>>) -> Self {
156 Self::Other(s.into())
157 }
158
159 #[cold]
161 pub fn type_check_fail_sig(mut data: &[u8], signature: &'static str) -> Self {
162 if data.len() > 4 {
163 data = &data[..4];
164 }
165 let expected_type = signature.split('(').next().unwrap();
166 Self::type_check_fail(data, expected_type)
167 }
168
169 #[cold]
171 pub fn type_check_fail_token<T: crate::SolType>(_token: &T::Token<'_>) -> Self {
172 Self::type_check_fail(&[], T::SOL_NAME)
173 }
174
175 #[cold]
177 pub fn type_check_fail(data: &[u8], expected_type: impl Into<Cow<'static, str>>) -> Self {
178 let data = &data[..cmp::min(data.len(), MAX_TYPE_CHECK_FAIL_DATA_LEN)];
179 Self::TypeCheckFail { expected_type: expected_type.into(), data: hex::encode(data) }
180 }
181
182 #[cold]
184 pub fn unknown_selector(name: &'static str, selector: [u8; 4]) -> Self {
185 Self::UnknownSelector { name, selector: selector.into() }
186 }
187
188 #[doc(hidden)] #[cold]
190 pub const fn invalid_event_signature_hash(
191 name: &'static str,
192 got: B256,
193 expected: B256,
194 ) -> Self {
195 Self::InvalidEventSignatureHash { name, got, expected }
196 }
197}
198
199impl From<hex::FromHexError> for Error {
200 #[inline]
201 fn from(value: hex::FromHexError) -> Self {
202 Self::FromHexError(value)
203 }
204}
205
206impl From<TryReserveError> for Error {
207 #[inline]
208 fn from(value: TryReserveError) -> Self {
209 Self::Reserve(value)
210 }
211}
212
213#[cfg(test)]
214mod tests {
215 use super::*;
216 use alloc::string::ToString;
217
218 #[test]
219 fn type_check_fail_data_is_bounded() {
220 let error = Error::type_check_fail(&[0; MAX_TYPE_CHECK_FAIL_DATA_LEN + 1], "test");
221 let Error::TypeCheckFail { data, .. } = error else { unreachable!() };
222 assert_eq!(data.len(), MAX_TYPE_CHECK_FAIL_DATA_LEN * 2);
223 }
224
225 #[test]
226 fn type_check_fail_token_omits_token_data() {
227 let token = crate::abi::token::WordToken(crate::Word::ZERO);
228 let error = Error::type_check_fail_token::<crate::sol_data::Bool>(&token);
229 let Error::TypeCheckFail { data, .. } = error else { unreachable!() };
230 assert!(data.is_empty());
231 }
232
233 #[test]
234 fn empty_type_check_fail_data_is_not_displayed() {
235 assert_eq!(
236 Error::type_check_fail(&[], "test").to_string(),
237 "type check failed for \"test\""
238 );
239 }
240}