alloy_sol_types/types/
ty.rs

1use crate::{
2    abi::{self, Token, TokenSeq},
3    private::SolTypeValue,
4    Result, Word,
5};
6use alloc::{borrow::Cow, vec::Vec};
7
8/// A Solidity type.
9///
10/// This trait is implemented by types that contain ABI encoding and decoding
11/// info for Solidity types. Types may be combined to express arbitrarily
12/// complex Solidity types.
13///
14/// These types are zero cost representations of Solidity types. They do not
15/// exist at runtime. They **only** contain information about the type, they do
16/// not carry any data.
17///
18/// # Implementer's Guide
19///
20/// It should not be necessary to implement this trait manually. Instead, use
21/// the [`sol!`] procedural macro to parse Solidity syntax into types that
22/// implement this trait.
23///
24/// # Examples
25///
26/// Basic usage:
27///
28/// ```
29/// use alloy_sol_types::{sol_data::*, SolType};
30///
31/// type Uint256DynamicArray = Array<Uint<256>>;
32/// assert_eq!(Uint256DynamicArray::sol_type_name(), "uint256[]");
33///
34/// type Erc20FunctionArgs = (Address, Uint<256>);
35/// assert_eq!(Erc20FunctionArgs::sol_type_name(), "(address,uint256)");
36///
37/// type LargeComplexType = (FixedArray<Array<Bool>, 2>, (FixedBytes<13>, String));
38/// assert_eq!(LargeComplexType::sol_type_name(), "(bool[][2],(bytes13,string))");
39/// ```
40///
41/// The previous example can be entirely replicated with the [`sol!`] macro:
42///
43/// ```
44/// use alloy_sol_types::{sol, SolType};
45///
46/// type Uint256DynamicArray = sol!(uint256[]);
47/// assert_eq!(Uint256DynamicArray::sol_type_name(), "uint256[]");
48///
49/// type Erc20FunctionArgs = sol!((address, uint256));
50/// assert_eq!(Erc20FunctionArgs::sol_type_name(), "(address,uint256)");
51///
52/// type LargeComplexType = sol!((bool[][2],(bytes13,string)));
53/// assert_eq!(LargeComplexType::sol_type_name(), "(bool[][2],(bytes13,string))");
54/// ```
55///
56/// For more complex usage, it's recommended to use the
57/// [`SolValue`](crate::SolValue) trait for primitive types, and the `Sol*`
58/// traits for other types created with [`sol!`]:
59///
60/// ```
61/// use alloy_primitives::Address;
62/// use alloy_sol_types::{sol, SolCall, SolStruct, SolValue};
63///
64/// sol! {
65///     struct MyStruct {
66///         bool a;
67///         uint64 b;
68///         address c;
69///     }
70///
71///     enum MyEnum {
72///         A,
73///         B,
74///         C,
75///     }
76///
77///     function myFunction(MyStruct my_struct, MyEnum my_enum);
78/// }
79///
80/// // `SolValue`
81/// let my_bool = true;
82/// let _ = my_bool.abi_encode();
83///
84/// let my_struct = MyStruct { a: true, b: 1, c: Address::ZERO };
85/// let _ = my_struct.abi_encode();
86///
87/// let my_enum = MyEnum::A;
88/// let _ = my_enum.abi_encode();
89///
90/// // `SolCall`
91/// let my_function_call = myFunctionCall { my_struct, my_enum };
92/// let _ = my_function_call.abi_encode();
93/// ```
94///
95/// [`sol!`]: crate::sol
96pub trait SolType: Sized {
97    /// The corresponding Rust type.
98    type RustType: SolTypeValue<Self> + 'static;
99
100    /// The corresponding [ABI token type](Token).
101    ///
102    /// This is the intermediate representation of the type that is used for
103    /// ABI encoding and decoding.
104    type Token<'a>: Token<'a>;
105
106    /// The name of this type in Solidity.
107    const SOL_NAME: &'static str;
108
109    /// The statically-known ABI-encoded size of the type.
110    ///
111    /// If this is not known at compile time, this should be `None`, which indicates that the
112    /// encoded size is dynamic.
113    const ENCODED_SIZE: Option<usize>;
114
115    /// The statically-known Non-standard Packed Mode ABI-encoded size of the type.
116    ///
117    /// If this is not known at compile time, this should be `None`, which indicates that the
118    /// encoded size is dynamic.
119    const PACKED_ENCODED_SIZE: Option<usize>;
120
121    /// Whether the ABI-encoded size is dynamic.
122    ///
123    /// There should be no need to override the default implementation.
124    const DYNAMIC: bool = Self::ENCODED_SIZE.is_none();
125
126    /// Returns the name of this type in Solidity.
127    #[deprecated(since = "0.6.3", note = "use `SOL_NAME` instead")]
128    #[inline]
129    fn sol_type_name() -> Cow<'static, str> {
130        Self::SOL_NAME.into()
131    }
132
133    /// Calculate the ABI-encoded size of the data, counting both head and tail
134    /// words. For a single-word type this will always be 32.
135    #[inline]
136    fn abi_encoded_size<E: ?Sized + SolTypeValue<Self>>(rust: &E) -> usize {
137        rust.stv_abi_encoded_size()
138    }
139
140    /// Returns `true` if the given token can be detokenized with this type.
141    fn valid_token(token: &Self::Token<'_>) -> bool;
142
143    /// Returns an error if the given token cannot be detokenized with this
144    /// type.
145    #[inline]
146    fn type_check(token: &Self::Token<'_>) -> Result<()> {
147        if Self::valid_token(token) {
148            Ok(())
149        } else {
150            Err(crate::Error::type_check_fail_token::<Self>(token))
151        }
152    }
153
154    /// Detokenize this type's value from the given token.
155    ///
156    /// See the [`abi::token`] module for more information.
157    fn detokenize(token: Self::Token<'_>) -> Self::RustType;
158
159    /// Tokenizes the given value into this type's token.
160    ///
161    /// See the [`abi::token`] module for more information.
162    #[inline]
163    fn tokenize<E: ?Sized + SolTypeValue<Self>>(rust: &E) -> Self::Token<'_> {
164        rust.stv_to_tokens()
165    }
166
167    /// Encode this data according to EIP-712 `encodeData` rules, and hash it
168    /// if necessary.
169    ///
170    /// Implementer's note: All single-word types are encoded as their word.
171    /// All multi-word types are encoded as the hash the concatenated data
172    /// words for each element
173    ///
174    /// <https://eips.ethereum.org/EIPS/eip-712#definition-of-encodedata>
175    #[inline]
176    fn eip712_data_word<E: ?Sized + SolTypeValue<Self>>(rust: &E) -> Word {
177        rust.stv_eip712_data_word()
178    }
179
180    /// Returns the length of this value when ABI-encoded in Non-standard Packed Mode.
181    ///
182    /// See [`abi_encode_packed`][SolType::abi_encode_packed] for more details.
183    #[inline]
184    fn abi_packed_encoded_size<E: ?Sized + SolTypeValue<Self>>(rust: &E) -> usize {
185        rust.stv_abi_packed_encoded_size()
186    }
187
188    /// Non-standard Packed Mode ABI encoding.
189    ///
190    /// See [`abi_encode_packed`][SolType::abi_encode_packed] for more details.
191    #[inline]
192    fn abi_encode_packed_to<E: ?Sized + SolTypeValue<Self>>(rust: &E, out: &mut Vec<u8>) {
193        rust.stv_abi_encode_packed_to(out)
194    }
195
196    /// Non-standard Packed Mode ABI encoding.
197    ///
198    /// This is different from normal ABI encoding:
199    /// - types shorter than 32 bytes are concatenated directly, without padding or sign extension;
200    /// - dynamic types are encoded in-place and without the length;
201    /// - array elements are padded, but still encoded in-place.
202    ///
203    /// More information can be found in the [Solidity docs](https://docs.soliditylang.org/en/latest/abi-spec.html#non-standard-packed-mode).
204    #[inline]
205    fn abi_encode_packed<E: ?Sized + SolTypeValue<Self>>(rust: &E) -> Vec<u8> {
206        let mut out = Vec::with_capacity(Self::abi_packed_encoded_size(rust));
207        Self::abi_encode_packed_to(rust, &mut out);
208        out
209    }
210
211    /// Tokenizes and ABI-encodes the given value by wrapping it in a
212    /// single-element sequence.
213    ///
214    /// See the [`abi`] module for more information.
215    #[inline]
216    fn abi_encode<E: ?Sized + SolTypeValue<Self>>(rust: &E) -> Vec<u8> {
217        abi::encode(&rust.stv_to_tokens())
218    }
219
220    /// Tokenizes and ABI-encodes the given value as function parameters.
221    ///
222    /// See the [`abi`] module for more information.
223    #[inline]
224    fn abi_encode_params<E: ?Sized + SolTypeValue<Self>>(rust: &E) -> Vec<u8>
225    where
226        for<'a> Self::Token<'a>: TokenSeq<'a>,
227    {
228        abi::encode_params(&rust.stv_to_tokens())
229    }
230
231    /// Tokenizes and ABI-encodes the given value as a sequence.
232    ///
233    /// See the [`abi`] module for more information.
234    #[inline]
235    fn abi_encode_sequence<E: ?Sized + SolTypeValue<Self>>(rust: &E) -> Vec<u8>
236    where
237        for<'a> Self::Token<'a>: TokenSeq<'a>,
238    {
239        abi::encode_sequence(&rust.stv_to_tokens())
240    }
241
242    /// Decodes this type's value from an ABI blob by interpreting it as a
243    /// single-element sequence.
244    ///
245    /// See the [`abi`] module for more information.
246    #[inline]
247    fn abi_decode(data: &[u8], validate: bool) -> Result<Self::RustType> {
248        abi::decode::<Self::Token<'_>>(data, validate)
249            .and_then(validate_and_detokenize::<Self>(validate))
250    }
251
252    /// Decodes this type's value from an ABI blob by interpreting it as
253    /// function parameters.
254    ///
255    /// See the [`abi`] module for more information.
256    #[inline]
257    fn abi_decode_params<'de>(data: &'de [u8], validate: bool) -> Result<Self::RustType>
258    where
259        Self::Token<'de>: TokenSeq<'de>,
260    {
261        abi::decode_params::<Self::Token<'_>>(data, validate)
262            .and_then(validate_and_detokenize::<Self>(validate))
263    }
264
265    /// Decodes this type's value from an ABI blob by interpreting it as a
266    /// sequence.
267    ///
268    /// See the [`abi`] module for more information.
269    #[inline]
270    fn abi_decode_sequence<'de>(data: &'de [u8], validate: bool) -> Result<Self::RustType>
271    where
272        Self::Token<'de>: TokenSeq<'de>,
273    {
274        abi::decode_sequence::<Self::Token<'_>>(data, validate)
275            .and_then(validate_and_detokenize::<Self>(validate))
276    }
277}
278
279#[inline]
280fn validate_and_detokenize<T: SolType>(
281    validate: bool,
282) -> impl FnOnce(T::Token<'_>) -> Result<T::RustType> {
283    move |token| {
284        if validate {
285            T::type_check(&token)?;
286        }
287        Ok(T::detokenize(token))
288    }
289}