forked from emmaguo13/zk-blind
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
15 changed files
with
1,403 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
[profile.default] | ||
src = "src" | ||
out = "artifacts" | ||
libs = ["../../node_modules", "lib"] | ||
optimizer = true | ||
optimizer-runs = 20_000 | ||
|
||
solc = "0.8.26" | ||
|
||
# See more config options https://github.com/foundry-rs/foundry/tree/master/config | ||
|
||
# OpenZeppelin | ||
build_info = true | ||
extra_output = ["storageLayout"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
{ | ||
"name": "@zk-jwt/zk-jwt-contracts", | ||
"version": "0.0.1", | ||
"license": "MIT", | ||
"scripts": { | ||
"build": "forge build --skip '*ZKSync*'", | ||
"test": "forge test", | ||
"lint": "solhint 'src/**/*.sol'" | ||
}, | ||
"dependencies": { | ||
"@openzeppelin/contracts": "^5.0.0", | ||
"@openzeppelin/contracts-upgradeable": "^5.0.0", | ||
"@zk-email/contracts": "^6.1.5", | ||
"solady": "^0.0.123", | ||
"solidity-stringutils": "github:LayerZero-Labs/solidity-stringutils" | ||
}, | ||
"devDependencies": { | ||
"ds-test": "https://github.com/dapphub/ds-test", | ||
"forge-std": "https://github.com/foundry-rs/forge-std", | ||
"solhint": "^3.6.1" | ||
}, | ||
"files": [ | ||
"/src", | ||
"foundry.toml", | ||
"package.json", | ||
"README.md", | ||
"remappings.txt" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
@openzeppelin/=../../node_modules/@openzeppelin | ||
@openzeppelin/contracts-upgradeable/=../../node_modules/@openzeppelin/contracts-upgradeable | ||
@zk-email/=../../node_modules/@zk-email | ||
forge-std/=../../node_modules/forge-std/src | ||
ds-test/=../../node_modules/ds-test/src | ||
solidity-stringutils/=../../node_modules/solidity-stringutils/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.9; | ||
|
||
interface IJwtGroth16Verifier { | ||
function verifyProof( | ||
uint[2] calldata _pA, | ||
uint[2][2] calldata _pB, | ||
uint[2] calldata _pC, | ||
uint[29] calldata _pubSignals | ||
) external view returns (bool); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.9; | ||
|
||
import "@openzeppelin/contracts/utils/Strings.sol"; | ||
import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; | ||
import {Address} from "@openzeppelin/contracts/utils/Address.sol"; | ||
import "./DecimalUtils.sol"; | ||
|
||
library CommandUtils { | ||
bytes16 private constant LOWER_HEX_DIGITS = "0123456789abcdef"; | ||
bytes16 private constant UPPER_HEX_DIGITS = "0123456789ABCDEF"; | ||
string public constant STRING_MATCHER = "{string}"; | ||
string public constant UINT_MATCHER = "{uint}"; | ||
string public constant INT_MATCHER = "{int}"; | ||
string public constant DECIMALS_MATCHER = "{decimals}"; | ||
string public constant ETH_ADDR_MATCHER = "{ethAddr}"; | ||
|
||
function addressToHexString( | ||
address addr, | ||
uint stringCase | ||
) internal pure returns (string memory) { | ||
if (stringCase == 0) { | ||
return addressToChecksumHexString(addr); | ||
} else if (stringCase == 1) { | ||
return Strings.toHexString(addr); | ||
} else if (stringCase == 2) { | ||
return lowerToUpperCase(Strings.toHexString(addr)); | ||
} else { | ||
revert("invalid stringCase"); | ||
} | ||
} | ||
|
||
function addressToChecksumHexString( | ||
address addr | ||
) internal pure returns (string memory) { | ||
string memory lowerCaseAddrWithOx = Strings.toHexString(addr); | ||
|
||
bytes memory lowerCaseAddr = new bytes(40); // Remove 0x added by the OZ lib | ||
for (uint8 i = 2; i < 42; i++) { | ||
lowerCaseAddr[i - 2] = bytes(lowerCaseAddrWithOx)[i]; | ||
} | ||
|
||
// Hash of lowercase addr | ||
uint256 lowerCaseHash = uint256( | ||
keccak256(abi.encodePacked(lowerCaseAddr)) | ||
); | ||
|
||
// Result hex = 42 chars with 0x prefix | ||
bytes memory result = new bytes(42); | ||
result[0] = "0"; | ||
result[1] = "x"; | ||
|
||
// Shift 24 bytes (96 bits) to the right; as we only need first 20 bytes of the hash to compare | ||
lowerCaseHash >>= 24 * 4; | ||
|
||
uint256 intAddr = uint256(uint160(addr)); | ||
|
||
for (uint8 i = 41; i > 1; --i) { | ||
uint8 hashChar = uint8(lowerCaseHash & 0xf); // Get last char of the hex | ||
uint8 addrChar = uint8(intAddr & 0xf); // Get last char of the address | ||
|
||
if (hashChar >= 8) { | ||
result[i] = UPPER_HEX_DIGITS[addrChar]; | ||
} else { | ||
result[i] = LOWER_HEX_DIGITS[addrChar]; | ||
} | ||
|
||
// Remove last char from both hash and addr | ||
intAddr >>= 4; | ||
lowerCaseHash >>= 4; | ||
} | ||
|
||
return string(result); | ||
} | ||
|
||
function lowerToUpperCase( | ||
string memory hexStr | ||
) internal pure returns (string memory) { | ||
bytes memory bytesStr = bytes(hexStr); | ||
for (uint i = 0; i < bytesStr.length; i++) { | ||
if (bytesStr[i] >= 0x61 && bytesStr[i] <= 0x66) { | ||
bytesStr[i] = bytes1(uint8(bytesStr[i]) - 32); | ||
} | ||
} | ||
return string(bytesStr); | ||
} | ||
|
||
/// @notice Convert bytes to hex string without 0x prefix | ||
/// @param data bytes to convert | ||
function bytesToHexString( | ||
bytes memory data | ||
) public pure returns (string memory) { | ||
bytes memory hexChars = "0123456789abcdef"; | ||
bytes memory hexString = new bytes(2 * data.length); | ||
|
||
for (uint256 i = 0; i < data.length; i++) { | ||
uint256 value = uint256(uint8(data[i])); | ||
hexString[2 * i] = hexChars[value >> 4]; | ||
hexString[2 * i + 1] = hexChars[value & 0xf]; | ||
} | ||
|
||
return string(hexString); | ||
} | ||
|
||
/// @notice Calculate the expected command. | ||
/// @param commandParams Params to be used in the command | ||
/// @param template Template to be used for the command | ||
/// @param stringCase Case of the ethereum address string to be used for the command - 0: checksum, 1: lowercase, 2: uppercase | ||
function computeExpectedCommand( | ||
bytes[] memory commandParams, | ||
string[] memory template, | ||
uint stringCase | ||
) public pure returns (string memory expectedCommand) { | ||
// Construct an expectedCommand from template and the values of commandParams. | ||
uint8 nextParamIndex = 0; | ||
string memory stringParam; | ||
bool isParamExist; | ||
for (uint8 i = 0; i < template.length; i++) { | ||
isParamExist = true; | ||
if (Strings.equal(template[i], STRING_MATCHER)) { | ||
string memory param = abi.decode( | ||
commandParams[nextParamIndex], | ||
(string) | ||
); | ||
stringParam = param; | ||
} else if (Strings.equal(template[i], UINT_MATCHER)) { | ||
uint256 param = abi.decode( | ||
commandParams[nextParamIndex], | ||
(uint256) | ||
); | ||
stringParam = Strings.toString(param); | ||
} else if (Strings.equal(template[i], INT_MATCHER)) { | ||
int256 param = abi.decode( | ||
commandParams[nextParamIndex], | ||
(int256) | ||
); | ||
stringParam = Strings.toStringSigned(param); | ||
} else if (Strings.equal(template[i], DECIMALS_MATCHER)) { | ||
uint256 param = abi.decode( | ||
commandParams[nextParamIndex], | ||
(uint256) | ||
); | ||
stringParam = DecimalUtils.uintToDecimalString(param); | ||
} else if (Strings.equal(template[i], ETH_ADDR_MATCHER)) { | ||
address param = abi.decode( | ||
commandParams[nextParamIndex], | ||
(address) | ||
); | ||
stringParam = addressToHexString(param, stringCase); | ||
} else { | ||
isParamExist = false; | ||
stringParam = template[i]; | ||
} | ||
|
||
if (i > 0) { | ||
expectedCommand = string( | ||
abi.encodePacked(expectedCommand, " ") | ||
); | ||
} | ||
expectedCommand = string( | ||
abi.encodePacked(expectedCommand, stringParam) | ||
); | ||
if (isParamExist) { | ||
nextParamIndex++; | ||
} | ||
} | ||
return expectedCommand; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.9; | ||
|
||
import "@openzeppelin/contracts/utils/Strings.sol"; | ||
|
||
/// @title DecimalUtils | ||
/// @notice DecimalUtils library for converting uint256 to string with decimal places | ||
library DecimalUtils { | ||
/// @notice Convert uint256 to human readable string with decimal places | ||
/// @param value uint256 value to convert | ||
/// @return string representation of value with decimal places | ||
function uintToDecimalString(uint256 value) public pure returns (string memory) { | ||
return uintToDecimalString(value, 18); | ||
} | ||
|
||
/// @notice Convert uint256 to human readable string with decimal places | ||
/// @param value uint256 value to convert | ||
/// @param decimal number of decimal places | ||
/// @return string representation of value with decimal places | ||
function uintToDecimalString(uint256 value, uint decimal) public pure returns (string memory) { | ||
// Convert value to string in wei format (no decimals) | ||
bytes memory valueBytes = bytes(Strings.toString(value)); | ||
uint8 valueLength = uint8(valueBytes.length); | ||
|
||
// Create result array with max length | ||
// If less than 18 decimals, then 2 extra for "0.", otherwise one extra for "." | ||
bytes memory result = new bytes(valueLength > decimal ? valueLength + 1 : decimal + 2); | ||
uint8 resultLength = uint8(result.length); | ||
|
||
// We will be populating result array by copying from value array from last to first index | ||
// Difference between result and value array index when copying | ||
// If more than 18, then 1 index diff for ".", otherwise actual diff in length | ||
uint delta = valueLength > decimal ? 1 : resultLength - valueLength; | ||
|
||
// Boolean to indicate if we found a non-zero digit when scanning from last to first index | ||
bool foundNonZeroDecimal; | ||
|
||
uint8 actualResultLen = 0; | ||
|
||
// In each iteration we fill one index of result array (starting from end) | ||
for (uint8 i = resultLength - 1; i >= 0; i--) { | ||
// Check if we have reached the index where we need to add decimal point | ||
if (i == resultLength - decimal - 1) { | ||
// No need to add "." if there was no value in decimal places | ||
if (foundNonZeroDecimal) { | ||
result[i] = "."; | ||
actualResultLen++; | ||
} | ||
// Set delta to 0, as we have already added decimal point (only for valueLength > 18) | ||
delta = 0; | ||
} | ||
// If valueLength < 18 and we have copied everything, fill zeros | ||
else if (valueLength <= decimal && i < resultLength - valueLength) { | ||
result[i] = "0"; | ||
actualResultLen++; | ||
} | ||
// If non-zero decimal is found, or decimal point inserted (delta == 0), copy from value array | ||
else if (foundNonZeroDecimal || delta == 0) { | ||
result[i] = valueBytes[i - delta]; | ||
actualResultLen++; | ||
} | ||
// If we find non-zero decumal for the first time (trailing zeros are skipped) | ||
else if (valueBytes[i - delta] != "0") { | ||
result[i] = valueBytes[i - delta]; | ||
actualResultLen++; | ||
foundNonZeroDecimal = true; | ||
} | ||
|
||
// To prevent the last i-- underflow | ||
if (i == 0) { | ||
break; | ||
} | ||
} | ||
|
||
// Create final result array with correct length | ||
bytes memory compactResult = new bytes(actualResultLen); | ||
for (uint8 i = 0; i < actualResultLen; i++) { | ||
compactResult[i] = result[i]; | ||
} | ||
|
||
return string(compactResult); | ||
} | ||
} | ||
|
Oops, something went wrong.