Cannot keys or values from json object using stdJson.readStringArray
Component
Forge
Have you ensured that all of these are up to date?
- [X] Foundry
- [X] Foundryup
What version of Foundry are you on?
forge 0.2.0 (8f246e0 2023-04-28T00:03:59.802167368Z)
What command(s) is the bug in?
forge test
Operating System
Linux
Describe the bug
Due to https://github.com/foundry-rs/foundry/pull/4833 we tried to update lib/forge-std from 4a79aca83f8075f8b1b4fe9153945fef08375630 to b971f66b8416e3b09f240f6ee7230ad3bdb13c19 in order to set the number of expected calls.
However, that breaks our usage of readStringArray . We try to read the methodIdentifiers from the ABI output in the "out" files. We have previously been able to use code similar to the following:
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.17;
import {stdJson} from "forge-std/StdJson.sol";
import {Test, console2} from "forge-std/Test.sol";
contract JsonTest is Test {
function test_readStringArray() public view {
string memory json = "{ \"abi\": [ ], \"methodIdentifiers\": { \"function1()\": \"abcdabcd\", \"function2()\": \"def0def0\" } } ";
string[] memory methodIdentifiers = stdJson.readStringArray(json, ".methodIdentifiers[*]~");
bytes[] memory selectors = new bytes[](methodIdentifiers.length);
for (uint i = 0; i < methodIdentifiers.length; i++) {
selectors[i] = vm.parseBytes(methodIdentifiers[i]);
}
console2.log(vm.toString(selectors[0]));
console2.log(vm.toString(selectors[1]));
}
}
Which outputs the following on the old version of the lib
[PASS] test_readStringArray() (gas: 15952)
Logs:
0xabcdabcd
0xdef0def0
But with the updated lib/forge-std we get:
[FAIL. Reason: EvmError: Revert] test_readStringArray() (gas: 4281)
Traces:
[4281] JsonTest::test_readStringArray()
├─ [0] VM::parseJsonStringArray({ "abi": [ ], "methodIdentifiers": { "function1()": "abcdabcd", "function2()": "def0def0" } } , .methodIdentifiers[*]~)
│ └─ ← 0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000086162636461626364000000000000000000000000000000000000000000000000
└─ ← "EvmError: Revert"
Note, the ~ at the end of .methodIdentifiers[*]~ should actually make it output the keys and not the values, but that is a separate and not important issue for us. Without it it still does not work.
Looking at https://github.com/foundry-rs/foundry/blob/8f246e07c89129b6effa89f0d71c4ac67758a155/testdata/cheats/Json.t.sol it seems reading all the keys or values of objects is not in the test suite, so could it be broken or are we using it wrong?
Hmm interesting, I don't recall anything touching this functionality recently that would have broken it. Are you certain it's the forge-std version bump that causes the issue, as opposed to a forge version bump? cc'ing @odyslam here too
It works before "forge update" but not after. Isn't that only the libs that get updated?
I've the same error. Let me investigate..
I believe #294 introduced some issue. Need to investigate more..
@lnist this workaround can help for the moment:
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.17;
import {stdJson} from "forge-std/StdJson.sol";
import {Test, console2} from "forge-std/Test.sol";
contract JsonTest is Test {
function test_readStringArray() public view {
string memory json = "{ \"abi\": [ ], \"methodIdentifiers\": { \"function1()\": \"abcdabcd\", \"function2()\": \"def0def0\" } } ";
string memory key = ".methodIdentifiers[*]~";
bytes memory encoded = vm.parseJson(json, key);
string[] memory methodIdentifiers = abi.decode(encoded, (string[]));
bytes[] memory selectors = new bytes[](methodIdentifiers.length);
for (uint i = 0; i < methodIdentifiers.length; i++) {
selectors[i] = vm.parseBytes(methodIdentifiers[i]);
}
console2.log(vm.toString(selectors[0]));
console2.log(vm.toString(selectors[1]));
}
}
Thanks! the workaround fixes the problem for us. Will switch back to the other implementation when it works for this scenario :)
Initial repro now yields:
[FAIL: vm.parseJsonStringArray: path ".methodIdentifiers[*]~" must return exactly one JSON value] test_readStringArray() (gas: 3899)
readStringArray only operates on string arrays, not JSON arrays of strings
If you have data that looks like this or use the workaround you specified it does work:
import {stdJson} from "forge-std/StdJson.sol";
import {Test, console2} from "forge-std/Test.sol";
contract JsonTest is Test {
function test_readStringArray() public view {
// Updated JSON: methodIdentifiers is now an array
string memory json = '{ "abi": [], "methodIdentifiers": ["abcdabcd", "def0def0"] }';
// Read the array of strings
string[] memory methodIdentifiers = stdJson.readStringArray(json, ".methodIdentifiers");
// Convert each string into bytes
bytes[] memory selectors = new bytes[](methodIdentifiers.length);
for (uint256 i = 0; i < methodIdentifiers.length; i++) {
selectors[i] = vm.parseBytes(methodIdentifiers[i]);
}
// Log the selectors
console2.log(vm.toString(selectors[0]));
console2.log(vm.toString(selectors[1]));
}
}
it does yield
[PASS] test_readStringArray() (gas: 19224)
Logs:
0xabcdabcd
0xdef0def0
Traces:
[19224] JsonTest::test_readStringArray()
├─ [0] VM::parseJsonStringArray("<stringified JSON>", ".methodIdentifiers") [staticcall]
│ └─ ← [Return] ["abcdabcd", "def0def0"]
├─ [0] VM::parseBytes("abcdabcd") [staticcall]
│ └─ ← [Return] 0xabcdabcd
├─ [0] VM::parseBytes("def0def0") [staticcall]
│ └─ ← [Return] 0xdef0def0
├─ [0] VM::toString(0xabcdabcd) [staticcall]
│ └─ ← [Return] "0xabcdabcd"
├─ [0] console::log("0xabcdabcd") [staticcall]
│ └─ ← [Stop]
├─ [0] VM::toString(0xdef0def0) [staticcall]
│ └─ ← [Return] "0xdef0def0"
├─ [0] console::log("0xdef0def0") [staticcall]
│ └─ ← [Stop]
└─ ← [Stop]
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 408.73µs (171.37µs CPU time)
For now marking as won't fix