From 58771c8415e73f2445c1fa933ab13d74a0fa9f7b Mon Sep 17 00:00:00 2001 From: Sebastiano Faiella Date: Thu, 15 Jun 2023 10:08:50 -0400 Subject: [PATCH] fix(forge): fixed vm.parseJson not parsing powers of 10 correctly (#5155) --- evm/src/executor/inspector/cheatcodes/ext.rs | 22 +++++ forge/tests/it/repros.rs | 6 ++ testdata/repros/Issue5038.t.sol | 99 ++++++++++++++++++++ 3 files changed, 127 insertions(+) create mode 100644 testdata/repros/Issue5038.t.sol diff --git a/evm/src/executor/inspector/cheatcodes/ext.rs b/evm/src/executor/inspector/cheatcodes/ext.rs index 1f35826b74bc..490ed4e86bc5 100644 --- a/evm/src/executor/inspector/cheatcodes/ext.rs +++ b/evm/src/executor/inspector/cheatcodes/ext.rs @@ -176,16 +176,38 @@ fn value_to_token(value: &Value) -> Result { } Value::Number(number) => { if let Some(f) = number.as_f64() { + // Check if the number has decimal digits because the EVM does not support floating + // point math if f.fract() == 0.0 { + // Use the string representation of the `serde_json` Number type instead of + // calling f.to_string(), because some numbers are wrongly rounded up after + // being convented to f64. + // Example: 18446744073709551615 becomes 18446744073709552000 after parsing it + // to f64. let s = number.to_string(); + + // Calling Number::to_string with powers of ten formats the number using + // scientific notation and causes from_dec_str to fail. Using format! with f64 + // keeps the full number representation. + // Example: 100000000000000000000 becomes 1e20 when Number::to_string is + // used. + let fallback_s = format!("{f}"); + if let Ok(n) = U256::from_dec_str(&s) { return Ok(Token::Uint(n)) } if let Ok(n) = I256::from_dec_str(&s) { return Ok(Token::Int(n.into_raw())) } + if let Ok(n) = U256::from_dec_str(&fallback_s) { + return Ok(Token::Uint(n)) + } + if let Ok(n) = I256::from_dec_str(&fallback_s) { + return Ok(Token::Int(n.into_raw())) + } } } + Err(fmt_err!("Unsupported value: {number:?}")) } Value::String(string) => { diff --git a/forge/tests/it/repros.rs b/forge/tests/it/repros.rs index c5ccc859e89c..7c13a7e7f33d 100644 --- a/forge/tests/it/repros.rs +++ b/forge/tests/it/repros.rs @@ -255,3 +255,9 @@ fn test_issue_4630() { fn test_issue_4586() { test_repro!("Issue4586"); } + +// +#[test] +fn test_issue_5038() { + test_repro!("Issue5038"); +} diff --git a/testdata/repros/Issue5038.t.sol b/testdata/repros/Issue5038.t.sol new file mode 100644 index 000000000000..ea3416887ae3 --- /dev/null +++ b/testdata/repros/Issue5038.t.sol @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.18; + +import "ds-test/test.sol"; +import "../cheats/Cheats.sol"; + +struct Value { + uint256 value; +} + +// https://github.com/foundry-rs/foundry/issues/5038 +contract Issue5038Test is DSTest { + Cheats constant vm = Cheats(HEVM_ADDRESS); + + function testParseMaxUint64() public { + string memory json = '{"value": 18446744073709551615}'; + bytes memory parsed = vm.parseJson(json); + Value memory data = abi.decode(parsed, (Value)); + assertEq(data.value, 18446744073709551615); + } + + function testParse18446744073709551616000() public { + string memory json = '{"value": 18446744073709551616000}'; + bytes memory parsed = vm.parseJson(json); + Value memory data = abi.decode(parsed, (Value)); + assertEq(data.value, 18446744073709551616000); + } + + function testParse1e19() public { + string memory json = '{"value": 10000000000000000000}'; + bytes memory parsed = vm.parseJson(json); + Value memory data = abi.decode(parsed, (Value)); + assertEq(data.value, 10000000000000000000); + } + + function test_parse_1e20() public { + string memory json = '{"value": 100000000000000000000}'; + bytes memory parsed = vm.parseJson(json); + Value memory data = abi.decode(parsed, (Value)); + assertEq(data.value, 100000000000000000000); + } + + function testParse1e21() public { + string memory json = '{"value": 1000000000000000000000}'; + bytes memory parsed = vm.parseJson(json); + Value memory data = abi.decode(parsed, (Value)); + assertEq(data.value, 1000000000000000000000); + } + + function testParse1e42() public { + string memory json = '{"value": 1000000000000000000000000000000000000000000}'; + bytes memory parsed = vm.parseJson(json); + Value memory data = abi.decode(parsed, (Value)); + assertEq(data.value, 1000000000000000000000000000000000000000000); + } + + function testParse1e42plusOne() public { + string memory json = '{"value": 1000000000000000000000000000000000000000001}'; + bytes memory parsed = vm.parseJson(json); + Value memory data = abi.decode(parsed, (Value)); + assertEq(data.value, 1000000000000000000000000000000000000000001); + } + + function testParse1e43plus5() public { + string memory json = '{"value": 10000000000000000000000000000000000000000005}'; + bytes memory parsed = vm.parseJson(json); + Value memory data = abi.decode(parsed, (Value)); + assertEq(data.value, 10000000000000000000000000000000000000000005); + } + + function testParse1e76() public { + string memory json = '{"value": 10000000000000000000000000000000000000000000000000000000000000000000000000000}'; + bytes memory parsed = vm.parseJson(json); + Value memory data = abi.decode(parsed, (Value)); + assertEq(data.value, 10000000000000000000000000000000000000000000000000000000000000000000000000000); + } + + function testParse1e76Plus10() public { + string memory json = '{"value": 10000000000000000000000000000000000000000000000000000000000000000000000000010}'; + bytes memory parsed = vm.parseJson(json); + Value memory data = abi.decode(parsed, (Value)); + assertEq(data.value, 10000000000000000000000000000000000000000000000000000000000000000000000000010); + } + + function testParseMinUint256() public { + string memory json = '{"value": -57896044618658097711785492504343953926634992332820282019728792003956564819968}'; + bytes memory parsed = vm.parseJson(json); + Value memory data = abi.decode(parsed, (Value)); + + assertEq(int256(data.value), -57896044618658097711785492504343953926634992332820282019728792003956564819968); + } + + function testParseMaxUint256() public { + string memory json = '{"value": 115792089237316195423570985008687907853269984665640564039457584007913129639935}'; + bytes memory parsed = vm.parseJson(json); + Value memory data = abi.decode(parsed, (Value)); + assertEq(data.value, 115792089237316195423570985008687907853269984665640564039457584007913129639935); + } +}