Skip to content

Commit

Permalink
fix(forge): fixed vm.parseJson not parsing powers of 10 correctly (#5155
Browse files Browse the repository at this point in the history
)
  • Loading branch information
xeno097 authored Jun 15, 2023
1 parent ce687fc commit 58771c8
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 0 deletions.
22 changes: 22 additions & 0 deletions evm/src/executor/inspector/cheatcodes/ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,16 +176,38 @@ fn value_to_token(value: &Value) -> Result<Token> {
}
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) => {
Expand Down
6 changes: 6 additions & 0 deletions forge/tests/it/repros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,3 +255,9 @@ fn test_issue_4630() {
fn test_issue_4586() {
test_repro!("Issue4586");
}

// <https://github.com/foundry-rs/foundry/issues/5038>
#[test]
fn test_issue_5038() {
test_repro!("Issue5038");
}
99 changes: 99 additions & 0 deletions testdata/repros/Issue5038.t.sol
Original file line number Diff line number Diff line change
@@ -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);
}
}

0 comments on commit 58771c8

Please sign in to comment.