From 7eddcdfaec7afc6dab2d07a7f290763521c70019 Mon Sep 17 00:00:00 2001 From: kamuik16 Date: Tue, 2 Apr 2024 11:34:55 +0530 Subject: [PATCH 1/5] feat: index cheatcode --- crates/cheatcodes/assets/cheatcodes.json | 20 ++++++++++++++++++++ crates/cheatcodes/spec/src/vm.rs | 3 +++ crates/cheatcodes/src/string.rs | 9 +++++++++ testdata/cheats/Vm.sol | 1 + testdata/default/cheats/StringUtils.t.sol | 7 +++++++ 5 files changed, 40 insertions(+) diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index dea51432227f..8e29c4200526 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -4798,6 +4798,26 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "index", + "description": "Returns the index of the first occurrence of a `key` in an `input` string.", + "declaration": "function index(string memory input, string memory key) external pure returns (uint256);", + "visibility": "external", + "mutability": "pure", + "signature": "index(string,string)", + "selector": "0x2e191895", + "selectorBytes": [ + 46, + 25, + 24, + 149 + ] + }, + "group": "string", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "isDir", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 8f0de363a7fa..0ad8d9f1708e 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -1678,6 +1678,9 @@ interface Vm { /// Splits the given `string` into an array of strings divided by the `delimiter`. #[cheatcode(group = String)] function split(string calldata input, string calldata delimiter) external pure returns (string[] memory outputs); + /// Returns the index of the first occurrence of a `key` in an `input` string. + #[cheatcode(group = String)] + function index(string memory input, string memory key) external pure returns (uint256); // ======== JSON Parsing and Manipulation ======== diff --git a/crates/cheatcodes/src/string.rs b/crates/cheatcodes/src/string.rs index b7992b9450fd..1ac6e6d78804 100644 --- a/crates/cheatcodes/src/string.rs +++ b/crates/cheatcodes/src/string.rs @@ -135,6 +135,15 @@ impl Cheatcode for splitCall { } } +// Index +impl Cheatcode for indexCall { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self { input, key } = self; + let Some(index) = input.find(key) else { todo!() }; + Ok(index.to_string().abi_encode()) + } +} + pub(super) fn parse(s: &str, ty: &DynSolType) -> Result { parse_value(s, ty).map(|v| v.abi_encode()) } diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index 2116acf2b8ff..c0bae242b9af 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -237,6 +237,7 @@ interface Vm { function getNonce(address account) external view returns (uint64 nonce); function getNonce(Wallet calldata wallet) external returns (uint64 nonce); function getRecordedLogs() external returns (Log[] memory logs); + function index(string memory input, string memory key) external pure returns (uint256); function isDir(string calldata path) external returns (bool result); function isFile(string calldata path) external returns (bool result); function isPersistent(address account) external view returns (bool persistent); diff --git a/testdata/default/cheats/StringUtils.t.sol b/testdata/default/cheats/StringUtils.t.sol index 471d628be018..4822f4bf6734 100644 --- a/testdata/default/cheats/StringUtils.t.sol +++ b/testdata/default/cheats/StringUtils.t.sol @@ -39,4 +39,11 @@ contract StringManipulationTest is DSTest { assertEq("World", splitResult[1]); assertEq("Reth", splitResult[2]); } + + function testIndex() public { + string memory input = "Hello, World!"; + string memory key = "W"; + uint256 index = vm.index(input, key); + assertEq(index, 0); + } } From 82697fdd94221bf3c53851d7568bd22f23ed0e6c Mon Sep 17 00:00:00 2001 From: kamuik16 Date: Tue, 2 Apr 2024 12:14:15 +0530 Subject: [PATCH 2/5] some nits to make it work --- crates/cheatcodes/src/string.rs | 2 +- testdata/default/cheats/StringUtils.t.sol | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/crates/cheatcodes/src/string.rs b/crates/cheatcodes/src/string.rs index 1ac6e6d78804..24d2e8dafec4 100644 --- a/crates/cheatcodes/src/string.rs +++ b/crates/cheatcodes/src/string.rs @@ -140,7 +140,7 @@ impl Cheatcode for indexCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { input, key } = self; let Some(index) = input.find(key) else { todo!() }; - Ok(index.to_string().abi_encode()) + parse(&index.to_string(), &DynSolType::Uint(256)) } } diff --git a/testdata/default/cheats/StringUtils.t.sol b/testdata/default/cheats/StringUtils.t.sol index 4822f4bf6734..1b8b4383d868 100644 --- a/testdata/default/cheats/StringUtils.t.sol +++ b/testdata/default/cheats/StringUtils.t.sol @@ -42,8 +42,13 @@ contract StringManipulationTest is DSTest { function testIndex() public { string memory input = "Hello, World!"; - string memory key = "W"; - uint256 index = vm.index(input, key); - assertEq(index, 0); + string memory key1 = "Hello"; + string memory key2 = ","; + string memory key3 = "W"; + string memory key4 = "!"; + assertEq(vm.index(input, key1), 0); + assertEq(vm.index(input, key2), 5); + assertEq(vm.index(input, key3), 7); + assertEq(vm.index(input, key4), 12); } } From e7545e34c38e31a2da30c4266d5000b074e7ebaf Mon Sep 17 00:00:00 2001 From: kamuik16 Date: Tue, 2 Apr 2024 12:26:00 +0530 Subject: [PATCH 3/5] nit: use as_str() --- crates/cheatcodes/src/string.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cheatcodes/src/string.rs b/crates/cheatcodes/src/string.rs index 24d2e8dafec4..60c7460d3bc9 100644 --- a/crates/cheatcodes/src/string.rs +++ b/crates/cheatcodes/src/string.rs @@ -140,7 +140,7 @@ impl Cheatcode for indexCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { input, key } = self; let Some(index) = input.find(key) else { todo!() }; - parse(&index.to_string(), &DynSolType::Uint(256)) + parse(index.to_string().as_str(), &DynSolType::Uint(256)) } } From cfca3bc2063418d42d43537170ae3f242d84cf0e Mon Sep 17 00:00:00 2001 From: kamuik16 Date: Tue, 2 Apr 2024 16:34:13 +0530 Subject: [PATCH 4/5] final changes --- crates/cheatcodes/assets/cheatcodes.json | 16 ++++++++-------- crates/cheatcodes/spec/src/vm.rs | 3 ++- crates/cheatcodes/src/string.rs | 11 ++++++++--- testdata/cheats/Vm.sol | 2 +- testdata/default/cheats/StringUtils.t.sol | 16 ++++++++-------- 5 files changed, 27 insertions(+), 21 deletions(-) diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 8e29c4200526..20d00f7fb1c2 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -4800,18 +4800,18 @@ }, { "func": { - "id": "index", + "id": "indexOf", "description": "Returns the index of the first occurrence of a `key` in an `input` string.", - "declaration": "function index(string memory input, string memory key) external pure returns (uint256);", + "declaration": "function indexOf(string memory input, string memory key) external pure returns (uint256);", "visibility": "external", "mutability": "pure", - "signature": "index(string,string)", - "selector": "0x2e191895", + "signature": "indexOf(string,string)", + "selector": "0x8a0807b7", "selectorBytes": [ - 46, - 25, - 24, - 149 + 138, + 8, + 7, + 183 ] }, "group": "string", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 0ad8d9f1708e..7b528d69ec71 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -1679,8 +1679,9 @@ interface Vm { #[cheatcode(group = String)] function split(string calldata input, string calldata delimiter) external pure returns (string[] memory outputs); /// Returns the index of the first occurrence of a `key` in an `input` string. + /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `key` is not found. #[cheatcode(group = String)] - function index(string memory input, string memory key) external pure returns (uint256); + function indexOf(string memory input, string memory key) external pure returns (uint256); // ======== JSON Parsing and Manipulation ======== diff --git a/crates/cheatcodes/src/string.rs b/crates/cheatcodes/src/string.rs index 60c7460d3bc9..d8572adf7606 100644 --- a/crates/cheatcodes/src/string.rs +++ b/crates/cheatcodes/src/string.rs @@ -135,11 +135,16 @@ impl Cheatcode for splitCall { } } -// Index -impl Cheatcode for indexCall { +// indexOf +impl Cheatcode for indexOfCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { input, key } = self; - let Some(index) = input.find(key) else { todo!() }; + let Some(index) = input.find(key) else { + return parse( + "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + &DynSolType::Uint(256), + ); + }; parse(index.to_string().as_str(), &DynSolType::Uint(256)) } } diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index c0bae242b9af..aff05c278740 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -237,7 +237,7 @@ interface Vm { function getNonce(address account) external view returns (uint64 nonce); function getNonce(Wallet calldata wallet) external returns (uint64 nonce); function getRecordedLogs() external returns (Log[] memory logs); - function index(string memory input, string memory key) external pure returns (uint256); + function indexOf(string memory input, string memory key) external pure returns (uint256); function isDir(string calldata path) external returns (bool result); function isFile(string calldata path) external returns (bool result); function isPersistent(address account) external view returns (bool persistent); diff --git a/testdata/default/cheats/StringUtils.t.sol b/testdata/default/cheats/StringUtils.t.sol index 1b8b4383d868..fa4710437759 100644 --- a/testdata/default/cheats/StringUtils.t.sol +++ b/testdata/default/cheats/StringUtils.t.sol @@ -40,15 +40,15 @@ contract StringManipulationTest is DSTest { assertEq("Reth", splitResult[2]); } - function testIndex() public { + function testIndexOf() public { string memory input = "Hello, World!"; string memory key1 = "Hello"; - string memory key2 = ","; - string memory key3 = "W"; - string memory key4 = "!"; - assertEq(vm.index(input, key1), 0); - assertEq(vm.index(input, key2), 5); - assertEq(vm.index(input, key3), 7); - assertEq(vm.index(input, key4), 12); + string memory key2 = "World"; + string memory key3 = "!"; + string memory key4 = "foundry"; + assertEq(vm.indexOf(input, key1), 0); + assertEq(vm.indexOf(input, key2), 7); + assertEq(vm.indexOf(input, key3), 12); + assertEq(vm.indexOf(input, key4), type(uint256).max); } } From cca6ed6542b350112aed514d4d03e91299adb0bf Mon Sep 17 00:00:00 2001 From: kamuik16 Date: Thu, 4 Apr 2024 09:19:45 +0530 Subject: [PATCH 5/5] chore: reviewed changes --- crates/cheatcodes/assets/cheatcodes.json | 2 +- crates/cheatcodes/spec/src/vm.rs | 1 + crates/cheatcodes/src/string.rs | 9 ++------- testdata/default/cheats/StringUtils.t.sol | 8 ++++---- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index 20d00f7fb1c2..69cea1943593 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -4801,7 +4801,7 @@ { "func": { "id": "indexOf", - "description": "Returns the index of the first occurrence of a `key` in an `input` string.", + "description": "Returns the index of the first occurrence of a `key` in an `input` string.\nReturns `NOT_FOUND` (i.e. `type(uint256).max`) if the `key` is not found.\nReturns 0 in case of an empty `key`.", "declaration": "function indexOf(string memory input, string memory key) external pure returns (uint256);", "visibility": "external", "mutability": "pure", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 7b528d69ec71..cb4ec7304ae1 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -1680,6 +1680,7 @@ interface Vm { function split(string calldata input, string calldata delimiter) external pure returns (string[] memory outputs); /// Returns the index of the first occurrence of a `key` in an `input` string. /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `key` is not found. + /// Returns 0 in case of an empty `key`. #[cheatcode(group = String)] function indexOf(string memory input, string memory key) external pure returns (uint256); diff --git a/crates/cheatcodes/src/string.rs b/crates/cheatcodes/src/string.rs index d8572adf7606..c98560a72f06 100644 --- a/crates/cheatcodes/src/string.rs +++ b/crates/cheatcodes/src/string.rs @@ -2,6 +2,7 @@ use crate::{Cheatcode, Cheatcodes, Result, Vm::*}; use alloy_dyn_abi::{DynSolType, DynSolValue}; +use alloy_primitives::U256; use alloy_sol_types::SolValue; // address @@ -139,13 +140,7 @@ impl Cheatcode for splitCall { impl Cheatcode for indexOfCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { input, key } = self; - let Some(index) = input.find(key) else { - return parse( - "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - &DynSolType::Uint(256), - ); - }; - parse(index.to_string().as_str(), &DynSolType::Uint(256)) + Ok(input.find(key).map(U256::from).unwrap_or(U256::MAX).abi_encode()) } } diff --git a/testdata/default/cheats/StringUtils.t.sol b/testdata/default/cheats/StringUtils.t.sol index fa4710437759..136164a413d7 100644 --- a/testdata/default/cheats/StringUtils.t.sol +++ b/testdata/default/cheats/StringUtils.t.sol @@ -42,13 +42,13 @@ contract StringManipulationTest is DSTest { function testIndexOf() public { string memory input = "Hello, World!"; - string memory key1 = "Hello"; - string memory key2 = "World"; - string memory key3 = "!"; + string memory key1 = "Hello,"; + string memory key2 = "World!"; + string memory key3 = ""; string memory key4 = "foundry"; assertEq(vm.indexOf(input, key1), 0); assertEq(vm.indexOf(input, key2), 7); - assertEq(vm.indexOf(input, key3), 12); + assertEq(vm.indexOf(input, key3), 0); assertEq(vm.indexOf(input, key4), type(uint256).max); } }