refactor: extract command related utilities

This commit is contained in:
zkfriendly
2025-08-22 14:51:30 +02:00
parent 60e2711ab6
commit 7982940380
5 changed files with 3 additions and 251 deletions

View File

@@ -2,8 +2,6 @@
pragma solidity ^0.8.0;
import { Bytes } from "@openzeppelin/contracts/utils/Bytes.sol";
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
import { CommandUtils } from "@zk-email/email-tx-builder/src/libraries/CommandUtils.sol";
/**
* @title CircuitUtils
@@ -13,7 +11,6 @@ import { CommandUtils } from "@zk-email/email-tx-builder/src/libraries/CommandUt
*/
library CircuitUtils {
using Bytes for bytes;
using Strings for string;
/**
* @notice Error thrown when the public signals array length is not exactly 60
@@ -210,9 +207,10 @@ library CircuitUtils {
}
/**
* @notice Flattens multiple arrays of field elements into a single array of length 60
* @notice Flattens multiple arrays of field elements into a single array
* @param inputs The arrays of field elements to flatten
* @return out The flattened array of length 60
* @param outLength The length of the flattened array
* @return out The flattened array
*/
function flattenFields(uint256[][] memory inputs, uint256 outLength) internal pure returns (uint256[] memory out) {
out = new uint256[](outLength);
@@ -227,98 +225,4 @@ library CircuitUtils {
if (k != outLength) revert InvalidPubSignalsLength();
return out;
}
/// @notice Extracts a parameter from a command string based on the template and parameter index.
/// @param template The command template as an array of strings.
/// @param command The command string to extract from.
/// @param paramIndex The zero-based index of the parameter to extract.
/// @return The extracted parameter as a string, or an empty string if not found.
function extractCommandParamByIndex(
string[] memory template,
string memory command,
uint256 paramIndex
)
internal
pure
returns (string memory)
{
uint256 wordIndex = _getParamWordIndex(template, paramIndex);
if (wordIndex == type(uint256).max) {
return "";
}
return _splitString(command, " ")[wordIndex];
}
/**
* @notice Checks if the given template string is a matcher (parameter placeholder).
* @param templateString The template string to check.
* @return True if the string is a matcher, false otherwise.
*/
function _isMatcher(string memory templateString) private pure returns (bool) {
return Strings.equal(templateString, CommandUtils.STRING_MATCHER)
|| Strings.equal(templateString, CommandUtils.UINT_MATCHER)
|| Strings.equal(templateString, CommandUtils.INT_MATCHER)
|| Strings.equal(templateString, CommandUtils.DECIMALS_MATCHER)
|| Strings.equal(templateString, CommandUtils.ETH_ADDR_MATCHER);
}
/**
* @notice Finds the index in the template corresponding to the Nth matcher (zero-based).
* @param template The command template as an array of strings.
* @param paramIndex The zero-based index of the parameter to find.
* @return paramTemplateIndex The zero-based index in the template array, or uint256 max if not found.
*/
function _getParamWordIndex(
string[] memory template,
uint256 paramIndex
)
private
pure
returns (uint256 paramTemplateIndex)
{
paramTemplateIndex = 0;
for (uint256 i = 0; i < template.length; i++) {
if (_isMatcher(template[i])) {
if (paramTemplateIndex == paramIndex) {
return i;
}
paramTemplateIndex++;
}
}
// return uint max if param not found
return type(uint256).max;
}
/**
* @notice Splits a string by a delimiter into an array of strings
* @param str The string to split
* @param delimiter The delimiter to split by
* @return The array of split strings
*/
function _splitString(string memory str, bytes1 delimiter) private pure returns (string[] memory) {
bytes memory strBytes = bytes(str);
uint256 count = 1;
for (uint256 i = 0; i < strBytes.length; i++) {
if (strBytes[i] == delimiter) {
count++;
}
}
string[] memory parts = new string[](count);
uint256 lastIndex = 0;
uint256 partIndex = 0;
for (uint256 i = 0; i < strBytes.length; i++) {
if (strBytes[i] == delimiter) {
bytes memory partBytes = strBytes.slice(lastIndex, i);
parts[partIndex] = string(partBytes);
lastIndex = i + 1;
partIndex++;
}
}
bytes memory lastPartBytes = strBytes.slice(lastIndex, strBytes.length);
parts[partIndex] = string(lastPartBytes);
return parts;
}
}

View File

@@ -9,7 +9,6 @@
"dependencies": {
"@openzeppelin/contracts": "5.3.0",
"@openzeppelin/contracts-upgradeable": "5.3.0",
"@zk-email/email-tx-builder": "https://github.com/zkemail/email-tx-builder#9620b7bbec4a2e15704f01ccc2635ec66bedbf7b",
"dotenv": "^16.3.1"
},
"files": [

View File

@@ -1,131 +0,0 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
import { Test } from "forge-std/Test.sol";
import { CommandUtils } from "@zk-email/email-tx-builder/src/libraries/CommandUtils.sol";
import { CircuitUtilsHelper } from "./_CircuitUtilsHelper.sol";
contract ExtractCommandParamByIndexTest is Test {
CircuitUtilsHelper private _helper;
function setUp() public {
_helper = new CircuitUtilsHelper();
}
function test_simpleStringParameter() public view {
string[] memory template = new string[](2);
template[0] = "String:";
template[1] = CommandUtils.STRING_MATCHER;
string memory command = "String: username";
string memory result = _helper.callExtractCommandParamByIndex(template, command, 0);
assertEq(result, "username");
}
function test_ethAddressParameter() public view {
string[] memory template = new string[](4);
template[0] = "Address:";
template[1] = CommandUtils.ETH_ADDR_MATCHER;
string memory command = "Address: 0xafBD210c60dD651892a61804A989eEF7bD63CBA0";
string memory result = _helper.callExtractCommandParamByIndex(template, command, 0);
assertEq(result, "0xafBD210c60dD651892a61804A989eEF7bD63CBA0");
}
function test_multipleParameters() public view {
string[] memory template = new string[](4);
template[0] = "Address1:";
template[1] = CommandUtils.ETH_ADDR_MATCHER;
template[2] = "Address2:";
template[3] = CommandUtils.ETH_ADDR_MATCHER;
string memory command =
"Address1: 0x0000000000000000000000000000000000000000 Address2: 0x1111111111111111111111111111111111111111";
string memory result1 = _helper.callExtractCommandParamByIndex(template, command, 0);
string memory result2 = _helper.callExtractCommandParamByIndex(template, command, 1);
assertEq(result1, "0x0000000000000000000000000000000000000000");
assertEq(result2, "0x1111111111111111111111111111111111111111");
}
function test_parameterIndexOutOfBounds() public view {
string[] memory template = new string[](2);
template[0] = "Address:";
template[1] = CommandUtils.ETH_ADDR_MATCHER;
string memory command = "Address: 0xafBD210c60dD651892a61804A989eEF7bD63CBA0";
string memory result = _helper.callExtractCommandParamByIndex(template, command, 2);
assertEq(result, "");
}
function test_noParameters() public view {
string[] memory template = new string[](2);
template[0] = "PING";
template[1] = "PONG";
string memory command = "PING PONG";
string memory result = _helper.callExtractCommandParamByIndex(template, command, 0);
assertEq(result, "");
}
function test_parameterAtStart() public view {
string[] memory template = new string[](2);
template[0] = CommandUtils.STRING_MATCHER;
template[1] = "!";
string memory command = "username !";
string memory result = _helper.callExtractCommandParamByIndex(template, command, 0);
assertEq(result, "username");
}
function test_consecutiveParameters() public view {
string[] memory template = new string[](4);
template[0] = CommandUtils.STRING_MATCHER;
template[1] = CommandUtils.UINT_MATCHER;
template[2] = CommandUtils.STRING_MATCHER;
template[3] = "!";
string memory command = "user 123 token !";
string memory result1 = _helper.callExtractCommandParamByIndex(template, command, 0);
string memory result2 = _helper.callExtractCommandParamByIndex(template, command, 1);
string memory result3 = _helper.callExtractCommandParamByIndex(template, command, 2);
assertEq(result1, "user");
assertEq(result2, "123");
assertEq(result3, "token");
}
function test_realEnsClaimCommand() public view {
string[] memory template = new string[](9);
template[0] = "Claim";
template[1] = "ENS";
template[2] = "name";
template[3] = "for";
template[4] = "address";
template[5] = CommandUtils.ETH_ADDR_MATCHER;
template[6] = "with";
template[7] = "resolver";
template[8] = CommandUtils.STRING_MATCHER;
string memory command =
"Claim ENS name for address 0xafBD210c60dD651892a61804A989eEF7bD63CBA0 with resolver resolver.eth";
string memory result1 = _helper.callExtractCommandParamByIndex(template, command, 0);
string memory result2 = _helper.callExtractCommandParamByIndex(template, command, 1);
assertEq(result1, "0xafBD210c60dD651892a61804A989eEF7bD63CBA0");
assertEq(result2, "resolver.eth");
}
function test_simpleEnsClaimCommand() public view {
string[] memory template = new string[](6);
template[0] = "Claim";
template[1] = "ENS";
template[2] = "name";
template[3] = "for";
template[4] = "address";
template[5] = CommandUtils.ETH_ADDR_MATCHER;
string memory command = "Claim ENS name for address 0xafBD210c60dD651892a61804A989eEF7bD63CBA0";
string memory result = _helper.callExtractCommandParamByIndex(template, command, 0);
assertEq(result, "0xafBD210c60dD651892a61804A989eEF7bD63CBA0");
}
}

View File

@@ -4,18 +4,6 @@ pragma solidity ^0.8.30;
import { CircuitUtils } from "../../CircuitUtils.sol";
contract CircuitUtilsHelper {
function callExtractCommandParamByIndex(
string[] memory template,
string memory command,
uint256 index
)
external
pure
returns (string memory)
{
return CircuitUtils.extractCommandParamByIndex(template, command, index);
}
function callFlattenFields(uint256[][] memory inputs, uint256 outLength) external pure returns (uint256[] memory) {
return CircuitUtils.flattenFields(inputs, outLength);
}

View File

@@ -2734,19 +2734,11 @@ __metadata:
dependencies:
"@openzeppelin/contracts": 5.3.0
"@openzeppelin/contracts-upgradeable": 5.3.0
"@zk-email/email-tx-builder": "https://github.com/zkemail/email-tx-builder#9620b7bbec4a2e15704f01ccc2635ec66bedbf7b"
dotenv: ^16.3.1
forge-std: "https://github.com/foundry-rs/forge-std#v1.10.0"
languageName: unknown
linkType: soft
"@zk-email/email-tx-builder@https://github.com/zkemail/email-tx-builder#9620b7bbec4a2e15704f01ccc2635ec66bedbf7b":
version: 1.0.0
resolution: "@zk-email/email-tx-builder@https://github.com/zkemail/email-tx-builder.git#commit=9620b7bbec4a2e15704f01ccc2635ec66bedbf7b"
checksum: da3d79f0299202794f161b0019a2d32f56530e28ae248b5ef3b0388e90546377767f28d7ae2690d259c878126d3d2f3e48f45a4e62081a7bb25371bcfe5bf517
languageName: node
linkType: hard
"@zk-email/helpers@workspace:packages/helpers":
version: 0.0.0-use.local
resolution: "@zk-email/helpers@workspace:packages/helpers"