diff --git a/.changeset/strong-lions-relax.md b/.changeset/strong-lions-relax.md new file mode 100644 index 00000000..26b2cc68 --- /dev/null +++ b/.changeset/strong-lions-relax.md @@ -0,0 +1,5 @@ +--- +"permissionless": patch +--- + +Allow using raw accounts with `signUserOperationHashWithECDSA` diff --git a/src/utils/index.ts b/src/utils/index.ts index f2394302..dda63cb3 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,4 +1,9 @@ import { type GetUserOperationHashParams, getUserOperationHash } from "./getUserOperationHash.js" -import { signUserOperationHashWithECDSA } from "./signUserOperationHashWithECDSA.js" +import { AccountOrClientNotFoundError, signUserOperationHashWithECDSA } from "./signUserOperationHashWithECDSA.js" -export { getUserOperationHash, type GetUserOperationHashParams, signUserOperationHashWithECDSA } +export { + getUserOperationHash, + type GetUserOperationHashParams, + signUserOperationHashWithECDSA, + AccountOrClientNotFoundError +} diff --git a/src/utils/signUserOperationHashWithECDSA.ts b/src/utils/signUserOperationHashWithECDSA.ts index d14440cd..45367afd 100644 --- a/src/utils/signUserOperationHashWithECDSA.ts +++ b/src/utils/signUserOperationHashWithECDSA.ts @@ -1,5 +1,13 @@ -import type { Account, Address, Chain, Client, Hash, Hex, Transport } from "viem" -import { AccountNotFoundError } from "viem/errors/account" +import { + type Account, + type Address, + BaseError, + type Chain, + type Client, + type Hash, + type Hex, + type Transport +} from "viem" import type { UserOperation } from "../types/userOperation.js" import { getUserOperationHash } from "./getUserOperationHash.js" @@ -10,25 +18,49 @@ function parseAccount(account: Address | Account): Account { type IsUndefined = [undefined] extends [T] ? true : false -type GetAccountParameter = - IsUndefined extends true ? { account: Account | Address } : { account?: Account | Address } +type GetAccountParameter< + TTransport extends Transport = Transport, + TChain extends Chain | undefined = Chain | undefined, + TAccount extends Account | undefined = Account | undefined +> = IsUndefined extends true + ? { account: Account; client?: undefined } + : { client: Client; account?: undefined } + +export type signUserOperationHashWithECDSAParams< + TTransport extends Transport = Transport, + TChain extends Chain | undefined = Chain | undefined, + TAccount extends Account | undefined = Account | undefined +> = GetAccountParameter & + ( + | { + hash: Hash + userOperation?: undefined + entryPoint?: undefined + chainId?: undefined + } + | { + hash?: undefined + userOperation: UserOperation + entryPoint: Address + chainId: number + } + ) -export type signUserOperationHashWithECDSAParams = - GetAccountParameter & - ( - | { - hash: Hash - userOperation?: undefined - entryPoint?: undefined - chainId?: undefined - } - | { - hash?: undefined - userOperation: UserOperation - entryPoint: Address - chainId: number - } +export class AccountOrClientNotFoundError extends BaseError { + override name = "AccountOrClientNotFoundError" + constructor({ docsPath }: { docsPath?: string } = {}) { + super( + [ + "Could not find an Account to execute with this Action.", + "Please provide an Account with the `account` argument on the Action, or by supplying an `account` to the WalletClient." + ].join("\n"), + { + docsPath, + docsSlug: "account" + } ) + } +} /** * @@ -57,18 +89,16 @@ export const signUserOperationHashWithECDSA = async < TTransport extends Transport = Transport, TChain extends Chain | undefined = Chain | undefined, TAccount extends Account | undefined = Account | undefined ->( - signer: Client, - { - account: account_ = signer.account, - hash, - userOperation, - chainId, - entryPoint - }: signUserOperationHashWithECDSAParams -): Promise => { +>({ + client, + account: account_ = client?.account, + hash, + userOperation, + chainId, + entryPoint +}: signUserOperationHashWithECDSAParams): Promise => { if (!account_) - throw new AccountNotFoundError({ + throw new AccountOrClientNotFoundError({ docsPath: "/permissionless/reference/utils/signUserOperationHashWithECDSA" }) @@ -89,7 +119,12 @@ export const signUserOperationHashWithECDSA = async < } }) - return signer.request({ + if (!client) + throw new AccountOrClientNotFoundError({ + docsPath: "/permissionless/reference/utils/signUserOperationHashWithECDSA" + }) + + return client.request({ method: "personal_sign", params: [userOperationHash, account.address] }) diff --git a/test/index.test.ts b/test/index.test.ts index ce055d04..bbaf83b1 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -9,6 +9,7 @@ import { getEntryPoint, getEoaWalletClient, getFactoryAddress, + getPrivateKeyAccount, getPublicClient, getTestingChain } from "./utils" @@ -124,7 +125,8 @@ describe("test public actions and utils", () => { const userOpHash = getUserOperationHash({ userOperation, entryPoint, chainId: chain.id }) - userOperation.signature = await signUserOperationHashWithECDSA(eoaWalletClient, { + userOperation.signature = await signUserOperationHashWithECDSA({ + client: eoaWalletClient, userOperation, entryPoint: entryPoint, chainId: chain.id @@ -134,7 +136,23 @@ describe("test public actions and utils", () => { expect(userOperation.signature).toBeString() expect(userOperation.signature).toStartWith("0x") - const signature = await signUserOperationHashWithECDSA(eoaWalletClient, { hash: userOpHash }) + const signature = await signUserOperationHashWithECDSA({ client: eoaWalletClient, hash: userOpHash }) + + await signUserOperationHashWithECDSA({ account: eoaWalletClient.account, hash: userOpHash }) + + await signUserOperationHashWithECDSA({ + account: eoaWalletClient.account, + userOperation, + entryPoint: entryPoint, + chainId: chain.id + }) + + await signUserOperationHashWithECDSA({ + account: getPrivateKeyAccount(), + userOperation, + entryPoint: entryPoint, + chainId: chain.id + }) expect(signature).not.toBeEmpty() expect(signature).toBeString()