From 45fb1760023603b949efe8406cfb6180035054b3 Mon Sep 17 00:00:00 2001 From: PatrickAlphac <54278053+PatrickAlphaC@users.noreply.github.com> Date: Wed, 22 May 2024 17:48:18 -0400 Subject: [PATCH 1/2] feat: added vm.keyExistsJson --- crates/cheatcodes/assets/cheatcodes.json | 22 +++++++++++++++++++++- crates/cheatcodes/spec/src/vm.rs | 6 +++++- crates/cheatcodes/src/json.rs | 19 +++++++++++++++---- crates/evm/traces/src/decoder/mod.rs | 8 +++++++- testdata/cheats/Json.t.sol | 22 ++++++++++++++++++++++ testdata/cheats/Vm.sol | 1 + 6 files changed, 71 insertions(+), 7 deletions(-) diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 05fe56ddf..8baf467b8 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -4861,7 +4861,7 @@ { "func": { "id": "keyExists", - "description": "Checks if `key` exists in a JSON object.", + "description": "Checks if `key` exists in a JSON object\n`keyExists` is being deprecated in favor of `keyExistsJson`. It will be removed in future versions.", "declaration": "function keyExists(string calldata json, string calldata key) external view returns (bool);", "visibility": "external", "mutability": "view", @@ -4878,6 +4878,26 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "keyExistsJson", + "description": "Checks if `key` exists in a JSON object.", + "declaration": "function keyExistsJson(string calldata json, string calldata key) external view returns (bool);", + "visibility": "external", + "mutability": "view", + "signature": "keyExistsJson(string,string)", + "selector": "0xdb4235f6", + "selectorBytes": [ + 219, + 66, + 53, + 246 + ] + }, + "group": "json", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "label", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index ac7debdb9..40e32fcfc 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -1661,8 +1661,12 @@ interface Vm { // limitations and caveats of the JSON parsing cheats. /// Checks if `key` exists in a JSON object. - #[cheatcode(group = Json)] + /// `keyExists` is being deprecated in favor of `keyExistsJson`. It will be removed in future versions. + #[cheatcode(group = Json, status = Deprecated)] function keyExists(string calldata json, string calldata key) external view returns (bool); + /// Checks if `key` exists in a JSON object. + #[cheatcode(group = Json)] + function keyExistsJson(string calldata json, string calldata key) external view returns (bool); /// ABI-encodes a JSON object. #[cheatcode(group = Json)] diff --git a/crates/cheatcodes/src/json.rs b/crates/cheatcodes/src/json.rs index 758d46160..e248b60c8 100644 --- a/crates/cheatcodes/src/json.rs +++ b/crates/cheatcodes/src/json.rs @@ -12,10 +12,14 @@ use std::{borrow::Cow, collections::BTreeMap, fmt::Write}; impl Cheatcode for keyExistsCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { json, key } = self; - let json = parse_json_str(json)?; - let values = select(&json, key)?; - let exists = !values.is_empty(); - Ok(exists.abi_encode()) + check_json_key_exists(json, key) + } +} + +impl Cheatcode for keyExistsJsonCall { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self { json, key } = self; + check_json_key_exists(json, key) } } @@ -280,6 +284,13 @@ impl Cheatcode for writeJson_1Call { } } +pub(super) fn check_json_key_exists(json: &str, key: &str) -> Result { + let json = parse_json_str(json)?; + let values = select(&json, key)?; + let exists = !values.is_empty(); + Ok(exists.abi_encode()) +} + fn parse_json(json: &str, path: &str) -> Result { let value = parse_json_str(json)?; let selected = select(&value, path)?; diff --git a/crates/evm/traces/src/decoder/mod.rs b/crates/evm/traces/src/decoder/mod.rs index 86d5ab53d..ac348c966 100644 --- a/crates/evm/traces/src/decoder/mod.rs +++ b/crates/evm/traces/src/decoder/mod.rs @@ -434,7 +434,9 @@ impl CallTraceDecoder { "parseJsonBytes32" | "parseJsonBytes32Array" | "writeJson" | + // `keyExists` is being deprecated in favor of `keyExistsJson`. It will be removed in future versions. "keyExists" | + "keyExistsJson" | "serializeBool" | "serializeUint" | "serializeInt" | @@ -447,7 +449,11 @@ impl CallTraceDecoder { } else { let mut decoded = func.abi_decode_input(&data[SELECTOR_LEN..], false).ok()?; let token = - if func.name.as_str() == "parseJson" || func.name.as_str() == "keyExists" { + + if func.name.as_str() == "parseJson" || + // `keyExists` is being deprecated in favor of `keyExistsJson`. It will be removed in future versions. + func.name.as_str() == "keyExists" || + func.name.as_str() == "keyExistsJson" { "" } else { "" diff --git a/testdata/cheats/Json.t.sol b/testdata/cheats/Json.t.sol index a43a7be5a..ae720fa53 100644 --- a/testdata/cheats/Json.t.sol +++ b/testdata/cheats/Json.t.sol @@ -286,6 +286,28 @@ contract WriteJsonTest is DSTest { assertEq(decodedData.a, 123); } + function test_checkKeyExistsJson() public { + string memory path = "fixtures/Json/write_complex_test.json"; + string memory json = vm.readFile(path); + bool exists = vm.keyExistsJson(json, ".a"); + assertTrue(exists); + + // TODO: issue deprecation warning + exists = vm.keyExists(json, ".a"); + assertTrue(exists); + } + + function test_checkKeyDoesNotExistJson() public { + string memory path = "fixtures/Json/write_complex_test.json"; + string memory json = vm.readFile(path); + bool exists = vm.keyExistsJson(json, ".d"); + assertTrue(!exists); + + // TODO: issue deprecation warning + exists = vm.keyExists(json, ".d"); + assertTrue(!exists); + } + function test_checkKeyExists() public { string memory path = "fixtures/Json/write_complex_test.json"; string memory json = vm.readFile(path); diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index 39f44f574..f2645bc1a 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -405,6 +405,7 @@ interface Vm { function isFile(string calldata path) external returns (bool result); function isPersistent(address account) external view returns (bool persistent); function keyExists(string calldata json, string calldata key) external view returns (bool); + function keyExistsJson(string calldata json, string calldata key) external view returns (bool); function label(address account, string calldata newLabel) external; function load(address target, bytes32 slot) external view returns (bytes32 data); function loadAllocs(string calldata pathToAllocsJson) external; From 96af53c8f5f52731efce18fee2c41f8f72ce6170 Mon Sep 17 00:00:00 2001 From: Nicolas Villanueva Date: Fri, 24 May 2024 10:15:14 +0100 Subject: [PATCH 2/2] Update crates/evm/traces/src/decoder/mod.rs --- crates/evm/traces/src/decoder/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/evm/traces/src/decoder/mod.rs b/crates/evm/traces/src/decoder/mod.rs index ac348c966..76ffd5ee0 100644 --- a/crates/evm/traces/src/decoder/mod.rs +++ b/crates/evm/traces/src/decoder/mod.rs @@ -449,7 +449,6 @@ impl CallTraceDecoder { } else { let mut decoded = func.abi_decode_input(&data[SELECTOR_LEN..], false).ok()?; let token = - if func.name.as_str() == "parseJson" || // `keyExists` is being deprecated in favor of `keyExistsJson`. It will be removed in future versions. func.name.as_str() == "keyExists" ||