Skip to content
Merged
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
1 change: 1 addition & 0 deletions abi/abi/HEVM.sol
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ writeJson(string, string)
writeJson(string, string, string)
parseJson(string)(bytes)
parseJson(string, string)(bytes)
parseJsonKeys(string, string)(string[])
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we add a parseJsonKeys(string)(string[]) to parse the root keys? might be better UX than passing $ as the key, but I don't do tons of jsonpath stuff so maybe $ is common/standardized and we should stick with just that?

parseJsonUint(string, string)(uint256)
parseJsonUintArray(string, string)(uint256[])
parseJsonInt(string, string)(int256)
Expand Down
85 changes: 85 additions & 0 deletions abi/src/bindings/hevm.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 31 additions & 0 deletions evm/src/executor/inspector/cheatcodes/ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,36 @@ fn parse_json(json_str: &str, key: &str, coerce: Option<ParamType>) -> Result {
}
}

// returns JSON keys of given object as a string array
fn parse_json_keys(json_str: &str, key: &str) -> Result {
let json = serde_json::from_str(json_str)?;
let values = jsonpath_lib::select(&json, &canonicalize_json_key(key))?;

// We need to check that values contains just one JSON-object and not an array of objects
ensure!(
values.len() == 1,
"You can only get keys for a single JSON-object. The key '{key}' returns a value or an array of JSON-objects",
);

let value = values[0];

ensure!(
value.is_object(),
"You can only get keys for JSON-object. The key '{key}' does not return an object",
);

let res = value
.as_object()
.ok_or(eyre::eyre!("Unexpected error while extracting JSON-object"))?
.keys()
.map(|key| Token::String(key.to_owned()))
.collect::<Vec<Token>>();

// encode the bytes as the 'bytes' solidity type
let abi_encoded = abi::encode(&[Token::Array(res)]);
Ok(abi_encoded.into())
}

/// Serializes a key:value pair to a specific object. By calling this function multiple times,
/// the user can serialize multiple KV pairs to the same object. The value can be of any type, even
/// a new object in itself. The function will return
Expand Down Expand Up @@ -523,6 +553,7 @@ pub fn apply(state: &mut Cheatcodes, call: &HEVMCalls) -> Option<Result> {
HEVMCalls::ParseJson0(inner) => parse_json(&inner.0, "$", None),
HEVMCalls::ParseJson1(inner) => parse_json(&inner.0, &inner.1, None),
HEVMCalls::ParseJsonBool(inner) => parse_json(&inner.0, &inner.1, Some(ParamType::Bool)),
HEVMCalls::ParseJsonKeys(inner) => parse_json_keys(&inner.0, &inner.1),
HEVMCalls::ParseJsonBoolArray(inner) => {
parse_json(&inner.0, &inner.1, Some(ParamType::Bool))
}
Expand Down
21 changes: 21 additions & 0 deletions testdata/cheats/Json.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,27 @@ contract ParseJsonTest is DSTest {
data = vm.parseJson("", ".");
assertEq(0, data.length);
}

function test_parseJsonKeys() public {
string memory jsonString =
'{"some_key_to_value": "some_value", "some_key_to_array": [1, 2, 3], "some_key_to_object": {"key1": "value1", "key2": 2}}';
string[] memory keys = vm.parseJsonKeys(jsonString, "$");
assertEq(abi.encode(keys), abi.encode(["some_key_to_value", "some_key_to_array", "some_key_to_object"]));

keys = vm.parseJsonKeys(jsonString, ".some_key_to_object");
assertEq(abi.encode(keys), abi.encode(["key1", "key2"]));

vm.expectRevert("You can only get keys for JSON-object. The key '.some_key_to_array' does not return an object");
vm.parseJsonKeys(jsonString, ".some_key_to_array");

vm.expectRevert("You can only get keys for JSON-object. The key '.some_key_to_value' does not return an object");
vm.parseJsonKeys(jsonString, ".some_key_to_value");

vm.expectRevert(
"You can only get keys for a single JSON-object. The key '.*' returns a value or an array of JSON-objects"
);
vm.parseJsonKeys(jsonString, ".*");
}
}

contract WriteJsonTest is DSTest {
Expand Down
2 changes: 2 additions & 0 deletions testdata/cheats/Vm.sol
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,8 @@ interface Vm {

function parseJson(string calldata) external returns (bytes memory);

function parseJsonKeys(string calldata, string calldata) external returns (string[] memory);

function parseJsonUint(string calldata, string calldata) external returns (uint256);

function parseJsonUintArray(string calldata, string calldata) external returns (uint256[] memory);
Expand Down