From daf8865d1522487218d3c0f2553ddb73f4414c5a Mon Sep 17 00:00:00 2001 From: Derek Chiang Date: Wed, 14 Feb 2024 17:45:40 -0500 Subject: [PATCH] Implement support for ERC-6492 --- bun.lockb | Bin 204648 -> 204912 bytes .../accounts/kernel/createKernelAccount.ts | 114 ++++++++++++++---- packages/core/accounts/utils/6492.ts | 70 +++++++++++ packages/core/accounts/utils/index.ts | 3 + packages/core/constants.ts | 1 + packages/core/index.ts | 1 + packages/core/package.json | 2 +- packages/test/ecdsaKernelAccount.test.ts | 95 ++++++++++++++- packages/test/utils.ts | 42 ++++--- 9 files changed, 284 insertions(+), 44 deletions(-) create mode 100644 packages/core/accounts/utils/6492.ts diff --git a/bun.lockb b/bun.lockb index e377454f544c393b8dea30970fa0c9662cebe674..f062ad543a03c44acb2d29366a69ff71d237f43b 100755 GIT binary patch delta 27662 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
  • 87<*$%5(^o+1L#kJ=`IE$`23Eb)#nk3%v%)D-?Mo+1 zQC3Wp`eZ*OttzRBS?Mxdsgda!F}6t=W@2Rn10(0@l2vq8YGO=wO#1iRP?R_HcUUx2 z@;QE%6jDBP>gQRU&)|9(B#oK5SkTZQPc}y?l?qAym6aLmPBklvBroa)e&KfC!0W

    hR`$9V`AtojYB?l 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 Fe*iz{xSId~ diff --git a/packages/core/accounts/kernel/createKernelAccount.ts b/packages/core/accounts/kernel/createKernelAccount.ts index 936c5893..656d4072 100644 --- a/packages/core/accounts/kernel/createKernelAccount.ts +++ b/packages/core/accounts/kernel/createKernelAccount.ts @@ -8,6 +8,7 @@ import { type Chain, type Client, type EncodeDeployDataParameters, + type Hash, type Hex, type Transport, type TypedDataDefinition, @@ -21,16 +22,19 @@ import { hashTypedData, keccak256, parseAbi, + publicActions, stringToHex, validateTypedData } from "viem" import { toAccount } from "viem/accounts" import { getBytecode } from "viem/actions" +import { KERNEL_NAME, LATEST_KERNEL_VERSION } from "../../constants.js" import type { KernelEncodeCallDataArgs, KernelPluginManager, KernelPluginManagerParams } from "../../types/kernel.js" +import { wrapSignatureWith6492 } from "../utils/6492.js" import { isKernelPluginManager, toKernelPluginManager @@ -202,6 +206,14 @@ const getAccountAddress = async < }) } +const parseFactoryAddressAndCallDataFromAccountInitCode = ( + initCode: Hex +): [Address, Hex] => { + const factoryAddress = `0x${initCode.substring(2, 42)}` as Address + const factoryCalldata = `0x${initCode.substring(42)}` as Hex + return [factoryAddress, factoryCalldata] +} + /** * Build a kernel smart account from a private key, that use the ECDSA signer behind the scene * @param client @@ -256,14 +268,13 @@ export async function createKernelAccount< } // Fetch account address and chain id - const [accountAddress] = await Promise.all([ + const accountAddress = deployedAccountAddress ?? - getAccountAddress({ - client, - entryPoint, - initCodeProvider: generateInitCode - }) - ]) + (await getAccountAddress({ + client, + entryPoint, + initCodeProvider: generateInitCode + })) if (!accountAddress) throw new Error("Account address not found") @@ -281,11 +292,28 @@ export async function createKernelAccount< "latest" ] }) - const decoded = decodeFunctionResult({ - abi: [...EIP1271ABI], - functionName: "eip712Domain", - data: domain - }) + + let name: string + let version: string + let chainId: bigint + + if (domain !== "0x") { + const decoded = decodeFunctionResult({ + abi: [...EIP1271ABI], + functionName: "eip712Domain", + data: domain + }) + + name = decoded[1] + version = decoded[2] + chainId = decoded[3] + } else { + name = KERNEL_NAME + version = LATEST_KERNEL_VERSION + chainId = client.chain + ? client.chain.id + : await client.extend(publicActions).getChainId() + } const encoded = encodeAbiParameters( [ @@ -301,9 +329,9 @@ export async function createKernelAccount< "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" ) ), - keccak256(stringToHex(decoded[1])), - keccak256(stringToHex(decoded[2])), - decoded[3], + keccak256(stringToHex(name)), + keccak256(stringToHex(version)), + BigInt(chainId), accountAddress ] ) @@ -324,7 +352,11 @@ export async function createKernelAccount< address: accountAddress, async signMessage({ message }) { const messageHash = hashMessage(message) - return signHashedMessage(messageHash) + const [isDeployed, signature] = await Promise.all([ + isAccountDeployed(), + signHashedMessage(messageHash) + ]) + return create6492Signature(isDeployed, signature) }, async signTransaction(_, __) { throw new SignTransactionNotSupportedBySmartAccount() @@ -347,16 +379,50 @@ export async function createKernelAccount< } as TypedDataDefinition) const typedHash = hashTypedData(typedData) - return await signHashedMessage(typedHash) + const [isDeployed, signature] = await Promise.all([ + isAccountDeployed(), + signHashedMessage(typedHash) + ]) + return create6492Signature(isDeployed, signature) } }) + const isAccountDeployed = async (): Promise => { + const contractCode = await getBytecode(client, { + address: accountAddress + }) + + return (contractCode?.length ?? 0) > 2 + } + + const create6492Signature = async ( + isDeployed: boolean, + signature: Hash + ): Promise => { + if (isDeployed) { + return signature + } + + const [factoryAddress, factoryCalldata] = + parseFactoryAddressAndCallDataFromAccountInitCode( + await generateInitCode() + ) + + return wrapSignatureWith6492({ + factoryAddress, + factoryCalldata, + signature + }) + } + return { ...account, client: client, publicKey: accountAddress, entryPoint: entryPoint, source: "kernelSmartAccount", + kernelPluginManager, + generateInitCode, // Get the nonce of the smart account async getNonce() { @@ -367,23 +433,19 @@ export async function createKernelAccount< key }) }, - kernelPluginManager, // Sign a user operation async signUserOperation(userOperation) { return kernelPluginManager.signUserOperation(userOperation) }, - generateInitCode, // Encode the init code async getInitCode() { - const contractCode = await getBytecode(client, { - address: accountAddress - }) - - if ((contractCode?.length ?? 0) > 2) return "0x" - - return generateInitCode() + if (await isAccountDeployed()) { + return "0x" + } else { + return generateInitCode() + } }, // Encode the deploy call data diff --git a/packages/core/accounts/utils/6492.ts b/packages/core/accounts/utils/6492.ts new file mode 100644 index 00000000..59dde213 --- /dev/null +++ b/packages/core/accounts/utils/6492.ts @@ -0,0 +1,70 @@ +// copied from: https://github.com/alchemyplatform/aa-sdk/blob/266c9757cd721ef0bd97d04c0b592a329f8a9da5/packages/core/src/signer/utils.ts + +import { + type Address, + type Hash, + type Hex, + type PublicClient, + concat, + encodeAbiParameters, + parseAbiParameters +} from "viem" + +export type SignWith6492Params = { + factoryAddress: Address + factoryCalldata: Hex + signature: Hash +} + +type VerifyEIP6492SignatureParams = { + signer: Address + hash: Hash + signature: Hash + client: PublicClient +} + +export const wrapSignatureWith6492 = ({ + factoryAddress, + factoryCalldata, + signature +}: SignWith6492Params): Hash => { + // wrap the signature as follows: https://eips.ethereum.org/EIPS/eip-6492 + // concat( + // abi.encode( + // (create2Factory, factoryCalldata, originalERC1271Signature), + // (address, bytes, bytes)), + // magicBytes + // ) + return concat([ + encodeAbiParameters(parseAbiParameters("address, bytes, bytes"), [ + factoryAddress, + factoryCalldata, + signature + ]), + "0x6492649264926492649264926492649264926492649264926492649264926492" + ]) +} + +// defined in https://github.com/AmbireTech/signature-validator/blob/main/index.ts#L13C17-L13C17 +const universalValidatorByteCode = + "0x60806040523480156200001157600080fd5b50604051620007003803806200070083398101604081905262000034916200056f565b6000620000438484846200004f565b9050806000526001601ff35b600080846001600160a01b0316803b806020016040519081016040528181526000908060200190933c90507f6492649264926492649264926492649264926492649264926492649264926492620000a68462000451565b036200021f57600060608085806020019051810190620000c79190620005ce565b8651929550909350915060000362000192576000836001600160a01b031683604051620000f5919062000643565b6000604051808303816000865af19150503d806000811462000134576040519150601f19603f3d011682016040523d82523d6000602084013e62000139565b606091505b5050905080620001905760405162461bcd60e51b815260206004820152601e60248201527f5369676e617475726556616c696461746f723a206465706c6f796d656e74000060448201526064015b60405180910390fd5b505b604051630b135d3f60e11b808252906001600160a01b038a1690631626ba7e90620001c4908b90869060040162000661565b602060405180830381865afa158015620001e2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200020891906200069d565b6001600160e01b031916149450505050506200044a565b805115620002b157604051630b135d3f60e11b808252906001600160a01b03871690631626ba7e9062000259908890889060040162000661565b602060405180830381865afa15801562000277573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200029d91906200069d565b6001600160e01b031916149150506200044a565b8251604114620003195760405162461bcd60e51b815260206004820152603a6024820152600080516020620006e083398151915260448201527f3a20696e76616c6964207369676e6174757265206c656e677468000000000000606482015260840162000187565b620003236200046b565b506020830151604080850151855186939260009185919081106200034b576200034b620006c9565b016020015160f81c9050601b81148015906200036b57508060ff16601c14155b15620003cf5760405162461bcd60e51b815260206004820152603b6024820152600080516020620006e083398151915260448201527f3a20696e76616c6964207369676e617475726520762076616c75650000000000606482015260840162000187565b6040805160008152602081018083528a905260ff83169181019190915260608101849052608081018390526001600160a01b038a169060019060a0016020604051602081039080840390855afa1580156200042e573d6000803e3d6000fd5b505050602060405103516001600160a01b031614955050505050505b9392505050565b60006020825110156200046357600080fd5b508051015190565b60405180606001604052806003906020820280368337509192915050565b6001600160a01b03811681146200049f57600080fd5b50565b634e487b7160e01b600052604160045260246000fd5b60005b83811015620004d5578181015183820152602001620004bb565b50506000910152565b600082601f830112620004f057600080fd5b81516001600160401b03808211156200050d576200050d620004a2565b604051601f8301601f19908116603f01168101908282118183101715620005385762000538620004a2565b816040528381528660208588010111156200055257600080fd5b62000565846020830160208901620004b8565b9695505050505050565b6000806000606084860312156200058557600080fd5b8351620005928162000489565b6020850151604086015191945092506001600160401b03811115620005b657600080fd5b620005c486828701620004de565b9150509250925092565b600080600060608486031215620005e457600080fd5b8351620005f18162000489565b60208501519093506001600160401b03808211156200060f57600080fd5b6200061d87838801620004de565b935060408601519150808211156200063457600080fd5b50620005c486828701620004de565b6000825162000657818460208701620004b8565b9190910192915050565b828152604060208201526000825180604084015262000688816060850160208701620004b8565b601f01601f1916919091016060019392505050565b600060208284031215620006b057600080fd5b81516001600160e01b0319811681146200044a57600080fd5b634e487b7160e01b600052603260045260246000fdfe5369676e617475726556616c696461746f72237265636f7665725369676e6572" + +export const verifyEIP6492Signature = async ({ + signer, + hash, + signature, + client +}: VerifyEIP6492SignatureParams): Promise => { + const result = await client.call({ + data: concat([ + universalValidatorByteCode, + encodeAbiParameters(parseAbiParameters("address, bytes32, bytes"), [ + signer, + hash, + signature + ]) + ]) + }) + + return result.data === "0x01" +} diff --git a/packages/core/accounts/utils/index.ts b/packages/core/accounts/utils/index.ts index 5e5f1795..33ab4e61 100644 --- a/packages/core/accounts/utils/index.ts +++ b/packages/core/accounts/utils/index.ts @@ -1,2 +1,5 @@ import { toKernelPluginManager } from "./toKernelPluginManager.js" export { toKernelPluginManager } + +import { verifyEIP6492Signature } from "./6492.js" +export { verifyEIP6492Signature } diff --git a/packages/core/constants.ts b/packages/core/constants.ts index f061435a..6de4e629 100644 --- a/packages/core/constants.ts +++ b/packages/core/constants.ts @@ -9,4 +9,5 @@ export const KernelImplToVersionMap: { [key: Address]: string } = { "0xD3F582F6B4814E989Ee8E96bc3175320B5A540ab": "0.2.3" } export const TOKEN_ACTION = "0x2087C7FfD0d0DAE80a00EE74325aBF3449e0eaf1" +export const KERNEL_NAME = "Kernel" export const LATEST_KERNEL_VERSION = "0.2.3" diff --git a/packages/core/index.ts b/packages/core/index.ts index 38314b6d..76003ff1 100644 --- a/packages/core/index.ts +++ b/packages/core/index.ts @@ -38,3 +38,4 @@ export { gasTokenAddresses, type TokenSymbolsMap } from "./gasTokenAddresses.js" +export { verifyEIP6492Signature } from "./accounts/utils" diff --git a/packages/core/package.json b/packages/core/package.json index 5df20c16..73293235 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@zerodev/sdk", - "version": "5.1.0", + "version": "5.1.1", "author": "ZeroDev", "main": "./_cjs/index.js", "module": "./_esm/index.js", diff --git a/packages/test/ecdsaKernelAccount.test.ts b/packages/test/ecdsaKernelAccount.test.ts index 14c371ce..00a33085 100644 --- a/packages/test/ecdsaKernelAccount.test.ts +++ b/packages/test/ecdsaKernelAccount.test.ts @@ -7,7 +7,8 @@ import { KernelAccountClient, KernelSmartAccount, createKernelAccount, - getERC20PaymasterApproveCall + getERC20PaymasterApproveCall, + verifyEIP6492Signature } from "@zerodev/sdk" import { gasTokenAddresses } from "@zerodev/sdk" import dotenv from "dotenv" @@ -32,13 +33,14 @@ import { hashTypedData, zeroAddress } from "viem" -import { privateKeyToAccount } from "viem/accounts" +import { privateKeyToAccount, sign } from "viem/accounts" import { goerli } from "viem/chains" import { EntryPointAbi } from "./abis/EntryPoint.js" import { GreeterAbi, GreeterBytecode } from "./abis/Greeter.js" import { TEST_ERC20Abi } from "./abis/Test_ERC20Abi.js" import { findUserOperationEvent, + getEcdsaKernelAccountWithRandomSigner, getEntryPoint, getKernelAccountClient, getKernelBundlerClient, @@ -122,6 +124,95 @@ describe("ECDSA kernel Account", () => { }).toThrow(new SignTransactionNotSupportedBySmartAccount()) }) + test("Should validate message signatures for undeployed accounts (6492)", async () => { + const account = await getEcdsaKernelAccountWithRandomSigner() + const message = "hello world" + const signature = await account.signMessage({ + message + }) + + expect( + await verifyEIP6492Signature({ + signer: account.address, + hash: hashMessage(message), + signature: signature, + client: publicClient + }) + ).toBeTrue() + + // Try using Ambire as well + const ambireResult = await verifyMessage({ + signer: account.address, + message, + signature: signature, + provider: new ethers.providers.JsonRpcProvider( + process.env.RPC_URL as string + ) + }) + expect(ambireResult).toBeTrue() + }) + + test("Should validate typed data signatures for undeployed accounts (6492)", async () => { + const domain = { + chainId: 1, + name: "Test", + verifyingContract: zeroAddress + } + + const primaryType = "Test" + + const types = { + Test: [ + { + name: "test", + type: "string" + } + ] + } + + const message = { + test: "hello world" + } + const typedHash = hashTypedData({ + domain, + primaryType, + types, + message + }) + + const account = await getEcdsaKernelAccountWithRandomSigner() + const signature = await account.signTypedData({ + domain, + primaryType, + types, + message + }) + + expect( + await verifyEIP6492Signature({ + signer: account.address, + hash: typedHash, + signature: signature, + client: publicClient + }) + ).toBeTrue() + + // Try using Ambire as well + const ambireResult = await verifyMessage({ + signer: account.address, + typedData: { + domain, + types, + message + }, + signature: signature, + provider: new ethers.providers.JsonRpcProvider( + process.env.RPC_URL as string + ) + }) + expect(ambireResult).toBeTrue() + }) + test( "Client signMessage should return a valid signature", async () => { diff --git a/packages/test/utils.ts b/packages/test/utils.ts index a50a2e4e..9c5cd317 100644 --- a/packages/test/utils.ts +++ b/packages/test/utils.ts @@ -106,24 +106,36 @@ export const getSignerToEcdsaKernelAccount = throw new Error("TEST_PRIVATE_KEY environment variable not set") } - const publicClient = await getPublicClient() - const signer = privateKeyToAccount(privateKey) - const ecdsaValidatorPlugin = await signerToEcdsaValidator( - publicClient, - { - entryPoint: getEntryPoint(), - signer: { ...signer, source: "local" as "local" | "external" } - } - ) + return getEcdsaKernelAccountWithPrivateKey(privateKey) + } - return createKernelAccount(publicClient, { - entryPoint: getEntryPoint(), - plugins: { - sudo: ecdsaValidatorPlugin - } - }) +export const getEcdsaKernelAccountWithRandomSigner = + async (): Promise => { + return getEcdsaKernelAccountWithPrivateKey(generatePrivateKey()) } +const getEcdsaKernelAccountWithPrivateKey = async ( + privateKey: Hex +): Promise => { + if (!privateKey) { + throw new Error("privateKey cannot be empty") + } + + const publicClient = await getPublicClient() + const signer = privateKeyToAccount(privateKey) + const ecdsaValidatorPlugin = await signerToEcdsaValidator(publicClient, { + entryPoint: getEntryPoint(), + signer: { ...signer, source: "local" as "local" | "external" } + }) + + return createKernelAccount(publicClient, { + entryPoint: getEntryPoint(), + plugins: { + sudo: ecdsaValidatorPlugin + } + }) +} + // we only use two signers for testing export const getSignersToWeightedEcdsaKernelAccount = async ( plugin?: KernelValidator