From 86f53d23faa7d4721fdd7c8b5b77568347b772e7 Mon Sep 17 00:00:00 2001 From: Garvit Khatri Date: Thu, 11 Apr 2024 00:29:57 +0530 Subject: [PATCH 1/3] Add getPackedUserOperation --- .../permissionless-test/ep-0.7/index.test.ts | 31 ++++- packages/permissionless/types/index.ts | 2 + .../permissionless/types/userOperation.ts | 14 +++ .../utils/getPackedUserOperation.ts | 114 ++++++++++++++++++ packages/permissionless/utils/index.ts | 3 + 5 files changed, 159 insertions(+), 5 deletions(-) create mode 100644 packages/permissionless/utils/getPackedUserOperation.ts diff --git a/packages/permissionless-test/ep-0.7/index.test.ts b/packages/permissionless-test/ep-0.7/index.test.ts index 491834fe..ca9cbaa6 100644 --- a/packages/permissionless-test/ep-0.7/index.test.ts +++ b/packages/permissionless-test/ep-0.7/index.test.ts @@ -7,13 +7,14 @@ import { } from "permissionless" import { getRequiredPrefund, - signUserOperationHashWithECDSA + signUserOperationHashWithECDSA, + getPackedUserOperation } from "permissionless/utils" import { - Address, - Hash, - Hex, - WalletClient, + type Address, + type Hash, + type Hex, + type WalletClient, encodeFunctionData, zeroAddress } from "viem" @@ -29,6 +30,7 @@ import { getSmartAccountClient, getTestingChain } from "./utils" +import type { PackedUserOperation } from "permissionless/types" dotenv.config() @@ -241,4 +243,23 @@ describe("test public actions and utils", () => { userOperation.maxFeePerGas ) }) + + test("getPackedUserOperation", async () => { + const smartAccountClient = await getSmartAccountClient() + + const userOperation = + await smartAccountClient.prepareUserOperationRequest({ + userOperation: { + callData: await smartAccountClient.account.encodeCallData({ + to: zeroAddress, + value: 0n, + data: "0x" + }) + } + }) + + const packedUserOperation = getPackedUserOperation(userOperation) + + expectTypeOf(packedUserOperation).toMatchTypeOf() + }) }) diff --git a/packages/permissionless/types/index.ts b/packages/permissionless/types/index.ts index 34924410..af9ac9a4 100644 --- a/packages/permissionless/types/index.ts +++ b/packages/permissionless/types/index.ts @@ -12,6 +12,8 @@ export type { EntryPoint } from "./entrypoint" +export type { PackedUserOperation } from "./userOperation" + export type GetAccountParameterWithClient< TTransport extends Transport = Transport, TChain extends Chain | undefined = Chain | undefined, diff --git a/packages/permissionless/types/userOperation.ts b/packages/permissionless/types/userOperation.ts index 139879c4..abd644df 100644 --- a/packages/permissionless/types/userOperation.ts +++ b/packages/permissionless/types/userOperation.ts @@ -86,3 +86,17 @@ export type UserOperation = initCode?: never paymasterAndData?: never } + +export type Hex32 = `0x${string & { length: 64 }}` + +export type PackedUserOperation = { + sender: Address + nonce: bigint + initCode: Hex + callData: Hex + accountGasLimits: Hex32 + preVerificationGas: bigint + gasFees: Hex32 + paymasterAndData: Hex + signature: Hex +} diff --git a/packages/permissionless/utils/getPackedUserOperation.ts b/packages/permissionless/utils/getPackedUserOperation.ts new file mode 100644 index 00000000..9b94d03b --- /dev/null +++ b/packages/permissionless/utils/getPackedUserOperation.ts @@ -0,0 +1,114 @@ +import { type Hex, concat, getAddress, pad, slice, toHex } from "viem" +import type { UserOperation } from "../types/userOperation" +import type { Hex32, PackedUserOperation } from "../types/userOperation" + +export function getInitCode(unpackedUserOperation: UserOperation<"v0.7">) { + return unpackedUserOperation.factory + ? concat([ + unpackedUserOperation.factory, + unpackedUserOperation.factoryData || ("0x" as Hex) + ]) + : "0x" +} + +export function unPackInitCode(initCode: Hex) { + if (initCode === "0x") { + return { + factory: null, + factoryData: null + } + } + return { + factory: getAddress(slice(initCode, 0, 20)), + factoryData: slice(initCode, 20) + } +} + +export function getAccountGasLimits( + unpackedUserOperation: UserOperation<"v0.7"> +) { + return concat([ + pad(toHex(unpackedUserOperation.verificationGasLimit), { + size: 16 + }), + pad(toHex(unpackedUserOperation.callGasLimit), { size: 16 }) + ]) as Hex32 +} + +export function unpackAccountGasLimits(accountGasLimits: Hex) { + return { + verificationGasLimit: BigInt(slice(accountGasLimits, 0, 16)), + callGasLimit: BigInt(slice(accountGasLimits, 16)) + } +} + +export function getGasLimits(unpackedUserOperation: UserOperation<"v0.7">) { + return concat([ + pad(toHex(unpackedUserOperation.maxPriorityFeePerGas), { + size: 16 + }), + pad(toHex(unpackedUserOperation.maxFeePerGas), { size: 16 }) + ]) as Hex32 +} + +export function unpackGasLimits(gasLimits: Hex) { + return { + maxPriorityFeePerGas: BigInt(slice(gasLimits, 0, 16)), + maxFeePerGas: BigInt(slice(gasLimits, 16)) + } +} + +export function getPaymasterAndData( + unpackedUserOperation: UserOperation<"v0.7"> +) { + return unpackedUserOperation.paymaster + ? concat([ + unpackedUserOperation.paymaster, + pad( + toHex( + unpackedUserOperation.paymasterVerificationGasLimit || 0n + ), + { + size: 16 + } + ), + pad(toHex(unpackedUserOperation.paymasterPostOpGasLimit || 0n), { + size: 16 + }), + unpackedUserOperation.paymasterData || ("0x" as Hex) + ]) + : "0x" +} + +export function unpackPaymasterAndData(paymasterAndData: Hex) { + if (paymasterAndData === "0x") { + return { + paymaster: null, + paymasterVerificationGasLimit: null, + paymasterPostOpGasLimit: null, + paymasterData: null + } + } + return { + paymaster: getAddress(slice(paymasterAndData, 0, 20)), + paymasterVerificationGasLimit: BigInt(slice(paymasterAndData, 20, 36)), + paymasterPostOpGasLimit: BigInt(slice(paymasterAndData, 36, 52)), + paymasterData: slice(paymasterAndData, 52) + } +} + +export const getPackedUserOperation = ( + userOperation: UserOperation<"v0.7"> +): PackedUserOperation => { + return { + sender: userOperation.sender, + nonce: userOperation.nonce, + initCode: getInitCode(userOperation), + callData: userOperation.callData, + accountGasLimits: getAccountGasLimits(userOperation), + preVerificationGas: userOperation.preVerificationGas, + gasFees: getGasLimits(userOperation), + paymasterAndData: getPaymasterAndData(userOperation), + signature: userOperation.signature + } +} diff --git a/packages/permissionless/utils/index.ts b/packages/permissionless/utils/index.ts index e38a6e41..c8adb0bf 100644 --- a/packages/permissionless/utils/index.ts +++ b/packages/permissionless/utils/index.ts @@ -30,6 +30,8 @@ import { getEntryPointVersion } from "./getEntryPointVersion" +import { getPackedUserOperation } from "./getPackedUserOperation" + export { transactionReceiptStatus, deepHexlify, @@ -45,6 +47,7 @@ export { isSmartAccountDeployed, providerToSmartAccountSigner, getAddressFromInitCodeOrPaymasterAndData, + getPackedUserOperation, getEntryPointVersion, ENTRYPOINT_ADDRESS_V06, ENTRYPOINT_ADDRESS_V07 From 13f165bdd33df044605d4cb9b74fbac2a32fdefb Mon Sep 17 00:00:00 2001 From: plusminushalf Date: Wed, 10 Apr 2024 19:00:31 +0000 Subject: [PATCH 2/3] chore: format --- packages/permissionless-test/ep-0.7/index.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/permissionless-test/ep-0.7/index.test.ts b/packages/permissionless-test/ep-0.7/index.test.ts index ca9cbaa6..e7a31a4c 100644 --- a/packages/permissionless-test/ep-0.7/index.test.ts +++ b/packages/permissionless-test/ep-0.7/index.test.ts @@ -5,10 +5,11 @@ import { getSenderAddress, getUserOperationHash } from "permissionless" +import type { PackedUserOperation } from "permissionless/types" import { + getPackedUserOperation, getRequiredPrefund, - signUserOperationHashWithECDSA, - getPackedUserOperation + signUserOperationHashWithECDSA } from "permissionless/utils" import { type Address, @@ -30,7 +31,6 @@ import { getSmartAccountClient, getTestingChain } from "./utils" -import type { PackedUserOperation } from "permissionless/types" dotenv.config() From 84d841d9f5474c0c5dc806ec6e8177551b2307e8 Mon Sep 17 00:00:00 2001 From: Garvit Khatri Date: Thu, 11 Apr 2024 00:30:56 +0530 Subject: [PATCH 3/3] added changeset --- .changeset/early-dogs-dream.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/early-dogs-dream.md diff --git a/.changeset/early-dogs-dream.md b/.changeset/early-dogs-dream.md new file mode 100644 index 00000000..f0d0aed3 --- /dev/null +++ b/.changeset/early-dogs-dream.md @@ -0,0 +1,5 @@ +--- +"permissionless": patch +--- + +Added util function getPackedUserOperation