alloy_dyn_abi/
specifier.rs

1//! Contains utilities for parsing Solidity types.
2//!
3//! This is a simple representation of Solidity type grammar.
4
5use crate::{DynSolCall, DynSolType, Result};
6use alloc::vec::Vec;
7use alloy_json_abi::{EventParam, Function, Param};
8use parser::{ParameterSpecifier, Parameters, RootType, TupleSpecifier, TypeSpecifier, TypeStem};
9
10#[cfg(feature = "eip712")]
11use alloy_json_abi::InternalType;
12
13/// Trait for items that can be resolved to `DynSol*`, i.e. they specify some Solidity interface
14/// item.
15///
16/// The `Specifier` trait is implemented by types that can be resolved into Solidity interface
17/// items, e.g. [`DynSolType`] or [`DynSolEvent`](crate::DynSolEvent).
18///
19/// ABI and related systems have many different ways of specifying Solidity interfaces.
20/// This trait provides a single pattern for resolving those encodings into
21/// Solidity interface items.
22///
23/// `Specifier<DynSolType>` is implemented for all the [`parser`] types, the
24/// [`Param`] and [`EventParam`] structs, and [`str`]. The [`str`]
25/// implementation calls [`DynSolType::parse`].
26///
27/// # Examples
28///
29/// ```
30/// # use alloy_dyn_abi::{DynSolType, Specifier};
31/// # use alloy_sol_type_parser::{RootType, TypeSpecifier};
32/// let my_ty = TypeSpecifier::parse("bool")?.resolve()?;
33/// assert_eq!(my_ty, DynSolType::Bool);
34///
35/// let my_ty = RootType::parse("uint256")?.resolve()?;
36/// assert_eq!(my_ty, DynSolType::Uint(256));
37///
38/// assert_eq!("bytes32".resolve()?, DynSolType::FixedBytes(32));
39/// # Ok::<_, alloy_dyn_abi::Error>(())
40/// ```
41pub trait Specifier<T> {
42    /// Resolve the type into a value.
43    fn resolve(&self) -> Result<T>;
44}
45
46impl Specifier<DynSolType> for str {
47    #[inline]
48    fn resolve(&self) -> Result<DynSolType> {
49        DynSolType::parse(self)
50    }
51}
52
53impl Specifier<DynSolType> for RootType<'_> {
54    fn resolve(&self) -> Result<DynSolType> {
55        match self.span() {
56            "address" => Ok(DynSolType::Address),
57            "function" => Ok(DynSolType::Function),
58            "bool" => Ok(DynSolType::Bool),
59            "string" => Ok(DynSolType::String),
60            "bytes" => Ok(DynSolType::Bytes),
61            "uint" => Ok(DynSolType::Uint(256)),
62            "int" => Ok(DynSolType::Int(256)),
63            name => {
64                if let Some(sz) = name.strip_prefix("bytes") {
65                    if let Ok(sz) = sz.parse() {
66                        if sz != 0 && sz <= 32 {
67                            return Ok(DynSolType::FixedBytes(sz));
68                        }
69                    }
70                    return Err(parser::Error::invalid_size(name).into());
71                }
72
73                // fast path both integer types
74                let (s, is_uint) =
75                    if let Some(s) = name.strip_prefix('u') { (s, true) } else { (name, false) };
76
77                if let Some(sz) = s.strip_prefix("int") {
78                    if let Ok(sz) = sz.parse() {
79                        if sz != 0 && sz <= 256 && sz % 8 == 0 {
80                            return if is_uint {
81                                Ok(DynSolType::Uint(sz))
82                            } else {
83                                Ok(DynSolType::Int(sz))
84                            };
85                        }
86                    }
87                    Err(parser::Error::invalid_size(name).into())
88                } else {
89                    Err(parser::Error::invalid_type_string(name).into())
90                }
91            }
92        }
93    }
94}
95
96impl Specifier<DynSolType> for TupleSpecifier<'_> {
97    #[inline]
98    fn resolve(&self) -> Result<DynSolType> {
99        tuple(&self.types).map(DynSolType::Tuple)
100    }
101}
102
103impl Specifier<DynSolType> for TypeStem<'_> {
104    #[inline]
105    fn resolve(&self) -> Result<DynSolType> {
106        match self {
107            Self::Root(root) => root.resolve(),
108            Self::Tuple(tuple) => tuple.resolve(),
109        }
110    }
111}
112
113impl Specifier<DynSolType> for TypeSpecifier<'_> {
114    fn resolve(&self) -> Result<DynSolType> {
115        self.stem.resolve().map(|ty| ty.array_wrap_from_iter(self.sizes.iter().copied()))
116    }
117}
118
119impl Specifier<DynSolType> for ParameterSpecifier<'_> {
120    #[inline]
121    fn resolve(&self) -> Result<DynSolType> {
122        self.ty.resolve()
123    }
124}
125
126impl Specifier<DynSolType> for Parameters<'_> {
127    #[inline]
128    fn resolve(&self) -> Result<DynSolType> {
129        tuple(&self.params).map(DynSolType::Tuple)
130    }
131}
132
133impl Specifier<DynSolType> for Param {
134    #[inline]
135    fn resolve(&self) -> Result<DynSolType> {
136        resolve_param(
137            &self.ty,
138            &self.components,
139            #[cfg(feature = "eip712")]
140            self.internal_type(),
141        )
142    }
143}
144
145impl Specifier<DynSolType> for EventParam {
146    #[inline]
147    fn resolve(&self) -> Result<DynSolType> {
148        resolve_param(
149            &self.ty,
150            &self.components,
151            #[cfg(feature = "eip712")]
152            self.internal_type(),
153        )
154    }
155}
156
157impl Specifier<DynSolCall> for Function {
158    #[inline]
159    fn resolve(&self) -> Result<DynSolCall> {
160        let selector = self.selector();
161        let parameters =
162            self.inputs.iter().map(Specifier::<DynSolType>::resolve).collect::<Result<Vec<_>>>()?;
163        let returns = self
164            .outputs
165            .iter()
166            .map(Specifier::<DynSolType>::resolve)
167            .collect::<Result<Vec<_>>>()?
168            .into();
169        let method = self.name.clone();
170
171        Ok(DynSolCall::new(selector, parameters, Some(method), returns))
172    }
173}
174
175fn resolve_param(
176    ty: &str,
177    components: &[Param],
178    #[cfg(feature = "eip712")] it: Option<&InternalType>,
179) -> Result<DynSolType> {
180    let ty = TypeSpecifier::parse(ty)?;
181
182    // type is simple, and we can resolve it via the specifier
183    if components.is_empty() {
184        return ty.resolve();
185    }
186
187    // type is complex
188    let tuple = tuple(components)?;
189
190    #[cfg(feature = "eip712")]
191    let resolved = if let Some((_, name)) = it.and_then(|i| i.as_struct()) {
192        DynSolType::CustomStruct {
193            // skip array sizes, since we have them already from parsing `ty`
194            name: name.split('[').next().unwrap().into(),
195            prop_names: components.iter().map(|c| c.name.clone()).collect(),
196            tuple,
197        }
198    } else {
199        DynSolType::Tuple(tuple)
200    };
201
202    #[cfg(not(feature = "eip712"))]
203    let resolved = DynSolType::Tuple(tuple);
204
205    Ok(resolved.array_wrap_from_iter(ty.sizes))
206}
207
208fn tuple<T: Specifier<DynSolType>>(slice: &[T]) -> Result<Vec<DynSolType>> {
209    let mut types = Vec::with_capacity(slice.len());
210    for ty in slice {
211        types.push(ty.resolve()?);
212    }
213    Ok(types)
214}
215
216macro_rules! deref_impls {
217    ($($(#[$attr:meta])* [$($gen:tt)*] $t:ty),+ $(,)?) => {$(
218        $(#[$attr])*
219        impl<$($gen)*> Specifier<DynSolType> for $t {
220            #[inline]
221            fn resolve(&self) -> Result<DynSolType> {
222                (**self).resolve()
223            }
224        }
225    )+};
226}
227
228deref_impls! {
229    [] alloc::string::String,
230    [T: ?Sized + Specifier<DynSolType>] &T,
231    [T: ?Sized + Specifier<DynSolType>] &mut T,
232    [T: ?Sized + Specifier<DynSolType>] alloc::boxed::Box<T>,
233    [T: ?Sized + alloc::borrow::ToOwned + Specifier<DynSolType>] alloc::borrow::Cow<'_, T>,
234    [T: ?Sized + Specifier<DynSolType>] alloc::rc::Rc<T>,
235    [T: ?Sized + Specifier<DynSolType>] alloc::sync::Arc<T>,
236}
237
238#[cfg(test)]
239mod tests {
240    use super::*;
241    use alloc::boxed::Box;
242
243    fn parse(s: &str) -> Result<DynSolType> {
244        s.parse()
245    }
246
247    #[test]
248    fn extra_close_parens() {
249        parse("(bool,uint256))").unwrap_err();
250        parse("bool,uint256))").unwrap_err();
251        parse("bool,uint256)").unwrap_err();
252    }
253
254    #[test]
255    fn extra_open_parents() {
256        parse("((bool,uint256)").unwrap_err();
257        parse("((bool,uint256").unwrap_err();
258        parse("(bool,uint256").unwrap_err();
259    }
260
261    #[test]
262    fn it_parses_tuples() {
263        assert_eq!(parse("(bool,)"), Ok(DynSolType::Tuple(vec![DynSolType::Bool])));
264        assert_eq!(
265            parse("(uint256,uint256)"),
266            Ok(DynSolType::Tuple(vec![DynSolType::Uint(256), DynSolType::Uint(256)]))
267        );
268        assert_eq!(
269            parse("(uint256,uint256)[2]"),
270            Ok(DynSolType::FixedArray(
271                Box::new(DynSolType::Tuple(vec![DynSolType::Uint(256), DynSolType::Uint(256)])),
272                2
273            ))
274        );
275    }
276
277    #[test]
278    fn nested_tuples() {
279        assert_eq!(
280            parse("(bool,(uint256,uint256))"),
281            Ok(DynSolType::Tuple(vec![
282                DynSolType::Bool,
283                DynSolType::Tuple(vec![DynSolType::Uint(256), DynSolType::Uint(256)])
284            ]))
285        );
286        assert_eq!(
287            parse("(((bool),),)"),
288            Ok(DynSolType::Tuple(vec![DynSolType::Tuple(vec![DynSolType::Tuple(vec![
289                DynSolType::Bool
290            ])])]))
291        );
292    }
293
294    #[test]
295    fn empty_tuples() {
296        assert_eq!(parse("()"), Ok(DynSolType::Tuple(vec![])));
297        assert_eq!(
298            parse("((),())"),
299            Ok(DynSolType::Tuple(vec![DynSolType::Tuple(vec![]), DynSolType::Tuple(vec![])]))
300        );
301        assert_eq!(
302            parse("((()))"),
303            Ok(DynSolType::Tuple(vec![DynSolType::Tuple(vec![DynSolType::Tuple(vec![])])]))
304        );
305    }
306
307    #[test]
308    fn it_parses_simple_types() {
309        assert_eq!(parse("uint256"), Ok(DynSolType::Uint(256)));
310        assert_eq!(parse("uint8"), Ok(DynSolType::Uint(8)));
311        assert_eq!(parse("uint"), Ok(DynSolType::Uint(256)));
312        assert_eq!(parse("address"), Ok(DynSolType::Address));
313        assert_eq!(parse("bool"), Ok(DynSolType::Bool));
314        assert_eq!(parse("string"), Ok(DynSolType::String));
315        assert_eq!(parse("bytes"), Ok(DynSolType::Bytes));
316        assert_eq!(parse("bytes32"), Ok(DynSolType::FixedBytes(32)));
317    }
318
319    #[test]
320    fn it_parses_complex_solidity_types() {
321        assert_eq!(parse("uint256[]"), Ok(DynSolType::Array(Box::new(DynSolType::Uint(256)))));
322        assert_eq!(
323            parse("uint256[2]"),
324            Ok(DynSolType::FixedArray(Box::new(DynSolType::Uint(256)), 2))
325        );
326        assert_eq!(
327            parse("uint256[2][3]"),
328            Ok(DynSolType::FixedArray(
329                Box::new(DynSolType::FixedArray(Box::new(DynSolType::Uint(256)), 2)),
330                3
331            ))
332        );
333        assert_eq!(
334            parse("uint256[][][]"),
335            Ok(DynSolType::Array(Box::new(DynSolType::Array(Box::new(DynSolType::Array(
336                Box::new(DynSolType::Uint(256))
337            ))))))
338        );
339
340        assert_eq!(
341            parse("tuple(address,bytes,(bool,(string,uint256)[][3]))[2]"),
342            Ok(DynSolType::FixedArray(
343                Box::new(DynSolType::Tuple(vec![
344                    DynSolType::Address,
345                    DynSolType::Bytes,
346                    DynSolType::Tuple(vec![
347                        DynSolType::Bool,
348                        DynSolType::FixedArray(
349                            Box::new(DynSolType::Array(Box::new(DynSolType::Tuple(vec![
350                                DynSolType::String,
351                                DynSolType::Uint(256)
352                            ])))),
353                            3
354                        ),
355                    ]),
356                ])),
357                2
358            ))
359        );
360    }
361
362    #[test]
363    fn library_enum_workaround() {
364        assert_eq!(parse("MyLibrary.MyEnum"), Ok(DynSolType::Uint(8)));
365        assert_eq!(
366            parse("MyLibrary.MyEnum[]"),
367            Ok(DynSolType::Array(Box::new(DynSolType::Uint(8))))
368        );
369    }
370}