Skip to content

Commit

Permalink
feat: Added MultiChainFlexCallDataValidator which allows a param valu…
Browse files Browse the repository at this point in the history
…e of callData to be flexible
  • Loading branch information
SahilVasava committed Oct 28, 2024
1 parent 737db31 commit 776be11
Show file tree
Hide file tree
Showing 7 changed files with 193 additions and 2 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,5 @@ typechain-types/
# zerodev orchestra
log/
.envrc

settings.json
1 change: 1 addition & 0 deletions lib/FreshCryptoLib
Submodule FreshCryptoLib added at d9bb3b
1 change: 1 addition & 0 deletions lib/I4337
Submodule I4337 added at dc6485
1 change: 1 addition & 0 deletions lib/p256-verifier
Submodule p256-verifier added at 29475a
2 changes: 1 addition & 1 deletion lib/solady
Submodule solady updated 78 files
+1,094 −988 .gas-snapshot
+2 −0 .github/workflows/ci-all-via-ir.yml
+27 −0 .github/workflows/ci-invariant-intense.yml
+13 −7 .github/workflows/ci.yml
+3 −0 .gitignore
+7 −2 README.md
+6 −2 foundry.toml
+1 −1 package.json
+11 −6 src/Milady.sol
+238 −47 src/accounts/ERC1271.sol
+33 −15 src/accounts/ERC4337.sol
+160 −72 src/accounts/ERC6551.sol
+13 −10 src/accounts/ERC6551Proxy.sol
+232 −0 src/accounts/LibERC6551.sol
+4 −4 src/tokens/ERC1155.sol
+7 −7 src/tokens/ERC4626.sol
+1 −1 src/tokens/ERC721.sol
+5 −0 src/utils/Base64.sol
+1 −1 src/utils/CREATE3.sol
+91 −0 src/utils/DeploylessPredeployQueryer.sol
+755 −4 src/utils/DynamicBufferLib.sol
+630 −0 src/utils/EnumerableSetLib.sol
+173 −92 src/utils/FixedPointMathLib.sol
+10 −9 src/utils/LibBit.sol
+5 −5 src/utils/LibBitmap.sol
+305 −1 src/utils/LibClone.sol
+342 −20 src/utils/LibPRNG.sol
+1 −1 src/utils/LibSort.sol
+3 −3 src/utils/LibString.sol
+324 −39 src/utils/MinHeapLib.sol
+97 −0 src/utils/P256.sol
+7 −7 src/utils/RedBlackTreeLib.sol
+1 −1 src/utils/SSTORE2.sol
+284 −97 src/utils/SafeCastLib.sol
+188 −3 src/utils/SafeTransferLib.sol
+121 −0 src/utils/SignatureCheckerLib.sol
+1 −1 src/utils/UUPSUpgradeable.sol
+190 −0 src/utils/UpgradeableBeacon.sol
+21 −1 test/Base64.t.sol
+157 −0 test/DeploylessPredeployQueryer.t.sol
+136 −0 test/DynamicBufferLib.t.sol
+315 −0 test/ERC1271.t.sol
+5 −1 test/ERC20.t.sol
+124 −63 test/ERC4337.t.sol
+1 −1 test/ERC4337Factory.t.sol
+177 −124 test/ERC4626.t.sol
+95 −51 test/ERC6551.t.sol
+647 −0 test/EnumerableSetLib.t.sol
+247 −14 test/FixedPointMathLib.t.sol
+19 −10 test/LibBitmap.t.sol
+156 −8 test/LibClone.t.sol
+183 −0 test/LibERC6551.t.sol
+440 −21 test/LibPRNG.t.sol
+20 −25 test/LibString.t.sol
+345 −3 test/MinHeapLib.t.sol
+25 −14 test/OwnableRoles.t.sol
+95 −0 test/P256.t.sol
+919 −199 test/SafeCastLib.t.sol
+363 −87 test/SafeTransferLib.t.sol
+212 −0 test/SignatureCheckerLib.t.sol
+197 −0 test/UpgradeableBeacon.t.sol
+184 −0 test/utils/Brutalizer.sol
+36 −155 test/utils/TestPlus.sol
+720 −93 test/utils/forge-std/Test.sol
+1,826 −366 test/utils/forge-std/Vm.sol
+0 −517 test/utils/forge-std/ds-test/test.sol
+2 −8 test/utils/mocks/MockERC1155.sol
+5 −1 test/utils/mocks/MockERC1271Wallet.sol
+2 −8 test/utils/mocks/MockERC20.sol
+2 −15 test/utils/mocks/MockERC2981.sol
+2 −8 test/utils/mocks/MockERC4337.sol
+21 −8 test/utils/mocks/MockERC6551.sol
+2 −8 test/utils/mocks/MockERC6909.sol
+2 −15 test/utils/mocks/MockERC721.sol
+1 −1 test/utils/mocks/MockEntryPoint.sol
+8 −20 test/utils/mocks/MockOwnable.sol
+14 −26 test/utils/mocks/MockOwnableRoles.sol
+2 −8 test/utils/mocks/MockUUPSImplementation.sol
186 changes: 186 additions & 0 deletions src/validator/MultiChainFlexCallDataValidator.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import {ECDSA} from "solady/utils/ECDSA.sol";
import {MerkleProofLib} from "solady/utils/MerkleProofLib.sol";
import {IValidator, IHook} from "../interfaces/IERC7579Modules.sol";
import {PackedUserOperation} from "../interfaces/PackedUserOperation.sol";
import {IEntryPoint} from "../interfaces/IEntryPoint.sol";
import {
SIG_VALIDATION_SUCCESS_UINT,
SIG_VALIDATION_FAILED_UINT,
MODULE_TYPE_VALIDATOR,
MODULE_TYPE_HOOK,
ERC1271_MAGICVALUE,
ERC1271_INVALID
} from "../types/Constants.sol";

struct ECDSAValidatorStorage {
address owner;
}

struct FlexCallData {
uint32 offset;
uint32 length;
bytes value;
}

bytes constant DUMMY_ECDSA_SIG =
hex"fffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c";

contract MultiChainFlexCallDataValidator is IValidator, IHook {
event OwnerRegistered(address indexed kernel, address indexed owner);

mapping(address => ECDSAValidatorStorage) public ecdsaValidatorStorage;

function onInstall(bytes calldata _data) external payable override {
address owner = address(bytes20(_data[0:20]));
ecdsaValidatorStorage[msg.sender].owner = owner;
emit OwnerRegistered(msg.sender, owner);
}

function onUninstall(bytes calldata) external payable override {
if (!_isInitialized(msg.sender)) revert NotInitialized(msg.sender);
delete ecdsaValidatorStorage[msg.sender];
}

function isModuleType(uint256 typeID) external pure override returns (bool) {
return typeID == MODULE_TYPE_VALIDATOR || typeID == MODULE_TYPE_HOOK;
}

function isInitialized(address smartAccount) external view override returns (bool) {
return _isInitialized(smartAccount);
}

function _isInitialized(address smartAccount) internal view returns (bool) {
return ecdsaValidatorStorage[smartAccount].owner != address(0);
}

function validateUserOp(PackedUserOperation calldata userOp, bytes32 userOpHash)
external
payable
override
returns (uint256)
{
bytes calldata sig = userOp.signature;
address owner = ecdsaValidatorStorage[msg.sender].owner;
if (sig.length == 65) {
// simple ecdsa verification
if (owner == ECDSA.recover(userOpHash, sig)) {
return SIG_VALIDATION_SUCCESS_UINT;
}
bytes32 ethHash = ECDSA.toEthSignedMessageHash(userOpHash);
address recovered = ECDSA.recover(ethHash, sig);
if (owner != recovered) {
return SIG_VALIDATION_FAILED_UINT;
}
return SIG_VALIDATION_SUCCESS_UINT;
}
bytes memory ecdsaSig = sig[0:65];
bytes32 merkleRoot = bytes32(sig[65:97]);
// if the signature is a dummy signature, then use dummyUserOpHash instead of real userOpHash
if (keccak256(ecdsaSig) == keccak256(DUMMY_ECDSA_SIG)) {
(bytes32 dummyUserOpHash, bytes32[] memory proof) = abi.decode(sig[97:], (bytes32, bytes32[]));
require(MerkleProofLib.verify(proof, merkleRoot, dummyUserOpHash), "hash is not in proof");
// otherwise, use real userOpHash
} else {
(bytes32[] memory proof, FlexCallData[] memory flexCallData) =
abi.decode(sig[97:], (bytes32[], FlexCallData[]));
PackedUserOperation memory _userOp = _toMemoryUserOp(userOp);

_userOp.callData = _replaceCallData(userOp.callData, flexCallData);
bytes32 modifiedUserOpHash = IEntryPoint(0x0000000071727De22E5E9d8BAf0edAc6f37da032).getUserOpHash(_userOp);
require(MerkleProofLib.verify(proof, merkleRoot, modifiedUserOpHash), "hash is not in proof");
}
// simple ecdsa verification
if (owner == ECDSA.recover(merkleRoot, ecdsaSig)) {
return SIG_VALIDATION_SUCCESS_UINT;
}
bytes32 ethRoot = ECDSA.toEthSignedMessageHash(merkleRoot);
address merkleRecovered = ECDSA.recover(ethRoot, ecdsaSig);
if (owner != merkleRecovered) {
return SIG_VALIDATION_FAILED_UINT;
}
return SIG_VALIDATION_SUCCESS_UINT;
}

function isValidSignatureWithSender(address, bytes32 hash, bytes calldata sig)
external
view
override
returns (bytes4)
{
address owner = ecdsaValidatorStorage[msg.sender].owner;
if (sig.length == 65) {
// simple ecdsa verification
if (owner == ECDSA.recover(hash, sig)) {
return ERC1271_MAGICVALUE;
}
bytes32 ethHash = ECDSA.toEthSignedMessageHash(hash);
address recovered = ECDSA.recover(ethHash, sig);
if (owner != recovered) {
return ERC1271_INVALID;
}
return ERC1271_MAGICVALUE;
}
bytes memory ecdsaSig = sig[0:65];
bytes32 merkleRoot = bytes32(sig[65:97]);
bytes32[] memory proof = abi.decode(sig[97:], (bytes32[]));
require(MerkleProofLib.verify(proof, merkleRoot, hash), "hash is not in proof");
// simple ecdsa verification
if (owner == ECDSA.recover(merkleRoot, ecdsaSig)) {
return ERC1271_MAGICVALUE;
}
bytes32 ethRoot = ECDSA.toEthSignedMessageHash(merkleRoot);
address merkleRecovered = ECDSA.recover(ethRoot, ecdsaSig);
if (owner != merkleRecovered) {
return ERC1271_INVALID;
}
return ERC1271_MAGICVALUE;
}

function preCheck(address msgSender, uint256 value, bytes calldata)
external
payable
override
returns (bytes memory)
{
require(msgSender == ecdsaValidatorStorage[msg.sender].owner, "ECDSAValidator: sender is not owner");
return hex"";
}

function postCheck(bytes calldata hookData) external payable override {}

function _toMemoryUserOp(PackedUserOperation calldata userOp) internal pure returns (PackedUserOperation memory) {
return PackedUserOperation({
sender: userOp.sender,
nonce: userOp.nonce,
initCode: userOp.initCode,
callData: userOp.callData,
accountGasLimits: userOp.accountGasLimits,
preVerificationGas: userOp.preVerificationGas,
gasFees: userOp.gasFees,
paymasterAndData: userOp.paymasterAndData,
signature: userOp.signature
});
}

function _replaceCallData(bytes memory originalCallData, FlexCallData[] memory flexCallDataArray)
internal
pure
returns (bytes memory)
{
bytes memory modifiedCallData = originalCallData;
for (uint256 i = 0; i < flexCallDataArray.length; i++) {
FlexCallData memory flexData = flexCallDataArray[i];
require(flexData.offset + flexData.length <= originalCallData.length, "FlexCallData out of bounds");
// Should not overwrite the first 4 bytes sig of the callData
require(flexData.offset > 4, "FlexCallData offset too small");
for (uint256 j = 0; j < flexData.length && j < flexData.value.length; j++) {
modifiedCallData[flexData.offset + j] = flexData.value[j];
}
}
return modifiedCallData;
}
}

0 comments on commit 776be11

Please sign in to comment.