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}