alloy_sol_types/
errors.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
// Copyright 2015-2020 Parity Technologies
// Copyright 2023-2023 Alloy Contributors

// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use crate::abi;
use alloc::{borrow::Cow, boxed::Box, collections::TryReserveError, string::String};
use alloy_primitives::{LogData, B256};
use core::fmt;

/// ABI result type.
pub type Result<T, E = Error> = core::result::Result<T, E>;

/// ABI Encoding and Decoding errors.
#[derive(Clone, Debug, PartialEq)]
pub enum Error {
    /// A typecheck detected a word that does not match the data type.
    TypeCheckFail {
        /// The Solidity type we failed to produce.
        expected_type: Cow<'static, str>,
        /// Hex-encoded data.
        data: String,
    },

    /// Overran deserialization buffer.
    Overrun,

    /// Allocation failed.
    Reserve(TryReserveError),

    /// Trailing bytes in deserialization buffer.
    BufferNotEmpty,

    /// Validation reserialization did not match input.
    ReserMismatch,

    /// ABI Decoding recursion limit exceeded.
    RecursionLimitExceeded(u8),

    /// Invalid enum value.
    InvalidEnumValue {
        /// The name of the enum.
        name: &'static str,
        /// The invalid value.
        value: u8,
        /// The maximum valid value.
        max: u8,
    },

    /// Could not decode an event from log topics.
    InvalidLog {
        /// The name of the enum or event.
        name: &'static str,
        /// The invalid log.
        log: Box<LogData>,
    },

    /// Unknown selector.
    UnknownSelector {
        /// The type name.
        name: &'static str,
        /// The unknown selector.
        selector: alloy_primitives::FixedBytes<4>,
    },

    /// Hex error.
    FromHexError(hex::FromHexError),

    /// Other errors.
    Other(Cow<'static, str>),
}

#[cfg(feature = "std")]
impl std::error::Error for Error {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        match self {
            Self::Reserve(e) => Some(e),
            Self::FromHexError(e) => Some(e),
            _ => None,
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::TypeCheckFail { expected_type, data } => {
                write!(f, "type check failed for {expected_type:?} with data: {data}",)
            }
            Self::Overrun => f.write_str("buffer overrun while deserializing"),
            Self::Reserve(e) => e.fmt(f),
            Self::BufferNotEmpty => f.write_str("buffer not empty after deserialization"),
            Self::ReserMismatch => f.write_str("reserialization did not match original"),
            Self::RecursionLimitExceeded(limit) => {
                write!(f, "recursion limit of {limit} exceeded during decoding")
            }
            Self::InvalidEnumValue { name, value, max } => {
                write!(f, "`{value}` is not a valid {name} enum value (max: `{max}`)")
            }
            Self::InvalidLog { name, log } => {
                write!(f, "could not decode {name} from log: {log:?}")
            }
            Self::UnknownSelector { name, selector } => {
                write!(f, "unknown selector `{selector}` for {name}")
            }
            Self::FromHexError(e) => e.fmt(f),
            Self::Other(e) => f.write_str(e),
        }
    }
}

impl Error {
    /// Instantiates a new error with a static str.
    #[cold]
    pub fn custom(s: impl Into<Cow<'static, str>>) -> Self {
        Self::Other(s.into())
    }

    /// Instantiates a new [`Error::TypeCheckFail`] with the provided data.
    #[cold]
    pub fn type_check_fail_sig(mut data: &[u8], signature: &'static str) -> Self {
        if data.len() > 4 {
            data = &data[..4];
        }
        let expected_type = signature.split('(').next().unwrap();
        Self::type_check_fail(data, expected_type)
    }

    /// Instantiates a new [`Error::TypeCheckFail`] with the provided token.
    #[cold]
    pub fn type_check_fail_token<T: crate::SolType>(token: &T::Token<'_>) -> Self {
        Self::type_check_fail(&abi::encode(token), T::SOL_NAME)
    }

    /// Instantiates a new [`Error::TypeCheckFail`] with the provided data.
    #[cold]
    pub fn type_check_fail(data: &[u8], expected_type: impl Into<Cow<'static, str>>) -> Self {
        Self::TypeCheckFail { expected_type: expected_type.into(), data: hex::encode(data) }
    }

    /// Instantiates a new [`Error::UnknownSelector`] with the provided data.
    #[cold]
    pub fn unknown_selector(name: &'static str, selector: [u8; 4]) -> Self {
        Self::UnknownSelector { name, selector: selector.into() }
    }

    #[doc(hidden)] // Not public API.
    #[cold]
    pub fn invalid_event_signature_hash(name: &'static str, got: B256, expected: B256) -> Self {
        Self::custom(format!(
            "invalid signature hash for event {name:?}: got {got}, expected {expected}"
        ))
    }
}

impl From<hex::FromHexError> for Error {
    #[inline]
    fn from(value: hex::FromHexError) -> Self {
        Self::FromHexError(value)
    }
}

impl From<TryReserveError> for Error {
    #[inline]
    fn from(value: TryReserveError) -> Self {
        Self::Reserve(value)
    }
}