Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/StdJson.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ library stdJson {

/// @notice ABI-encodes the JSON value selected by `key`.
/// @dev `key` uses the same selector syntax as `vm.parseJson*`, such as `.a` or `$`.
/// @dev The value's type is inferred, not declared. A hex string of exactly 20 bytes is encoded
/// as an `address` and one of exactly 32 bytes as a `bytes32`; every other length is encoded as
/// dynamic `bytes`. Decoding those two lengths as `bytes` therefore reverts, so prefer
/// `readBytes` when the value is meant to be byte data.
function parseRaw(string memory json, string memory key) internal pure returns (bytes memory) {
return vm.parseJson(json, key);
}
Expand Down
28 changes: 28 additions & 0 deletions test/StdJson.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,34 @@ contract StdJsonTest is Test {
assertEq(json.readUint(".a"), 123);
}

// Regression test for https://github.com/foundry-rs/forge-std/issues/592
// `parseRaw` infers a value's type, encoding a 20-byte hex string as an `address` and a
// 32-byte one as a `bytes32`. Reading either back as `bytes` reverts, which is how
// `readBytes` broke when it decoded `parseRaw` output instead of calling
// `vm.parseJsonBytes`. Pin the lengths on both sides of each inference boundary.
function test_ReadBytesAtInferredTypeLengths() public pure {
string memory nineteen = '{"a":"0x00000000000000000000000000000000000000"}';
assertEq(nineteen.readBytes(".a"), hex"00000000000000000000000000000000000000");

string memory twentyZeros = '{"a":"0x0000000000000000000000000000000000000000"}';
assertEq(twentyZeros.readBytes(".a"), hex"0000000000000000000000000000000000000000");

string memory twentyAddressShaped = '{"a":"0x4bf5122f344554c53bde2ebb8cd2b7e3d1600ad6"}';
assertEq(twentyAddressShaped.readBytes(".a"), hex"4bf5122f344554c53bde2ebb8cd2b7e3d1600ad6");

string memory twentyOne = '{"a":"0x000000000000000000000000000000000000000000"}';
assertEq(twentyOne.readBytes(".a"), hex"000000000000000000000000000000000000000000");

string memory thirtyOne = '{"a":"0x00000000000000000000000000000000000000000000000000000000000012"}';
assertEq(thirtyOne.readBytes(".a"), hex"00000000000000000000000000000000000000000000000000000000000012");

string memory thirtyTwo = '{"a":"0x4bf5122f344554c53bde2ebb8cd2b7e3d1600ad64bf5122f344554c53bde2ebb"}';
assertEq(thirtyTwo.readBytes(".a"), hex"4bf5122f344554c53bde2ebb8cd2b7e3d1600ad64bf5122f344554c53bde2ebb");

string memory thirtyThree = '{"a":"0x4bf5122f344554c53bde2ebb8cd2b7e3d1600ad64bf5122f344554c53bde2ebb00"}';
assertEq(thirtyThree.readBytes(".a"), hex"4bf5122f344554c53bde2ebb8cd2b7e3d1600ad64bf5122f344554c53bde2ebb00");
}

function test_writeJson() public {
string memory json = "json";
json.serialize("a", uint256(123));
Expand Down