Skip to content

Commit

Permalink
Merge pull request #29 from pimlicolabs/feat/signUserOperationHashWit…
Browse files Browse the repository at this point in the history
…hECDSA

Feat/sign user operation hash with ecdsa
  • Loading branch information
kristofgazso authored Nov 1, 2023
2 parents c44ff64 + 5e990c1 commit 42d88db
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 35 deletions.
5 changes: 5 additions & 0 deletions .changeset/strong-lions-relax.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"permissionless": patch
---

Allow using raw accounts with `signUserOperationHashWithECDSA`
9 changes: 7 additions & 2 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -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
}
97 changes: 66 additions & 31 deletions src/utils/signUserOperationHashWithECDSA.ts
Original file line number Diff line number Diff line change
@@ -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"

Expand All @@ -10,25 +18,49 @@ function parseAccount(account: Address | Account): Account {

type IsUndefined<T> = [undefined] extends [T] ? true : false

type GetAccountParameter<TAccount extends Account | undefined = Account | undefined,> =
IsUndefined<TAccount> 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<TAccount> extends true
? { account: Account; client?: undefined }
: { client: Client<TTransport, TChain, TAccount>; account?: undefined }

export type signUserOperationHashWithECDSAParams<
TTransport extends Transport = Transport,
TChain extends Chain | undefined = Chain | undefined,
TAccount extends Account | undefined = Account | undefined
> = GetAccountParameter<TTransport, TChain, TAccount> &
(
| {
hash: Hash
userOperation?: undefined
entryPoint?: undefined
chainId?: undefined
}
| {
hash?: undefined
userOperation: UserOperation
entryPoint: Address
chainId: number
}
)

export type signUserOperationHashWithECDSAParams<TAccount extends Account | undefined = Account | undefined> =
GetAccountParameter<TAccount> &
(
| {
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"
}
)
}
}

/**
*
Expand Down Expand Up @@ -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<TTransport, TChain, TAccount>,
{
account: account_ = signer.account,
hash,
userOperation,
chainId,
entryPoint
}: signUserOperationHashWithECDSAParams<TAccount>
): Promise<Hex> => {
>({
client,
account: account_ = client?.account,
hash,
userOperation,
chainId,
entryPoint
}: signUserOperationHashWithECDSAParams<TTransport, TChain, TAccount>): Promise<Hex> => {
if (!account_)
throw new AccountNotFoundError({
throw new AccountOrClientNotFoundError({
docsPath: "/permissionless/reference/utils/signUserOperationHashWithECDSA"
})

Expand All @@ -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]
})
Expand Down
22 changes: 20 additions & 2 deletions test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
getEntryPoint,
getEoaWalletClient,
getFactoryAddress,
getPrivateKeyAccount,
getPublicClient,
getTestingChain
} from "./utils"
Expand Down Expand Up @@ -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
Expand All @@ -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()
Expand Down

0 comments on commit 42d88db

Please sign in to comment.