diff --git a/crates/dyn-abi/src/coerce.rs b/crates/dyn-abi/src/coerce.rs index 914cd6032..d6cd11a6e 100644 --- a/crates/dyn-abi/src/coerce.rs +++ b/crates/dyn-abi/src/coerce.rs @@ -58,7 +58,7 @@ impl DynSolType { /// use alloy_primitives::U256; /// /// let ty: DynSolType = "(uint256,string)[]".parse()?; - /// let value = ty.coerce_str("[(0, \"hello\"), (42, \"world\")]")?; + /// let value = ty.coerce_str("[(0, \"hello\"), (4.2e1, \"world\")]")?; /// assert_eq!( /// value, /// DynSolValue::Array(vec![ @@ -384,20 +384,25 @@ fn uint<'i>(len: usize) -> impl Parser, U256, ContextError> { } // (intpart * 10^fract.len() + fract) * 10^(units-fract.len()) - intpart - .checked_mul(U256::from(10usize.pow(fract.len() as u32))) - .and_then(|u| u.checked_add(fract_uint)) - .and_then(|u| u.checked_mul(U256::from(10usize.pow((units - fract.len()) as u32)))) - .ok_or_else(|| { - ErrMode::from_external_error(input, ErrorKind::Verify, Error::IntOverflow) - }) + (|| -> Option { + let extension = U256::from(10u64).checked_pow(U256::from(fract.len()))?; + let extended = intpart.checked_mul(extension)?; + let uint = fract_uint.checked_add(extended)?; + let units = U256::from(10u64).checked_pow(U256::from(units - fract.len()))?; + uint.checked_mul(units) + })() } else if units > 0 { - intpart.checked_mul(U256::from(10usize.pow(units as u32))).ok_or_else(|| { - ErrMode::from_external_error(input, ErrorKind::Verify, Error::IntOverflow) - }) + // intpart * 10^units + (|| -> Option { + let units = U256::from(10u64).checked_pow(U256::from(units))?; + intpart.checked_mul(units) + })() } else { - Ok(intpart) - }?; + Some(intpart) + } + .ok_or_else(|| { + ErrMode::from_external_error(input, ErrorKind::Verify, Error::IntOverflow) + })?; if uint.bit_len() > len { return Err(ErrMode::from_external_error(input, ErrorKind::Verify, Error::IntOverflow)); @@ -535,6 +540,38 @@ mod tests { use alloy_primitives::address; use core::str::FromStr; + fn uint_test(s: &str, expected: Result<&str, ()>) { + for (ty, negate) in [ + (DynSolType::Uint(256), false), + (DynSolType::Int(256), false), + (DynSolType::Int(256), true), + ] { + let s = if negate { &format!("-{s}") } else { s }; + let expected = if negate { + expected.map(|s| format!("-{s}")) + } else { + expected.map(|s| s.to_string()) + }; + let d = format!("{s:?} as {ty:?}"); + + let actual = ty.coerce_str(s); + match (actual, expected) { + (Ok(actual), Ok(expected)) => match (actual, ty) { + (DynSolValue::Uint(v, 256), DynSolType::Uint(256)) => { + assert_eq!(v, expected.parse::().unwrap(), "{d}"); + } + (DynSolValue::Int(v, 256), DynSolType::Int(256)) => { + assert_eq!(v, expected.parse::().unwrap(), "{d}"); + } + (actual, _) => panic!("{d}: unexpected value: {actual:?}"), + }, + (Err(_), Err(())) => {} + (Ok(actual), Err(_)) => panic!("{d}: expected failure, got {actual:?}"), + (Err(e), Ok(_)) => panic!("{d}: {e:?}"), + } + } + } + #[track_caller] fn assert_error_contains(e: &impl core::fmt::Display, s: &str) { if cfg!(feature = "std") { @@ -1313,37 +1350,25 @@ mod tests { #[test] fn coerce_uint_scientific() { - assert_eq!( - DynSolType::Uint(256).coerce_str("1e18").unwrap(), - DynSolValue::Uint(U256::from_str("1000000000000000000").unwrap(), 256) - ); + uint_test("1e18", Ok("1000000000000000000")); - assert_eq!( - DynSolType::Uint(256).coerce_str("74258.225772486694040708e18").unwrap(), - DynSolValue::Uint(U256::from_str("74258225772486694040708").unwrap(), 256) - ); + uint_test("0.03069536448928848133e20", Ok("3069536448928848133")); - assert_eq!( - DynSolType::Uint(256).coerce_str("1.5e18").unwrap(), - DynSolValue::Uint(U256::from_str("1500000000000000000").unwrap(), 256) - ); + uint_test("1.5e18", Ok("1500000000000000000")); - assert_eq!( - DynSolType::Uint(256).coerce_str("1e-3 ether").unwrap(), - DynSolValue::Uint(U256::from_str("1000000000000000").unwrap(), 256) - ); - assert_eq!( - DynSolType::Uint(256).coerce_str("1.0e-3 ether").unwrap(), - DynSolValue::Uint(U256::from_str("1000000000000000").unwrap(), 256) - ); - assert_eq!( - DynSolType::Uint(256).coerce_str("1.1e-3 ether").unwrap(), - DynSolValue::Uint(U256::from_str("1100000000000000").unwrap(), 256) - ); + uint_test("1e-3 ether", Ok("1000000000000000")); + uint_test("1.0e-3 ether", Ok("1000000000000000")); + uint_test("1.1e-3 ether", Ok("1100000000000000")); + + uint_test("74258.225772486694040708e18", Ok("74258225772486694040708")); + uint_test("0.03069536448928848133e20", Ok("3069536448928848133")); + uint_test("0.000000000003069536448928848133e30", Ok("3069536448928848133")); - assert!(DynSolType::Uint(256).coerce_str("1e-18").is_err()); - assert!(DynSolType::Uint(256).coerce_str("1 e18").is_err()); - assert!(DynSolType::Uint(256).coerce_str("1ex").is_err()); - assert!(DynSolType::Uint(256).coerce_str("1e").is_err()); + uint_test("1e-1", Err(())); + uint_test("1e-2", Err(())); + uint_test("1e-18", Err(())); + uint_test("1 e18", Err(())); + uint_test("1ex", Err(())); + uint_test("1e", Err(())); } } diff --git a/crates/sol-type-parser/src/input.rs b/crates/sol-type-parser/src/input.rs index 6af47b2d4..6cd6487b7 100644 --- a/crates/sol-type-parser/src/input.rs +++ b/crates/sol-type-parser/src/input.rs @@ -22,6 +22,7 @@ impl core::error::Error for CustomError {} pub type Input<'a> = winnow::Stateful<&'a str, RecursionCheck>; +#[inline] pub fn new_input(input: &str) -> Input<'_> { winnow::Stateful { input, state: Default::default() } }