alloy_dyn_abi/
coerce.rs

1use crate::{dynamic::ty::as_tuple, DynSolType, DynSolValue, Result};
2use alloc::vec::Vec;
3use alloy_primitives::{Address, Function, Sign, I256, U256};
4use alloy_sol_types::Word;
5use core::fmt;
6use parser::{
7    new_input,
8    utils::{array_parser, char_parser, spanned},
9    Input,
10};
11use winnow::{
12    ascii::{alpha0, alpha1, digit1, hex_digit0, hex_digit1, space0},
13    combinator::{cut_err, dispatch, empty, fail, opt, preceded, trace},
14    error::{
15        AddContext, ContextError, ErrMode, FromExternalError, ParserError, StrContext,
16        StrContextValue,
17    },
18    stream::Stream,
19    token::take_while,
20    ModalParser, ModalResult, Parser,
21};
22
23impl DynSolType {
24    /// Coerces a string into a [`DynSolValue`] via this type.
25    ///
26    /// # Syntax
27    ///
28    /// - [`Bool`](DynSolType::Bool): `true|false`
29    /// - [`Int`](DynSolType::Int): `[+-]?{Uint}`
30    /// - [`Uint`](DynSolType::Uint): `{literal}(\.[0-9]+)?(\s*{unit})?`
31    ///   - literal: base 2, 8, 10, or 16 integer literal. If not in base 10, must be prefixed with
32    ///     `0b`, `0o`, or `0x` respectively.
33    ///   - unit: same as [Solidity ether units](https://docs.soliditylang.org/en/latest/units-and-global-variables.html#ether-units)
34    ///   - decimals with more digits than the unit's exponent value are not allowed
35    /// - [`FixedBytes`](DynSolType::FixedBytes): `(0x)?[0-9A-Fa-f]{$0*2}`
36    /// - [`Address`](DynSolType::Address): `(0x)?[0-9A-Fa-f]{40}`
37    /// - [`Function`](DynSolType::Function): `(0x)?[0-9A-Fa-f]{48}`
38    /// - [`Bytes`](DynSolType::Bytes): `(0x)?[0-9A-Fa-f]+`
39    /// - [`String`](DynSolType::String): `.*`
40    ///   - can be surrounded by a pair of `"` or `'`
41    ///   - trims whitespace if not surrounded
42    /// - [`Array`](DynSolType::Array): any number of the inner type delimited by commas (`,`) and
43    ///   surrounded by brackets (`[]`)
44    /// - [`FixedArray`](DynSolType::FixedArray): exactly the given number of the inner type
45    ///   delimited by commas (`,`) and surrounded by brackets (`[]`)
46    /// - [`Tuple`](DynSolType::Tuple): the inner types delimited by commas (`,`) and surrounded by
47    ///   parentheses (`()`)
48    #[cfg_attr(
49        feature = "eip712",
50        doc = "- [`CustomStruct`](DynSolType::CustomStruct): the same as `Tuple`"
51    )]
52    ///
53    /// # Examples
54    ///
55    /// ```
56    /// use alloy_dyn_abi::{DynSolType, DynSolValue};
57    /// use alloy_primitives::U256;
58    ///
59    /// let ty: DynSolType = "(uint256,string)[]".parse()?;
60    /// let value = ty.coerce_str("[(0, \"hello\"), (4.2e1, \"world\")]")?;
61    /// assert_eq!(
62    ///     value,
63    ///     DynSolValue::Array(vec![
64    ///         DynSolValue::Tuple(vec![
65    ///             DynSolValue::Uint(U256::from(0), 256),
66    ///             DynSolValue::String(String::from("hello"))
67    ///         ]),
68    ///         DynSolValue::Tuple(vec![
69    ///             DynSolValue::Uint(U256::from(42), 256),
70    ///             DynSolValue::String(String::from("world"))
71    ///         ]),
72    ///     ])
73    /// );
74    /// assert!(value.matches(&ty));
75    /// assert_eq!(value.as_type().unwrap(), ty);
76    /// # Ok::<_, alloy_dyn_abi::Error>(())
77    /// ```
78    #[doc(alias = "tokenize")] // from ethabi
79    pub fn coerce_str(&self, s: &str) -> Result<DynSolValue> {
80        ValueParser::new(self)
81            .parse(new_input(s))
82            .map_err(|e| crate::Error::TypeParser(parser::Error::parser(e)))
83    }
84}
85
86struct ValueParser<'a> {
87    ty: &'a DynSolType,
88    list_end: Option<char>,
89}
90
91impl<'i> Parser<Input<'i>, DynSolValue, ErrMode<ContextError>> for ValueParser<'_> {
92    fn parse_next(&mut self, input: &mut Input<'i>) -> ModalResult<DynSolValue, ContextError> {
93        #[cfg(feature = "debug")]
94        let name = self.ty.sol_type_name();
95        #[cfg(not(feature = "debug"))]
96        let name = "value_parser";
97        trace(name, move |input: &mut Input<'i>| match self.ty {
98            DynSolType::Bool => bool(input).map(DynSolValue::Bool),
99            &DynSolType::Int(size) => {
100                int(size).parse_next(input).map(|int| DynSolValue::Int(int, size))
101            }
102            &DynSolType::Uint(size) => {
103                uint(size).parse_next(input).map(|uint| DynSolValue::Uint(uint, size))
104            }
105            &DynSolType::FixedBytes(size) => {
106                fixed_bytes(size).parse_next(input).map(|word| DynSolValue::FixedBytes(word, size))
107            }
108            DynSolType::Address => address(input).map(DynSolValue::Address),
109            DynSolType::Function => function(input).map(DynSolValue::Function),
110            DynSolType::Bytes => bytes(input).map(DynSolValue::Bytes),
111            DynSolType::String => {
112                self.string().parse_next(input).map(|s| DynSolValue::String(s.into()))
113            }
114            DynSolType::Array(ty) => self.in_list(']', |this| {
115                this.with(ty).array().parse_next(input).map(DynSolValue::Array)
116            }),
117            DynSolType::FixedArray(ty, len) => self.in_list(']', |this| {
118                this.with(ty).fixed_array(*len).parse_next(input).map(DynSolValue::FixedArray)
119            }),
120            as_tuple!(DynSolType tys) => {
121                self.in_list(')', |this| this.tuple(tys).parse_next(input).map(DynSolValue::Tuple))
122            }
123        })
124        .parse_next(input)
125    }
126}
127
128impl<'a> ValueParser<'a> {
129    #[inline]
130    const fn new(ty: &'a DynSolType) -> Self {
131        Self { list_end: None, ty }
132    }
133
134    #[inline]
135    fn in_list<F: FnOnce(&mut Self) -> R, R>(&mut self, list_end: char, f: F) -> R {
136        let prev = core::mem::replace(&mut self.list_end, Some(list_end));
137        let r = f(self);
138        self.list_end = prev;
139        r
140    }
141
142    #[inline]
143    const fn with(&self, ty: &'a DynSolType) -> Self {
144        Self { list_end: self.list_end, ty }
145    }
146
147    #[inline]
148    fn string<'s, 'i: 's>(&'s self) -> impl ModalParser<Input<'i>, &'i str, ContextError> + 's {
149        trace("string", |input: &mut Input<'i>| {
150            let Some(delim) = input.chars().next() else {
151                return Ok("");
152            };
153            let has_delim = matches!(delim, '"' | '\'');
154            if has_delim {
155                let _ = input.next_token();
156            }
157
158            // TODO: escapes?
159            let mut s = if has_delim || self.list_end.is_some() {
160                let (chs, l) = if has_delim {
161                    ([delim, '\0'], 1)
162                } else if let Some(c) = self.list_end {
163                    ([',', c], 2)
164                } else {
165                    unreachable!()
166                };
167                let min = if has_delim { 0 } else { 1 };
168                take_while(min.., move |c: char| !unsafe { chs.get_unchecked(..l) }.contains(&c))
169                    .parse_next(input)?
170            } else {
171                input.next_slice(input.len())
172            };
173
174            if has_delim {
175                cut_err(char_parser(delim))
176                    .context(StrContext::Label("string"))
177                    .parse_next(input)?;
178            } else {
179                s = s.trim_end();
180            }
181
182            Ok(s)
183        })
184    }
185
186    #[inline]
187    fn array<'i: 'a>(self) -> impl ModalParser<Input<'i>, Vec<DynSolValue>, ContextError> + 'a {
188        #[cfg(feature = "debug")]
189        let name = format!("{}[]", self.ty);
190        #[cfg(not(feature = "debug"))]
191        let name = "array";
192        trace(name, array_parser(self))
193    }
194
195    #[inline]
196    fn fixed_array<'i: 'a>(
197        self,
198        len: usize,
199    ) -> impl ModalParser<Input<'i>, Vec<DynSolValue>, ContextError> + 'a {
200        #[cfg(feature = "debug")]
201        let name = format!("{}[{len}]", self.ty);
202        #[cfg(not(feature = "debug"))]
203        let name = "fixed_array";
204        trace(
205            name,
206            array_parser(self).try_map(move |values: Vec<DynSolValue>| {
207                if values.len() == len {
208                    Ok(values)
209                } else {
210                    Err(Error::FixedArrayLengthMismatch(len, values.len()))
211                }
212            }),
213        )
214    }
215
216    #[inline]
217    #[allow(clippy::ptr_arg)]
218    fn tuple<'i: 's, 't: 's, 's>(
219        &'s self,
220        tuple: &'t Vec<DynSolType>,
221    ) -> impl ModalParser<Input<'i>, Vec<DynSolValue>, ContextError> + 's {
222        #[cfg(feature = "debug")]
223        let name = DynSolType::Tuple(tuple.clone()).to_string();
224        #[cfg(not(feature = "debug"))]
225        let name = "tuple";
226        trace(name, move |input: &mut Input<'i>| {
227            space0(input)?;
228            char_parser('(').parse_next(input)?;
229
230            let mut values = Vec::with_capacity(tuple.len());
231            for (i, ty) in tuple.iter().enumerate() {
232                if i > 0 {
233                    space0(input)?;
234                    char_parser(',').parse_next(input)?;
235                }
236                space0(input)?;
237                values.push(self.with(ty).parse_next(input)?);
238            }
239
240            space0(input)?;
241            char_parser(')').parse_next(input)?;
242
243            Ok(values)
244        })
245    }
246}
247
248#[derive(Debug)]
249enum Error {
250    IntOverflow,
251    FractionalNotAllowed(U256),
252    NegativeUnits,
253    TooManyDecimals(usize, usize),
254    InvalidFixedBytesLength(usize),
255    FixedArrayLengthMismatch(usize, usize),
256    EmptyHexStringWithoutPrefix,
257}
258
259impl core::error::Error for Error {}
260
261impl fmt::Display for Error {
262    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
263        match self {
264            Self::IntOverflow => f.write_str("number too large to fit in target type"),
265            Self::TooManyDecimals(expected, actual) => {
266                write!(f, "expected at most {expected} decimals, got {actual}")
267            }
268            Self::FractionalNotAllowed(n) => write!(f, "non-zero fraction .{n} not allowed"),
269            Self::NegativeUnits => f.write_str("negative units not allowed"),
270            Self::InvalidFixedBytesLength(len) => {
271                write!(f, "fixed bytes length {len} greater than 32")
272            }
273            Self::FixedArrayLengthMismatch(expected, actual) => {
274                write!(f, "fixed array length mismatch: expected {expected} elements, got {actual}")
275            }
276            Self::EmptyHexStringWithoutPrefix => {
277                f.write_str("expected hex digits or the `0x` prefix for an empty hex string")
278            }
279        }
280    }
281}
282
283#[inline]
284fn bool(input: &mut Input<'_>) -> ModalResult<bool> {
285    trace(
286        "bool",
287        dispatch! {alpha1.context(StrContext::Label("boolean"));
288            "true" => empty.value(true),
289            "false" => empty.value(false),
290            _ => fail
291        }
292        .context(StrContext::Label("boolean")),
293    )
294    .parse_next(input)
295}
296
297#[inline]
298fn int<'i>(size: usize) -> impl ModalParser<Input<'i>, I256, ContextError> {
299    #[cfg(feature = "debug")]
300    let name = format!("int{size}");
301    #[cfg(not(feature = "debug"))]
302    let name = "int";
303    trace(
304        name,
305        (int_sign, uint(size)).try_map(move |(sign, abs)| {
306            if !sign.is_negative() && abs.bit_len() > size - 1 {
307                return Err(Error::IntOverflow);
308            }
309            I256::checked_from_sign_and_abs(sign, abs).ok_or(Error::IntOverflow)
310        }),
311    )
312}
313
314#[inline]
315fn int_sign(input: &mut Input<'_>) -> ModalResult<Sign> {
316    trace("int_sign", |input: &mut Input<'_>| match input.as_bytes().first() {
317        Some(b'+') => {
318            let _ = input.next_slice(1);
319            Ok(Sign::Positive)
320        }
321        Some(b'-') => {
322            let _ = input.next_slice(1);
323            Ok(Sign::Negative)
324        }
325        Some(_) | None => Ok(Sign::Positive),
326    })
327    .parse_next(input)
328}
329
330#[inline]
331fn uint<'i>(len: usize) -> impl ModalParser<Input<'i>, U256, ContextError> {
332    #[cfg(feature = "debug")]
333    let name = format!("uint{len}");
334    #[cfg(not(feature = "debug"))]
335    let name = "uint";
336    trace(name, move |input: &mut Input<'_>| {
337        let intpart = prefixed_int(input)?;
338        let fract =
339            opt(preceded(
340                '.',
341                cut_err(digit1.context(StrContext::Expected(StrContextValue::Description(
342                    "at least one digit",
343                )))),
344            ))
345            .parse_next(input)?;
346
347        let intpart =
348            intpart.parse::<U256>().map_err(|e| ErrMode::from_external_error(input, e))?;
349        let e = opt(scientific_notation).parse_next(input)?.unwrap_or(0);
350
351        let _ = space0(input)?;
352        let units = int_units(input)?;
353
354        let units = units as isize + e;
355        if units < 0 {
356            return Err(ErrMode::from_external_error(input, Error::NegativeUnits));
357        }
358        let units = units as usize;
359
360        let uint = if let Some(fract) = fract {
361            let fract_uint = U256::from_str_radix(fract, 10)
362                .map_err(|e| ErrMode::from_external_error(input, e))?;
363
364            if units == 0 && !fract_uint.is_zero() {
365                return Err(ErrMode::from_external_error(
366                    input,
367                    Error::FractionalNotAllowed(fract_uint),
368                ));
369            }
370
371            if fract.len() > units {
372                return Err(ErrMode::from_external_error(
373                    input,
374                    Error::TooManyDecimals(units, fract.len()),
375                ));
376            }
377
378            // (intpart * 10^fract.len() + fract) * 10^(units-fract.len())
379            (|| -> Option<U256> {
380                let extension = U256::from(10u64).checked_pow(U256::from(fract.len()))?;
381                let extended = intpart.checked_mul(extension)?;
382                let uint = fract_uint.checked_add(extended)?;
383                let units = U256::from(10u64).checked_pow(U256::from(units - fract.len()))?;
384                uint.checked_mul(units)
385            })()
386        } else if units > 0 {
387            // intpart * 10^units
388            (|| -> Option<U256> {
389                let units = U256::from(10u64).checked_pow(U256::from(units))?;
390                intpart.checked_mul(units)
391            })()
392        } else {
393            Some(intpart)
394        }
395        .ok_or_else(|| ErrMode::from_external_error(input, Error::IntOverflow))?;
396
397        if uint.bit_len() > len {
398            return Err(ErrMode::from_external_error(input, Error::IntOverflow));
399        }
400
401        Ok(uint)
402    })
403}
404
405#[inline]
406fn prefixed_int<'i>(input: &mut Input<'i>) -> ModalResult<&'i str> {
407    trace(
408        "prefixed_int",
409        spanned(|input: &mut Input<'i>| {
410            let has_prefix =
411                matches!(input.get(..2), Some("0b" | "0B" | "0o" | "0O" | "0x" | "0X"));
412            let checkpoint = input.checkpoint();
413            if has_prefix {
414                let _ = input.next_slice(2);
415                // parse hex since it's the most general
416                hex_digit1(input)
417            } else {
418                digit1(input)
419            }
420            .map_err(|e: ErrMode<_>| {
421                e.add_context(
422                    input,
423                    &checkpoint,
424                    StrContext::Expected(StrContextValue::Description("at least one digit")),
425                )
426            })
427        }),
428    )
429    .parse_next(input)
430    .map(|(s, _)| s)
431}
432
433#[inline]
434fn int_units(input: &mut Input<'_>) -> ModalResult<usize> {
435    trace(
436        "int_units",
437        dispatch! {alpha0;
438            "ether" => empty.value(18),
439            "gwei" | "nano" | "nanoether" => empty.value(9),
440            "" | "wei" => empty.value(0),
441            _ => fail,
442        },
443    )
444    .parse_next(input)
445}
446
447#[inline]
448fn scientific_notation(input: &mut Input<'_>) -> ModalResult<isize> {
449    // Check if we have 'e' or 'E' followed by an optional sign and digits
450    if !matches!(input.chars().next(), Some('e' | 'E')) {
451        return Err(ErrMode::from_input(input));
452    }
453    let _ = input.next_token();
454    winnow::ascii::dec_int(input)
455}
456
457#[inline]
458fn fixed_bytes<'i>(len: usize) -> impl ModalParser<Input<'i>, Word, ContextError> {
459    #[cfg(feature = "debug")]
460    let name = format!("bytes{len}");
461    #[cfg(not(feature = "debug"))]
462    let name = "bytesN";
463    trace(name, move |input: &mut Input<'_>| {
464        if len > Word::len_bytes() {
465            return Err(
466                ErrMode::from_external_error(input, Error::InvalidFixedBytesLength(len)).cut()
467            );
468        }
469
470        let hex = hex_str(input)?;
471        let mut out = Word::ZERO;
472        match hex::decode_to_slice(hex, &mut out[..len]) {
473            Ok(()) => Ok(out),
474            Err(e) => Err(ErrMode::from_external_error(input, e).cut()),
475        }
476    })
477}
478
479#[inline]
480fn address(input: &mut Input<'_>) -> ModalResult<Address> {
481    trace("address", hex_str.try_map(hex::FromHex::from_hex)).parse_next(input)
482}
483
484#[inline]
485fn function(input: &mut Input<'_>) -> ModalResult<Function> {
486    trace("function", hex_str.try_map(hex::FromHex::from_hex)).parse_next(input)
487}
488
489#[inline]
490fn bytes(input: &mut Input<'_>) -> ModalResult<Vec<u8>> {
491    trace("bytes", hex_str.try_map(hex::decode)).parse_next(input)
492}
493
494#[inline]
495fn hex_str<'i>(input: &mut Input<'i>) -> ModalResult<&'i str> {
496    trace("hex_str", |input: &mut Input<'i>| {
497        // Allow empty `bytes` only with a prefix.
498        let has_prefix = opt("0x").parse_next(input)?.is_some();
499        let s = hex_digit0(input)?;
500        if !has_prefix && s.is_empty() {
501            return Err(ErrMode::from_external_error(input, Error::EmptyHexStringWithoutPrefix));
502        }
503        Ok(s)
504    })
505    .parse_next(input)
506}
507
508#[cfg(test)]
509mod tests {
510    use super::*;
511    use alloc::{
512        boxed::Box,
513        string::{String, ToString},
514    };
515    use alloy_primitives::address;
516    use core::str::FromStr;
517
518    fn uint_test(s: &str, expected: Result<&str, ()>) {
519        for (ty, negate) in [
520            (DynSolType::Uint(256), false),
521            (DynSolType::Int(256), false),
522            (DynSolType::Int(256), true),
523        ] {
524            let s = if negate { &format!("-{s}") } else { s };
525            let expected = if negate {
526                expected.map(|s| format!("-{s}"))
527            } else {
528                expected.map(|s| s.to_string())
529            };
530            let d = format!("{s:?} as {ty:?}");
531
532            let actual = ty.coerce_str(s);
533            match (actual, expected) {
534                (Ok(actual), Ok(expected)) => match (actual, ty) {
535                    (DynSolValue::Uint(v, 256), DynSolType::Uint(256)) => {
536                        assert_eq!(v, expected.parse::<U256>().unwrap(), "{d}");
537                    }
538                    (DynSolValue::Int(v, 256), DynSolType::Int(256)) => {
539                        assert_eq!(v, expected.parse::<I256>().unwrap(), "{d}");
540                    }
541                    (actual, _) => panic!("{d}: unexpected value: {actual:?}"),
542                },
543                (Err(_), Err(())) => {}
544                (Ok(actual), Err(_)) => panic!("{d}: expected failure, got {actual:?}"),
545                (Err(e), Ok(_)) => panic!("{d}: {e:?}"),
546            }
547        }
548    }
549
550    #[track_caller]
551    fn assert_error_contains(e: &impl core::fmt::Display, s: &str) {
552        if cfg!(feature = "std") {
553            let es = e.to_string();
554            assert!(es.contains(s), "{s:?} not in {es:?}");
555        }
556    }
557
558    #[test]
559    fn coerce_bool() {
560        assert_eq!(DynSolType::Bool.coerce_str("true").unwrap(), DynSolValue::Bool(true));
561        assert_eq!(DynSolType::Bool.coerce_str("false").unwrap(), DynSolValue::Bool(false));
562
563        assert!(DynSolType::Bool.coerce_str("").is_err());
564        assert!(DynSolType::Bool.coerce_str("0").is_err());
565        assert!(DynSolType::Bool.coerce_str("1").is_err());
566        assert!(DynSolType::Bool.coerce_str("tru").is_err());
567    }
568
569    #[test]
570    fn coerce_int() {
571        assert_eq!(
572            DynSolType::Int(256)
573                .coerce_str("0x1111111111111111111111111111111111111111111111111111111111111111")
574                .unwrap(),
575            DynSolValue::Int(I256::from_be_bytes([0x11; 32]), 256)
576        );
577
578        assert_eq!(
579            DynSolType::Int(256)
580                .coerce_str("0x2222222222222222222222222222222222222222222222222222222222222222")
581                .unwrap(),
582            DynSolValue::Int(I256::from_be_bytes([0x22; 32]), 256)
583        );
584
585        assert_eq!(
586            DynSolType::Int(256)
587                .coerce_str("0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")
588                .unwrap(),
589            DynSolValue::Int(I256::MAX, 256)
590        );
591        assert!(DynSolType::Int(256)
592            .coerce_str("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")
593            .is_err());
594
595        assert_eq!(
596            DynSolType::Int(256).coerce_str("0").unwrap(),
597            DynSolValue::Int(I256::ZERO, 256)
598        );
599
600        assert_eq!(
601            DynSolType::Int(256).coerce_str("-0").unwrap(),
602            DynSolValue::Int(I256::ZERO, 256)
603        );
604
605        assert_eq!(
606            DynSolType::Int(256).coerce_str("+0").unwrap(),
607            DynSolValue::Int(I256::ZERO, 256)
608        );
609
610        assert_eq!(
611            DynSolType::Int(256).coerce_str("-1").unwrap(),
612            DynSolValue::Int(I256::MINUS_ONE, 256)
613        );
614
615        assert_eq!(
616            DynSolType::Int(256)
617                .coerce_str(
618                    "57896044618658097711785492504343953926634992332820282019728792003956564819967"
619                )
620                .unwrap(),
621            DynSolValue::Int(I256::MAX, 256)
622        );
623        assert_eq!(
624            DynSolType::Int(256).coerce_str("-57896044618658097711785492504343953926634992332820282019728792003956564819968").unwrap(),
625            DynSolValue::Int(I256::MIN, 256)
626        );
627    }
628
629    #[test]
630    fn coerce_int_overflow() {
631        assert_eq!(
632            DynSolType::Int(8).coerce_str("126").unwrap(),
633            DynSolValue::Int(I256::try_from(126).unwrap(), 8),
634        );
635        assert_eq!(
636            DynSolType::Int(8).coerce_str("127").unwrap(),
637            DynSolValue::Int(I256::try_from(127).unwrap(), 8),
638        );
639        assert!(DynSolType::Int(8).coerce_str("128").is_err());
640        assert!(DynSolType::Int(8).coerce_str("129").is_err());
641        assert_eq!(
642            DynSolType::Int(16).coerce_str("128").unwrap(),
643            DynSolValue::Int(I256::try_from(128).unwrap(), 16),
644        );
645        assert_eq!(
646            DynSolType::Int(16).coerce_str("129").unwrap(),
647            DynSolValue::Int(I256::try_from(129).unwrap(), 16),
648        );
649
650        assert_eq!(
651            DynSolType::Int(8).coerce_str("-1").unwrap(),
652            DynSolValue::Int(I256::MINUS_ONE, 8),
653        );
654        assert_eq!(
655            DynSolType::Int(16).coerce_str("-1").unwrap(),
656            DynSolValue::Int(I256::MINUS_ONE, 16),
657        );
658    }
659
660    #[test]
661    fn coerce_uint() {
662        assert_eq!(
663            DynSolType::Uint(256)
664                .coerce_str("0x1111111111111111111111111111111111111111111111111111111111111111")
665                .unwrap(),
666            DynSolValue::Uint(U256::from_be_bytes([0x11; 32]), 256)
667        );
668
669        assert_eq!(
670            DynSolType::Uint(256)
671                .coerce_str("0x2222222222222222222222222222222222222222222222222222222222222222")
672                .unwrap(),
673            DynSolValue::Uint(U256::from_be_bytes([0x22; 32]), 256)
674        );
675
676        assert_eq!(
677            DynSolType::Uint(256)
678                .coerce_str("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")
679                .unwrap(),
680            DynSolValue::Uint(U256::from_be_bytes([0xff; 32]), 256)
681        );
682
683        // 255 bits fails
684        assert!(DynSolType::Uint(255)
685            .coerce_str("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")
686            .is_err());
687
688        assert_eq!(
689            DynSolType::Uint(256)
690                .coerce_str("115792089237316195423570985008687907853269984665640564039457584007913129639935")
691                .unwrap(),
692            DynSolValue::Uint(U256::MAX, 256)
693        );
694
695        assert_eq!(
696            DynSolType::Uint(256).coerce_str("0").unwrap(),
697            DynSolValue::Uint(U256::ZERO, 256)
698        );
699
700        assert_eq!(
701            DynSolType::Uint(256).coerce_str("1").unwrap(),
702            DynSolValue::Uint(U256::from(1), 256)
703        );
704    }
705
706    #[test]
707    fn coerce_uint_overflow() {
708        assert_eq!(
709            DynSolType::Uint(8).coerce_str("254").unwrap(),
710            DynSolValue::Uint(U256::from(254), 8),
711        );
712        assert_eq!(
713            DynSolType::Uint(8).coerce_str("255").unwrap(),
714            DynSolValue::Uint(U256::from(255), 8),
715        );
716        assert!(DynSolType::Uint(8).coerce_str("256").is_err());
717        assert!(DynSolType::Uint(8).coerce_str("257").is_err());
718        assert_eq!(
719            DynSolType::Uint(16).coerce_str("256").unwrap(),
720            DynSolValue::Uint(U256::from(256), 16),
721        );
722        assert_eq!(
723            DynSolType::Uint(16).coerce_str("257").unwrap(),
724            DynSolValue::Uint(U256::from(257), 16),
725        );
726    }
727
728    #[test]
729    fn coerce_uint_wei() {
730        assert_eq!(
731            DynSolType::Uint(256).coerce_str("1wei").unwrap(),
732            DynSolValue::Uint(U256::from(1), 256)
733        );
734        assert_eq!(
735            DynSolType::Uint(256).coerce_str("1 wei").unwrap(),
736            DynSolValue::Uint(U256::from(1), 256)
737        );
738
739        assert!(DynSolType::Uint(256).coerce_str("1").is_ok());
740        assert!(DynSolType::Uint(256).coerce_str("1.").is_err());
741        assert!(DynSolType::Uint(256).coerce_str("1 .").is_err());
742        assert!(DynSolType::Uint(256).coerce_str("1 .0").is_err());
743        assert!(DynSolType::Uint(256).coerce_str("1.wei").is_err());
744        assert!(DynSolType::Uint(256).coerce_str("1. wei").is_err());
745        assert!(DynSolType::Uint(256).coerce_str("1.0wei").is_err());
746        assert!(DynSolType::Uint(256).coerce_str("1.0 wei").is_err());
747        assert!(DynSolType::Uint(256).coerce_str("1.00wei").is_err());
748        assert!(DynSolType::Uint(256).coerce_str("1.00 wei").is_err());
749    }
750
751    #[test]
752    fn coerce_uint_gwei() {
753        assert_eq!(
754            DynSolType::Uint(256).coerce_str("1nano").unwrap(),
755            DynSolValue::Uint(U256::from_str("1000000000").unwrap(), 256)
756        );
757
758        assert_eq!(
759            DynSolType::Uint(256).coerce_str("1nanoether").unwrap(),
760            DynSolValue::Uint(U256::from_str("1000000000").unwrap(), 256)
761        );
762
763        assert_eq!(
764            DynSolType::Uint(256).coerce_str("1gwei").unwrap(),
765            DynSolValue::Uint(U256::from_str("1000000000").unwrap(), 256)
766        );
767
768        assert_eq!(
769            DynSolType::Uint(256).coerce_str("0.1 gwei").unwrap(),
770            DynSolValue::Uint(U256::from_str("100000000").unwrap(), 256)
771        );
772
773        assert_eq!(
774            DynSolType::Uint(256).coerce_str("0.000000001gwei").unwrap(),
775            DynSolValue::Uint(U256::from(1), 256)
776        );
777
778        assert_eq!(
779            DynSolType::Uint(256).coerce_str("0.123456789gwei").unwrap(),
780            DynSolValue::Uint(U256::from_str("123456789").unwrap(), 256)
781        );
782
783        assert_eq!(
784            DynSolType::Uint(256).coerce_str("123456789123.123456789gwei").unwrap(),
785            DynSolValue::Uint(U256::from_str("123456789123123456789").unwrap(), 256)
786        );
787    }
788
789    #[test]
790    fn coerce_uint_ether() {
791        assert_eq!(
792            DynSolType::Uint(256).coerce_str("10000000000ether").unwrap(),
793            DynSolValue::Uint(U256::from_str("10000000000000000000000000000").unwrap(), 256)
794        );
795
796        assert_eq!(
797            DynSolType::Uint(256).coerce_str("1ether").unwrap(),
798            DynSolValue::Uint(U256::from_str("1000000000000000000").unwrap(), 256)
799        );
800
801        assert_eq!(
802            DynSolType::Uint(256).coerce_str("0.01 ether").unwrap(),
803            DynSolValue::Uint(U256::from_str("10000000000000000").unwrap(), 256)
804        );
805
806        assert_eq!(
807            DynSolType::Uint(256).coerce_str("0.000000000000000001ether").unwrap(),
808            DynSolValue::Uint(U256::from(1), 256)
809        );
810
811        assert_eq!(
812            DynSolType::Uint(256).coerce_str("0.000000000000000001ether"),
813            DynSolType::Uint(256).coerce_str("1wei"),
814        );
815
816        assert_eq!(
817            DynSolType::Uint(256).coerce_str("0.123456789123456789ether").unwrap(),
818            DynSolValue::Uint(U256::from_str("123456789123456789").unwrap(), 256)
819        );
820
821        assert_eq!(
822            DynSolType::Uint(256).coerce_str("0.123456789123456000ether").unwrap(),
823            DynSolValue::Uint(U256::from_str("123456789123456000").unwrap(), 256)
824        );
825
826        assert_eq!(
827            DynSolType::Uint(256).coerce_str("0.1234567891234560ether").unwrap(),
828            DynSolValue::Uint(U256::from_str("123456789123456000").unwrap(), 256)
829        );
830
831        assert_eq!(
832            DynSolType::Uint(256).coerce_str("123456.123456789123456789ether").unwrap(),
833            DynSolValue::Uint(U256::from_str("123456123456789123456789").unwrap(), 256)
834        );
835
836        assert_eq!(
837            DynSolType::Uint(256).coerce_str("123456.123456789123456000ether").unwrap(),
838            DynSolValue::Uint(U256::from_str("123456123456789123456000").unwrap(), 256)
839        );
840
841        assert_eq!(
842            DynSolType::Uint(256).coerce_str("123456.1234567891234560ether").unwrap(),
843            DynSolValue::Uint(U256::from_str("123456123456789123456000").unwrap(), 256)
844        );
845    }
846
847    #[test]
848    fn coerce_uint_array_ether() {
849        assert_eq!(
850            DynSolType::Array(Box::new(DynSolType::Uint(256)))
851                .coerce_str("[ 1   ether,  10 ether ]")
852                .unwrap(),
853            DynSolValue::Array(vec![
854                DynSolValue::Uint(U256::from_str("1000000000000000000").unwrap(), 256),
855                DynSolValue::Uint(U256::from_str("10000000000000000000").unwrap(), 256),
856            ])
857        );
858    }
859
860    #[test]
861    fn coerce_uint_invalid_units() {
862        // 0.1 wei
863        assert!(DynSolType::Uint(256).coerce_str("0.1 wei").is_err());
864        assert!(DynSolType::Uint(256).coerce_str("0.0000000000000000001ether").is_err());
865
866        // 1 ether + 0.1 wei
867        assert!(DynSolType::Uint(256).coerce_str("1.0000000000000000001ether").is_err());
868
869        // 1_000_000_000 ether + 0.1 wei
870        assert!(DynSolType::Uint(256).coerce_str("1000000000.0000000000000000001ether").is_err());
871
872        assert!(DynSolType::Uint(256).coerce_str("0..1 gwei").is_err());
873
874        assert!(DynSolType::Uint(256).coerce_str("..1 gwei").is_err());
875
876        assert!(DynSolType::Uint(256).coerce_str("1. gwei").is_err());
877
878        assert!(DynSolType::Uint(256).coerce_str(".1 gwei").is_err());
879
880        assert!(DynSolType::Uint(256).coerce_str("2.1.1 gwei").is_err());
881
882        assert!(DynSolType::Uint(256).coerce_str(".1.1 gwei").is_err());
883
884        assert!(DynSolType::Uint(256).coerce_str("1abc").is_err());
885
886        assert!(DynSolType::Uint(256).coerce_str("1 gwei ").is_err());
887
888        assert!(DynSolType::Uint(256).coerce_str("g 1 gwei").is_err());
889
890        assert!(DynSolType::Uint(256).coerce_str("1gwei 1 gwei").is_err());
891    }
892
893    #[test]
894    fn coerce_fixed_bytes() {
895        let mk_word = |sl: &[u8]| {
896            let mut out = Word::ZERO;
897            out[..sl.len()].copy_from_slice(sl);
898            out
899        };
900
901        // not actually valid, but we don't care here
902        assert_eq!(
903            DynSolType::FixedBytes(0).coerce_str("0x").unwrap(),
904            DynSolValue::FixedBytes(mk_word(&[]), 0)
905        );
906
907        assert_eq!(
908            DynSolType::FixedBytes(1).coerce_str("0x00").unwrap(),
909            DynSolValue::FixedBytes(mk_word(&[0x00]), 1)
910        );
911        assert_eq!(
912            DynSolType::FixedBytes(1).coerce_str("0x00").unwrap(),
913            DynSolValue::FixedBytes(mk_word(&[0x00]), 1)
914        );
915        assert_eq!(
916            DynSolType::FixedBytes(2).coerce_str("0017").unwrap(),
917            DynSolValue::FixedBytes(mk_word(&[0x00, 0x17]), 2)
918        );
919        assert_eq!(
920            DynSolType::FixedBytes(3).coerce_str("123456").unwrap(),
921            DynSolValue::FixedBytes(mk_word(&[0x12, 0x34, 0x56]), 3)
922        );
923
924        let e = DynSolType::FixedBytes(1).coerce_str("").unwrap_err();
925        assert_error_contains(&e, &Error::EmptyHexStringWithoutPrefix.to_string());
926        let e = DynSolType::FixedBytes(1).coerce_str("0").unwrap_err();
927        assert_error_contains(&e, &hex::FromHexError::OddLength.to_string());
928        let e = DynSolType::FixedBytes(1).coerce_str("0x").unwrap_err();
929        assert_error_contains(&e, &hex::FromHexError::InvalidStringLength.to_string());
930        let e = DynSolType::FixedBytes(1).coerce_str("0x0").unwrap_err();
931        assert_error_contains(&e, &hex::FromHexError::OddLength.to_string());
932
933        let t = DynSolType::Array(Box::new(DynSolType::FixedBytes(1)));
934        let e = t.coerce_str("[0]").unwrap_err();
935        assert_error_contains(&e, &hex::FromHexError::OddLength.to_string());
936        let e = t.coerce_str("[0x]").unwrap_err();
937        assert_error_contains(&e, &hex::FromHexError::InvalidStringLength.to_string());
938        let e = t.coerce_str("[0x0]").unwrap_err();
939        assert_error_contains(&e, &hex::FromHexError::OddLength.to_string());
940
941        let t = DynSolType::Array(Box::new(DynSolType::Tuple(vec![DynSolType::FixedBytes(1)])));
942        let e = t.coerce_str("[(0)]").unwrap_err();
943        assert_error_contains(&e, &hex::FromHexError::OddLength.to_string());
944        let e = t.coerce_str("[(0x)]").unwrap_err();
945        assert_error_contains(&e, &hex::FromHexError::InvalidStringLength.to_string());
946        let e = t.coerce_str("[(0x0)]").unwrap_err();
947        assert_error_contains(&e, &hex::FromHexError::OddLength.to_string());
948    }
949
950    #[test]
951    fn coerce_address() {
952        // 38
953        assert!(DynSolType::Address.coerce_str("00000000000000000000000000000000000000").is_err());
954        // 39
955        assert!(DynSolType::Address.coerce_str("000000000000000000000000000000000000000").is_err());
956        // 40
957        assert_eq!(
958            DynSolType::Address.coerce_str("0000000000000000000000000000000000000000").unwrap(),
959            DynSolValue::Address(Address::ZERO)
960        );
961        assert_eq!(
962            DynSolType::Address.coerce_str("0x1111111111111111111111111111111111111111").unwrap(),
963            DynSolValue::Address(Address::new([0x11; 20]))
964        );
965        assert_eq!(
966            DynSolType::Address.coerce_str("2222222222222222222222222222222222222222").unwrap(),
967            DynSolValue::Address(Address::new([0x22; 20]))
968        );
969    }
970
971    #[test]
972    fn coerce_function() {
973        assert_eq!(
974            DynSolType::Function
975                .coerce_str("000000000000000000000000000000000000000000000000")
976                .unwrap(),
977            DynSolValue::Function(Function::ZERO)
978        );
979        assert_eq!(
980            DynSolType::Function
981                .coerce_str("0x111111111111111111111111111111111111111111111111")
982                .unwrap(),
983            DynSolValue::Function(Function::new([0x11; 24]))
984        );
985        assert_eq!(
986            DynSolType::Function
987                .coerce_str("222222222222222222222222222222222222222222222222")
988                .unwrap(),
989            DynSolValue::Function(Function::new([0x22; 24]))
990        );
991    }
992
993    #[test]
994    fn coerce_bytes() {
995        let e = DynSolType::Bytes.coerce_str("").unwrap_err();
996        assert_error_contains(&e, &Error::EmptyHexStringWithoutPrefix.to_string());
997
998        assert_eq!(DynSolType::Bytes.coerce_str("0x").unwrap(), DynSolValue::Bytes(vec![]));
999        assert!(DynSolType::Bytes.coerce_str("0x0").is_err());
1000        assert!(DynSolType::Bytes.coerce_str("0").is_err());
1001        assert_eq!(DynSolType::Bytes.coerce_str("00").unwrap(), DynSolValue::Bytes(vec![0]));
1002        assert_eq!(DynSolType::Bytes.coerce_str("0x00").unwrap(), DynSolValue::Bytes(vec![0]));
1003
1004        assert_eq!(
1005            DynSolType::Bytes.coerce_str("123456").unwrap(),
1006            DynSolValue::Bytes(vec![0x12, 0x34, 0x56])
1007        );
1008        assert_eq!(
1009            DynSolType::Bytes.coerce_str("0x0017").unwrap(),
1010            DynSolValue::Bytes(vec![0x00, 0x17])
1011        );
1012
1013        let t = DynSolType::Tuple(vec![DynSolType::Bytes, DynSolType::Bytes]);
1014        let e = t.coerce_str("(0, 0x0)").unwrap_err();
1015        assert_error_contains(&e, &hex::FromHexError::OddLength.to_string());
1016
1017        // TODO: cut_err in `array_parser` somehow
1018        /*
1019        let t = DynSolType::Array(Box::new(DynSolType::Tuple(vec![
1020            DynSolType::Bytes,
1021            DynSolType::Bytes,
1022        ])));
1023        let e = t.coerce_str("[(0, 0x0)]").unwrap_err();
1024        assert_error_contains(&e, &hex::FromHexError::OddLength.to_string());
1025
1026        let t = DynSolType::Array(Box::new(DynSolType::Tuple(vec![
1027            DynSolType::Bytes,
1028            DynSolType::Bytes,
1029        ])));
1030        let e = t.coerce_str("[(0x00, 0x0)]").unwrap_err();
1031        assert_error_contains(&e, &hex::FromHexError::OddLength.to_string());
1032        */
1033    }
1034
1035    #[test]
1036    fn coerce_string() {
1037        assert_eq!(
1038            DynSolType::String.coerce_str("gavofyork").unwrap(),
1039            DynSolValue::String("gavofyork".into())
1040        );
1041        assert_eq!(
1042            DynSolType::String.coerce_str("gav of york").unwrap(),
1043            DynSolValue::String("gav of york".into())
1044        );
1045        assert_eq!(
1046            DynSolType::String.coerce_str("\"hello world\"").unwrap(),
1047            DynSolValue::String("hello world".into())
1048        );
1049        assert_eq!(
1050            DynSolType::String.coerce_str("'hello world'").unwrap(),
1051            DynSolValue::String("hello world".into())
1052        );
1053        assert_eq!(
1054            DynSolType::String.coerce_str("'\"hello world\"'").unwrap(),
1055            DynSolValue::String("\"hello world\"".into())
1056        );
1057        assert_eq!(
1058            DynSolType::String.coerce_str("'   hello world '").unwrap(),
1059            DynSolValue::String("   hello world ".into())
1060        );
1061        assert_eq!(
1062            DynSolType::String.coerce_str("'\"hello world'").unwrap(),
1063            DynSolValue::String("\"hello world".into())
1064        );
1065        assert_eq!(
1066            DynSolType::String.coerce_str("a, b").unwrap(),
1067            DynSolValue::String("a, b".into())
1068        );
1069        assert_eq!(
1070            DynSolType::String.coerce_str("hello (world)").unwrap(),
1071            DynSolValue::String("hello (world)".into())
1072        );
1073
1074        assert!(DynSolType::String.coerce_str("\"hello world").is_err());
1075        assert!(DynSolType::String.coerce_str("\"hello world'").is_err());
1076        assert!(DynSolType::String.coerce_str("'hello world").is_err());
1077        assert!(DynSolType::String.coerce_str("'hello world\"").is_err());
1078
1079        assert_eq!(
1080            DynSolType::String.coerce_str("Hello, world!").unwrap(),
1081            DynSolValue::String("Hello, world!".into())
1082        );
1083        let s = "$$g]a\"v/of;[()];2,yo\r)k_";
1084        assert_eq!(DynSolType::String.coerce_str(s).unwrap(), DynSolValue::String(s.into()));
1085    }
1086
1087    #[test]
1088    fn coerce_strings() {
1089        let arr = DynSolType::Array(Box::new(DynSolType::String));
1090        let mk_arr = |s: &[&str]| {
1091            DynSolValue::Array(s.iter().map(|s| DynSolValue::String(s.to_string())).collect())
1092        };
1093
1094        assert_eq!(arr.coerce_str("[]").unwrap(), mk_arr(&[]));
1095        assert_eq!(arr.coerce_str("[    ]").unwrap(), mk_arr(&[]));
1096
1097        // TODO: should this be an error?
1098        // assert!(arr.coerce_str("[,]").is_err());
1099        // assert!(arr.coerce_str("[ , ]").is_err());
1100
1101        assert_eq!(arr.coerce_str("[ foo bar ]").unwrap(), mk_arr(&["foo bar"]));
1102        assert_eq!(arr.coerce_str("[foo bar,]").unwrap(), mk_arr(&["foo bar"]));
1103        assert_eq!(arr.coerce_str("[  foo bar,  ]").unwrap(), mk_arr(&["foo bar"]));
1104        assert_eq!(arr.coerce_str("[ foo , bar ]").unwrap(), mk_arr(&["foo", "bar"]));
1105
1106        assert_eq!(arr.coerce_str("[\"foo\",\"bar\"]").unwrap(), mk_arr(&["foo", "bar"]));
1107
1108        assert_eq!(arr.coerce_str("['']").unwrap(), mk_arr(&[""]));
1109        assert_eq!(arr.coerce_str("[\"\"]").unwrap(), mk_arr(&[""]));
1110        assert_eq!(arr.coerce_str("['', '']").unwrap(), mk_arr(&["", ""]));
1111        assert_eq!(arr.coerce_str("['', \"\"]").unwrap(), mk_arr(&["", ""]));
1112        assert_eq!(arr.coerce_str("[\"\", '']").unwrap(), mk_arr(&["", ""]));
1113        assert_eq!(arr.coerce_str("[\"\", \"\"]").unwrap(), mk_arr(&["", ""]));
1114    }
1115
1116    #[test]
1117    fn coerce_array_of_bytes_and_strings() {
1118        let ty = DynSolType::Array(Box::new(DynSolType::Bytes));
1119        assert_eq!(ty.coerce_str("[]"), Ok(DynSolValue::Array(vec![])));
1120        assert_eq!(ty.coerce_str("[0x]"), Ok(DynSolValue::Array(vec![DynSolValue::Bytes(vec![])])));
1121
1122        let ty = DynSolType::Array(Box::new(DynSolType::String));
1123        assert_eq!(ty.coerce_str("[]"), Ok(DynSolValue::Array(vec![])));
1124        assert_eq!(
1125            ty.coerce_str("[\"\"]"),
1126            Ok(DynSolValue::Array(vec![DynSolValue::String(String::new())]))
1127        );
1128        assert_eq!(
1129            ty.coerce_str("[0x]"),
1130            Ok(DynSolValue::Array(vec![DynSolValue::String("0x".into())]))
1131        );
1132    }
1133
1134    #[test]
1135    fn coerce_empty_array() {
1136        assert_eq!(
1137            DynSolType::Array(Box::new(DynSolType::Bool)).coerce_str("[]").unwrap(),
1138            DynSolValue::Array(vec![])
1139        );
1140        assert_eq!(
1141            DynSolType::FixedArray(Box::new(DynSolType::Bool), 0).coerce_str("[]").unwrap(),
1142            DynSolValue::FixedArray(vec![]),
1143        );
1144        assert!(DynSolType::FixedArray(Box::new(DynSolType::Bool), 1).coerce_str("[]").is_err());
1145    }
1146
1147    #[test]
1148    fn coerce_bool_array() {
1149        assert_eq!(
1150            DynSolType::coerce_str(&DynSolType::Array(Box::new(DynSolType::Bool)), "[true, false]")
1151                .unwrap(),
1152            DynSolValue::Array(vec![DynSolValue::Bool(true), DynSolValue::Bool(false)])
1153        );
1154    }
1155
1156    #[test]
1157    fn coerce_bool_array_of_arrays() {
1158        assert_eq!(
1159            DynSolType::coerce_str(
1160                &DynSolType::Array(Box::new(DynSolType::Array(Box::new(DynSolType::Bool)))),
1161                "[ [ true, true, false ], [ false]]"
1162            )
1163            .unwrap(),
1164            DynSolValue::Array(vec![
1165                DynSolValue::Array(vec![
1166                    DynSolValue::Bool(true),
1167                    DynSolValue::Bool(true),
1168                    DynSolValue::Bool(false)
1169                ]),
1170                DynSolValue::Array(vec![DynSolValue::Bool(false)])
1171            ])
1172        );
1173    }
1174
1175    #[test]
1176    fn coerce_bool_fixed_array() {
1177        let ty = DynSolType::FixedArray(Box::new(DynSolType::Bool), 3);
1178        assert!(ty.coerce_str("[]").is_err());
1179        assert!(ty.coerce_str("[true]").is_err());
1180        assert!(ty.coerce_str("[true, false]").is_err());
1181        assert_eq!(
1182            ty.coerce_str("[true, false, true]").unwrap(),
1183            DynSolValue::FixedArray(vec![
1184                DynSolValue::Bool(true),
1185                DynSolValue::Bool(false),
1186                DynSolValue::Bool(true),
1187            ])
1188        );
1189        assert!(ty.coerce_str("[true, false, false, true]").is_err());
1190    }
1191
1192    #[test]
1193    fn single_quoted_in_array_must_error() {
1194        assert!(DynSolType::Array(Box::new(DynSolType::Bool))
1195            .coerce_str("[true,\"false,false]")
1196            .is_err());
1197        assert!(DynSolType::Array(Box::new(DynSolType::Bool)).coerce_str("[false\"]").is_err());
1198        assert!(DynSolType::Array(Box::new(DynSolType::Bool))
1199            .coerce_str("[true,false\"]")
1200            .is_err());
1201        assert!(DynSolType::Array(Box::new(DynSolType::Bool))
1202            .coerce_str("[true,\"false\",false]")
1203            .is_err());
1204        assert!(DynSolType::Array(Box::new(DynSolType::Bool)).coerce_str("[true,false]").is_ok());
1205    }
1206
1207    #[test]
1208    fn tuples() {
1209        let ty = DynSolType::Tuple(vec![DynSolType::String, DynSolType::Bool, DynSolType::String]);
1210        assert_eq!(
1211            ty.coerce_str("(\"a,]) b\", true, true? ]and] false!)").unwrap(),
1212            DynSolValue::Tuple(vec![
1213                DynSolValue::String("a,]) b".into()),
1214                DynSolValue::Bool(true),
1215                DynSolValue::String("true? ]and] false!".into()),
1216            ])
1217        );
1218        assert!(ty.coerce_str("(\"\", true, a, b)").is_err());
1219        assert!(ty.coerce_str("(a, b, true, a)").is_err());
1220    }
1221
1222    #[test]
1223    fn tuples_arrays_mixed() {
1224        assert_eq!(
1225            DynSolType::Array(Box::new(DynSolType::Tuple(vec![
1226                DynSolType::Array(Box::new(DynSolType::Tuple(vec![DynSolType::Bool]))),
1227                DynSolType::Array(Box::new(DynSolType::Tuple(vec![
1228                    DynSolType::Bool,
1229                    DynSolType::Bool
1230                ]))),
1231            ])))
1232            .coerce_str("[([(true)],[(false,true)])]")
1233            .unwrap(),
1234            DynSolValue::Array(vec![DynSolValue::Tuple(vec![
1235                DynSolValue::Array(vec![DynSolValue::Tuple(vec![DynSolValue::Bool(true)])]),
1236                DynSolValue::Array(vec![DynSolValue::Tuple(vec![
1237                    DynSolValue::Bool(false),
1238                    DynSolValue::Bool(true)
1239                ])]),
1240            ])])
1241        );
1242
1243        assert_eq!(
1244            DynSolType::Tuple(vec![
1245                DynSolType::Array(Box::new(DynSolType::Tuple(vec![DynSolType::Bool]))),
1246                DynSolType::Array(Box::new(DynSolType::Tuple(vec![
1247                    DynSolType::Bool,
1248                    DynSolType::Bool
1249                ]))),
1250            ])
1251            .coerce_str("([(true)],[(false,true)])")
1252            .unwrap(),
1253            DynSolValue::Tuple(vec![
1254                DynSolValue::Array(vec![DynSolValue::Tuple(vec![DynSolValue::Bool(true)])]),
1255                DynSolValue::Array(vec![DynSolValue::Tuple(vec![
1256                    DynSolValue::Bool(false),
1257                    DynSolValue::Bool(true)
1258                ])]),
1259            ])
1260        );
1261    }
1262
1263    #[test]
1264    fn tuple_array_nested() {
1265        assert_eq!(
1266            DynSolType::Tuple(vec![
1267                DynSolType::Array(Box::new(DynSolType::Tuple(vec![DynSolType::Address]))),
1268                DynSolType::Uint(256),
1269            ])
1270            .coerce_str("([(5c9d55b78febcc2061715ba4f57ecf8ea2711f2c)],2)")
1271            .unwrap(),
1272            DynSolValue::Tuple(vec![
1273                DynSolValue::Array(vec![DynSolValue::Tuple(vec![DynSolValue::Address(address!(
1274                    "5c9d55b78febcc2061715ba4f57ecf8ea2711f2c"
1275                ))])]),
1276                DynSolValue::Uint(U256::from(2), 256),
1277            ])
1278        );
1279    }
1280
1281    // keep `n` low to avoid stack overflows (debug mode)
1282    #[test]
1283    fn lotsa_array_nesting() {
1284        let n = 10;
1285
1286        let mut ty = DynSolType::Bool;
1287        for _ in 0..n {
1288            ty = DynSolType::Array(Box::new(ty));
1289        }
1290        let mut value_str = String::new();
1291        value_str.push_str(&"[".repeat(n));
1292        value_str.push_str("true");
1293        value_str.push_str(&"]".repeat(n));
1294
1295        let mut value = ty.coerce_str(&value_str).unwrap();
1296        for _ in 0..n {
1297            let DynSolValue::Array(arr) = value else { panic!("{value:?}") };
1298            assert_eq!(arr.len(), 1);
1299            value = arr.into_iter().next().unwrap();
1300        }
1301        assert_eq!(value, DynSolValue::Bool(true));
1302    }
1303
1304    #[test]
1305    fn lotsa_tuple_nesting() {
1306        let n = 10;
1307
1308        let mut ty = DynSolType::Bool;
1309        for _ in 0..n {
1310            ty = DynSolType::Tuple(vec![ty]);
1311        }
1312        let mut value_str = String::new();
1313        value_str.push_str(&"(".repeat(n));
1314        value_str.push_str("true");
1315        value_str.push_str(&")".repeat(n));
1316
1317        let mut value = ty.coerce_str(&value_str).unwrap();
1318        for _ in 0..n {
1319            let DynSolValue::Tuple(tuple) = value else { panic!("{value:?}") };
1320            assert_eq!(tuple.len(), 1);
1321            value = tuple.into_iter().next().unwrap();
1322        }
1323        assert_eq!(value, DynSolValue::Bool(true));
1324    }
1325
1326    #[test]
1327    fn coerce_uint_scientific() {
1328        uint_test("1e18", Ok("1000000000000000000"));
1329
1330        uint_test("0.03069536448928848133e20", Ok("3069536448928848133"));
1331
1332        uint_test("1.5e18", Ok("1500000000000000000"));
1333
1334        uint_test("1e-3 ether", Ok("1000000000000000"));
1335        uint_test("1.0e-3 ether", Ok("1000000000000000"));
1336        uint_test("1.1e-3 ether", Ok("1100000000000000"));
1337
1338        uint_test("74258.225772486694040708e18", Ok("74258225772486694040708"));
1339        uint_test("0.03069536448928848133e20", Ok("3069536448928848133"));
1340        uint_test("0.000000000003069536448928848133e30", Ok("3069536448928848133"));
1341
1342        uint_test("1e-1", Err(()));
1343        uint_test("1e-2", Err(()));
1344        uint_test("1e-18", Err(()));
1345        uint_test("1 e18", Err(()));
1346        uint_test("1ex", Err(()));
1347        uint_test("1e", Err(()));
1348    }
1349}