From b0dfe5c6e99412d4b4927a139e8bfc1a25817acd Mon Sep 17 00:00:00 2001 From: Garvit Khatri Date: Fri, 5 Jul 2024 12:33:14 +0100 Subject: [PATCH 1/8] Make fields of execution mode optional with defaults --- .../actions/erc7579/supportsExecutionMode.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/permissionless/actions/erc7579/supportsExecutionMode.ts b/packages/permissionless/actions/erc7579/supportsExecutionMode.ts index b204d2ea..f9e0bf53 100644 --- a/packages/permissionless/actions/erc7579/supportsExecutionMode.ts +++ b/packages/permissionless/actions/erc7579/supportsExecutionMode.ts @@ -20,9 +20,9 @@ export type CallType = "call" | "delegatecall" | "batchcall" export type ExecutionMode = { type: callType - revertOnError: boolean - selector: Hex - context: Hex + revertOnError?: boolean + selector?: Hex + context?: Hex } export type SupportsExecutionModeParameters< @@ -33,8 +33,8 @@ export type SupportsExecutionModeParameters< callType extends CallType = CallType > = GetAccountParameter & ExecutionMode -function parseCallType(executionMode: CallType) { - switch (executionMode) { +function parseCallType(callType: CallType) { + switch (callType) { case "call": return "0x00" case "delegatecall": @@ -56,8 +56,8 @@ export function encodeExecutionMode({ toHex(toBytes(parseCallType(type), { size: 1 })), toHex(toBytes(revertOnError ? "0x01" : "0x00", { size: 1 })), toHex(toBytes("0x0", { size: 4 })), - toHex(toBytes(selector, { size: 4 })), - toHex(toBytes(context, { size: 22 })) + toHex(toBytes(selector ?? "0x", { size: 4 })), + toHex(toBytes(context ?? "0x", { size: 22 })) ] ) } From 3d7bd7f13455f84739528934c83b00f91f79e8a2 Mon Sep 17 00:00:00 2001 From: Garvit Khatri Date: Fri, 5 Jul 2024 13:25:34 +0100 Subject: [PATCH 2/8] add more tests --- .../erc7579/supportsExecutionMode.test.ts | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/packages/permissionless/actions/erc7579/supportsExecutionMode.test.ts b/packages/permissionless/actions/erc7579/supportsExecutionMode.test.ts index df2637dd..e2c77bd5 100644 --- a/packages/permissionless/actions/erc7579/supportsExecutionMode.test.ts +++ b/packages/permissionless/actions/erc7579/supportsExecutionMode.test.ts @@ -63,6 +63,59 @@ describe.each(getCoreSmartAccounts())( supportsExecutionModeBatchCallBeforeDeployPostDeploy ).toBe(true) + expect(supportsExecutionModeBatchCallBeforeDeploy).toBe( + supportsExecutionModeBatchCallBeforeDeployPostDeploy + ) + } + ) + testWithRpc.skipIf(!getErc7579SmartAccountClient)( + "supportsExecutionMode", + async ({ rpc }) => { + const { anvilRpc, altoRpc, paymasterRpc } = rpc + + if (!getErc7579SmartAccountClient) { + throw new Error("getErc7579SmartAccountClient not defined") + } + + const smartClient = await getErc7579SmartAccountClient({ + entryPoint: ENTRYPOINT_ADDRESS_V07, + privateKey: generatePrivateKey(), + altoRpc: altoRpc, + anvilRpc: anvilRpc, + paymasterClient: getPimlicoPaymasterClient({ + entryPoint: ENTRYPOINT_ADDRESS_V07, + paymasterRpc + }) + }) + + const supportsExecutionModeBatchCallBeforeDeploy = + await supportsExecutionMode(smartClient as any, { + account: smartClient.account as any, + type: "delegatecall" + }) + + expect(supportsExecutionModeBatchCallBeforeDeploy).toBe(true) + + // deploy account + await smartClient.sendTransaction({ + to: zeroAddress, + value: 0n, + data: "0x" + }) + + const supportsExecutionModeBatchCallBeforeDeployPostDeploy = + await supportsExecutionMode(smartClient as any, { + account: smartClient.account as any, + type: "batchcall", + revertOnError: false, + selector: "0x0", + context: "0x" + }) + + expect( + supportsExecutionModeBatchCallBeforeDeployPostDeploy + ).toBe(true) + expect(supportsExecutionModeBatchCallBeforeDeploy).toBe( supportsExecutionModeBatchCallBeforeDeployPostDeploy ) From b08b3945664611c0b14e0f34216372337849151e Mon Sep 17 00:00:00 2001 From: Garvit Khatri Date: Fri, 5 Jul 2024 16:48:05 +0100 Subject: [PATCH 3/8] Add more test coverage --- .../prepareUserOperationRequest.test.ts | 373 +++++++++++++++++- 1 file changed, 371 insertions(+), 2 deletions(-) diff --git a/packages/permissionless/actions/smartAccount/prepareUserOperationRequest.test.ts b/packages/permissionless/actions/smartAccount/prepareUserOperationRequest.test.ts index f1755183..3bf09aec 100644 --- a/packages/permissionless/actions/smartAccount/prepareUserOperationRequest.test.ts +++ b/packages/permissionless/actions/smartAccount/prepareUserOperationRequest.test.ts @@ -2,11 +2,15 @@ import { type Chain, type Client, type Transport, zeroAddress } from "viem" import { generatePrivateKey } from "viem/accounts" import { describe, expect } from "vitest" import { testWithRpc } from "../../../permissionless-test/src/testWithRpc" -import { getCoreSmartAccounts } from "../../../permissionless-test/src/utils" +import { + getCoreSmartAccounts, + getPimlicoBundlerClient, + getPimlicoPaymasterClient +} from "../../../permissionless-test/src/utils" import type { SmartAccount } from "../../accounts" -import type { SmartAccountClient } from "../../clients/createSmartAccountClient" import type { EntryPoint } from "../../types/entrypoint" import { ENTRYPOINT_ADDRESS_V06, ENTRYPOINT_ADDRESS_V07 } from "../../utils" +import { pimlicoPaymasterActions } from "../pimlico" import { prepareUserOperationRequest } from "./prepareUserOperationRequest" describe.each(getCoreSmartAccounts())( @@ -81,6 +85,189 @@ describe.each(getCoreSmartAccounts())( } ) + testWithRpc.skipIf(!supportsEntryPointV06)( + "prepareUserOperationRequest_v06", + async ({ rpc }) => { + const { anvilRpc, altoRpc, paymasterRpc } = rpc + + const smartClient = await getSmartAccountClient({ + entryPoint: ENTRYPOINT_ADDRESS_V06, + privateKey: generatePrivateKey(), + altoRpc: altoRpc, + anvilRpc: anvilRpc + }) + + const pimlicoBundlerClient = getPimlicoBundlerClient({ + entryPoint: ENTRYPOINT_ADDRESS_V06, + altoRpc: altoRpc + }) + + const pimlicoPaymasterActions = getPimlicoPaymasterClient({ + entryPoint: ENTRYPOINT_ADDRESS_V06, + paymasterRpc: paymasterRpc + }) + + const userOperation = await prepareUserOperationRequest( + smartClient as Client< + Transport, + Chain, + SmartAccount + >, + { + userOperation: { + callData: await smartClient.account.encodeCallData({ + to: zeroAddress, + data: "0x", + value: 0n + }) + }, + middleware: { + gasPrice: async () => + ( + await pimlicoBundlerClient.getUserOperationGasPrice() + ).fast, + sponsorUserOperation: + pimlicoPaymasterActions.sponsorUserOperation + } + } + ) + + expect(userOperation).toBeTruthy() + expect(userOperation.sender).toBe(smartClient.account.address) + expect(userOperation.nonce).toBe( + await smartClient.account.getNonce() + ) + expect(userOperation.initCode).toBe( + await smartClient.account.getInitCode() + ) + expect(userOperation.callData).toBe( + await smartClient.account.encodeCallData({ + to: zeroAddress, + data: "0x", + value: 0n + }) + ) + expect(userOperation.callGasLimit).toBeTruthy() + expect(userOperation.verificationGasLimit).toBeTruthy() + expect(userOperation.maxFeePerGas).toBeTruthy() + expect(userOperation.maxPriorityFeePerGas).toBeTruthy() + expect(userOperation.paymasterAndData).toBeTruthy() + expect(userOperation.signature).toBe( + // @ts-ignore: since tests return all smart account client, some of them don't support V06. + // The TS error is because in that case, getDummySignature would not accept the userOperation of type UserOperation + await smartClient.account.getDummySignature(userOperation) + ) + + expect(userOperation.factory).toBe(undefined) + expect(userOperation.factoryData).toBe(undefined) + expect(userOperation.paymaster).toBe(undefined) + expect(userOperation.paymasterVerificationGasLimit).toBe( + undefined + ) + expect(userOperation.paymasterPostOpGasLimit).toBe(undefined) + expect(userOperation.paymasterData).toBe(undefined) + } + ) + + testWithRpc.skipIf(!supportsEntryPointV06)( + "prepareUserOperationRequest_v06", + async ({ rpc }) => { + const { anvilRpc, altoRpc, paymasterRpc } = rpc + + const smartClient = await getSmartAccountClient({ + entryPoint: ENTRYPOINT_ADDRESS_V06, + privateKey: generatePrivateKey(), + altoRpc: altoRpc, + anvilRpc: anvilRpc + }) + + const pimlicoBundlerClient = getPimlicoBundlerClient({ + entryPoint: ENTRYPOINT_ADDRESS_V06, + altoRpc: altoRpc + }) + const pimlicoPaymasterActions = getPimlicoPaymasterClient({ + entryPoint: ENTRYPOINT_ADDRESS_V06, + paymasterRpc: paymasterRpc + }) + + const userOperation = await prepareUserOperationRequest( + smartClient as Client< + Transport, + Chain, + SmartAccount + >, + { + userOperation: { + callData: await smartClient.account.encodeCallData({ + to: zeroAddress, + data: "0x", + value: 0n + }) + }, + middleware: async (args: { + userOperation: T + entryPoint: EntryPoint + }) => { + const gasPrice = ( + await pimlicoBundlerClient.getUserOperationGasPrice() + ).fast + + return { + ...args.userOperation, + maxFeePerGas: gasPrice.maxFeePerGas, + maxPriorityFeePerGas: + gasPrice.maxPriorityFeePerGas, + ...(await pimlicoPaymasterActions.sponsorUserOperation( + { + userOperation: { + ...args.userOperation, + maxFeePerGas: gasPrice.maxFeePerGas, + maxPriorityFeePerGas: + gasPrice.maxPriorityFeePerGas + } + } + )) + } as T + } + } + ) + + expect(userOperation).toBeTruthy() + expect(userOperation.sender).toBe(smartClient.account.address) + expect(userOperation.nonce).toBe( + await smartClient.account.getNonce() + ) + expect(userOperation.initCode).toBe( + await smartClient.account.getInitCode() + ) + expect(userOperation.callData).toBe( + await smartClient.account.encodeCallData({ + to: zeroAddress, + data: "0x", + value: 0n + }) + ) + expect(userOperation.callGasLimit).toBeTruthy() + expect(userOperation.verificationGasLimit).toBeTruthy() + expect(userOperation.maxFeePerGas).toBeTruthy() + expect(userOperation.maxPriorityFeePerGas).toBeTruthy() + expect(userOperation.paymasterAndData).toBeTruthy() + expect(userOperation.signature).toBe( + // @ts-ignore: since tests return all smart account client, some of them don't support V06. + // The TS error is because in that case, getDummySignature would not accept the userOperation of type UserOperation + await smartClient.account.getDummySignature(userOperation) + ) + + expect(userOperation.factory).toBe(undefined) + expect(userOperation.factoryData).toBe(undefined) + expect(userOperation.paymaster).toBe(undefined) + expect(userOperation.paymasterVerificationGasLimit).toBe( + undefined + ) + expect(userOperation.paymasterPostOpGasLimit).toBe(undefined) + expect(userOperation.paymasterData).toBe(undefined) + } + ) testWithRpc.skipIf(!supportsEntryPointV07)( "prepareUserOperationRequest_v07", async ({ rpc }) => { @@ -147,5 +334,187 @@ describe.each(getCoreSmartAccounts())( expect(userOperation.initCode).toBe(undefined) } ) + + testWithRpc.skipIf(!supportsEntryPointV07)( + "prepareUserOperationRequest_v07", + async ({ rpc }) => { + const { anvilRpc, altoRpc, paymasterRpc } = rpc + + const smartClient = await getSmartAccountClient({ + entryPoint: ENTRYPOINT_ADDRESS_V07, + privateKey: generatePrivateKey(), + altoRpc: altoRpc, + anvilRpc: anvilRpc + }) + + const pimlicoBundlerClient = getPimlicoBundlerClient({ + entryPoint: ENTRYPOINT_ADDRESS_V07, + altoRpc: altoRpc + }) + + const pimlicoPaymasterActions = getPimlicoPaymasterClient({ + entryPoint: ENTRYPOINT_ADDRESS_V07, + paymasterRpc: paymasterRpc + }) + + const userOperation = await prepareUserOperationRequest( + smartClient as Client< + Transport, + Chain, + SmartAccount + >, + { + userOperation: { + callData: await smartClient.account.encodeCallData({ + to: zeroAddress, + data: "0x", + value: 0n + }) + }, + middleware: { + gasPrice: async () => + ( + await pimlicoBundlerClient.getUserOperationGasPrice() + ).fast, + sponsorUserOperation: + pimlicoPaymasterActions.sponsorUserOperation + } + } + ) + + expect(userOperation).toBeTruthy() + expect(userOperation.sender).toBe(smartClient.account.address) + expect(userOperation.nonce).toBe( + await smartClient.account.getNonce() + ) + expect(userOperation.factory).toBe( + await smartClient.account.getFactory() + ) + expect(userOperation.factoryData).toBe( + await smartClient.account.getFactoryData() + ) + expect(userOperation.callData).toBe( + await smartClient.account.encodeCallData({ + to: zeroAddress, + data: "0x", + value: 0n + }) + ) + expect(userOperation.callGasLimit).toBeTruthy() + expect(userOperation.verificationGasLimit).toBeTruthy() + expect(userOperation.maxFeePerGas).toBeTruthy() + expect(userOperation.maxPriorityFeePerGas).toBeTruthy() + expect(userOperation.paymaster).toBeTruthy() + expect(userOperation.paymasterVerificationGasLimit).toBeTruthy() + expect(userOperation.signature).toBe( + // @ts-ignore: since tests return all smart account client, some of them don't support V07. + // The TS error is because in that case, getDummySignature would not accept the userOperation of type UserOperation + await smartClient.account.getDummySignature(userOperation) + ) + expect(userOperation.paymasterPostOpGasLimit).toBe(1n) + expect(userOperation.paymasterData).toBeTruthy() + expect(userOperation.paymasterAndData).toBe(undefined) + expect(userOperation.initCode).toBe(undefined) + } + ) + + testWithRpc.skipIf(!supportsEntryPointV07)( + "prepareUserOperationRequest_v07", + async ({ rpc }) => { + const { anvilRpc, altoRpc, paymasterRpc } = rpc + + const smartClient = await getSmartAccountClient({ + entryPoint: ENTRYPOINT_ADDRESS_V07, + privateKey: generatePrivateKey(), + altoRpc: altoRpc, + anvilRpc: anvilRpc + }) + + const pimlicoBundlerClient = getPimlicoBundlerClient({ + entryPoint: ENTRYPOINT_ADDRESS_V07, + altoRpc: altoRpc + }) + const pimlicoPaymasterActions = getPimlicoPaymasterClient({ + entryPoint: ENTRYPOINT_ADDRESS_V07, + paymasterRpc: paymasterRpc + }) + + const userOperation = await prepareUserOperationRequest( + smartClient as Client< + Transport, + Chain, + SmartAccount + >, + { + userOperation: { + callData: await smartClient.account.encodeCallData({ + to: zeroAddress, + data: "0x", + value: 0n + }) + }, + middleware: async (args: { + userOperation: T + entryPoint: EntryPoint + }) => { + const gasPrice = ( + await pimlicoBundlerClient.getUserOperationGasPrice() + ).fast + + return { + ...args.userOperation, + maxFeePerGas: gasPrice.maxFeePerGas, + maxPriorityFeePerGas: + gasPrice.maxPriorityFeePerGas, + ...(await pimlicoPaymasterActions.sponsorUserOperation( + { + userOperation: { + ...args.userOperation, + maxFeePerGas: gasPrice.maxFeePerGas, + maxPriorityFeePerGas: + gasPrice.maxPriorityFeePerGas + } + } + )) + } as T + } + } + ) + + expect(userOperation).toBeTruthy() + expect(userOperation.sender).toBe(smartClient.account.address) + expect(userOperation.nonce).toBe( + await smartClient.account.getNonce() + ) + expect(userOperation.factory).toBe( + await smartClient.account.getFactory() + ) + expect(userOperation.factoryData).toBe( + await smartClient.account.getFactoryData() + ) + expect(userOperation.callData).toBe( + await smartClient.account.encodeCallData({ + to: zeroAddress, + data: "0x", + value: 0n + }) + ) + expect(userOperation.callGasLimit).toBeTruthy() + expect(userOperation.verificationGasLimit).toBeTruthy() + expect(userOperation.maxFeePerGas).toBeTruthy() + expect(userOperation.maxPriorityFeePerGas).toBeTruthy() + expect(userOperation.paymaster).toBeTruthy() + expect(userOperation.paymasterVerificationGasLimit).toBeTruthy() + expect(userOperation.signature).toBe( + // @ts-ignore: since tests return all smart account client, some of them don't support V07. + // The TS error is because in that case, getDummySignature would not accept the userOperation of type UserOperation + await smartClient.account.getDummySignature(userOperation) + ) + expect(userOperation.paymasterPostOpGasLimit).toBe(1n) + expect(userOperation.paymasterData).toBeTruthy() + expect(userOperation.paymasterAndData).toBe(undefined) + expect(userOperation.initCode).toBe(undefined) + } + ) } ) From 4a7fd86b5341ac7d849bb2ff704cbedc22078e18 Mon Sep 17 00:00:00 2001 From: Garvit Khatri Date: Fri, 5 Jul 2024 17:20:24 +0100 Subject: [PATCH 4/8] Add smartAccount exports test --- .../actions/smartAccount.test.ts | 198 ++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 packages/permissionless/actions/smartAccount.test.ts diff --git a/packages/permissionless/actions/smartAccount.test.ts b/packages/permissionless/actions/smartAccount.test.ts new file mode 100644 index 00000000..c2a63c7e --- /dev/null +++ b/packages/permissionless/actions/smartAccount.test.ts @@ -0,0 +1,198 @@ +import type { + Abi, + Account, + Address, + Chain, + Client, + Hash, + Hex, + SignTypedDataParameters, + Transport, + TypedData +} from "viem" +import type { SignMessageParameters } from "viem" +import { describe, expectTypeOf, test } from "vitest" +import type { SmartAccount } from "../accounts" +import type { BundlerRpcSchema } from "../types/bundler" +import type { + ENTRYPOINT_ADDRESS_V06_TYPE, + EntryPoint +} from "../types/entrypoint" +import { + type DeployContractParametersWithPaymaster, + type PrepareUserOperationRequestParameters, + type PrepareUserOperationRequestReturnType, + type SendTransactionWithPaymasterParameters, + type SendTransactionsWithPaymasterParameters, + type SendUserOperationParameters, + type WriteContractWithPaymasterParameters, + deployContract, + prepareUserOperationRequest, + sendTransaction, + sendTransactions, + sendUserOperation, + signMessage, + signTypedData, + writeContract +} from "./smartAccount" + +describe("index", () => { + test("sendUserOperation", () => { + expectTypeOf(deployContract).toBeFunction() + expectTypeOf(deployContract) + .parameter(0) + .toMatchTypeOf< + Client< + Transport, + Chain | undefined, + Account | undefined, + BundlerRpcSchema + > + >() + expectTypeOf(deployContract) + .parameter(1) + .toMatchTypeOf>() + expectTypeOf(deployContract).returns.toMatchTypeOf>() + }) + test("prepareUserOperationRequest", () => { + expectTypeOf(prepareUserOperationRequest).toBeFunction() + expectTypeOf(prepareUserOperationRequest) + .parameter(0) + .toMatchTypeOf< + Client< + Transport, + Chain | undefined, + Account | undefined, + BundlerRpcSchema + > + >() + expectTypeOf(prepareUserOperationRequest) + .parameter(1) + .toMatchTypeOf>() + expectTypeOf(prepareUserOperationRequest).returns.toMatchTypeOf< + Promise> + >() + }) + test("sendTransaction", () => { + expectTypeOf(sendTransaction).toBeFunction() + expectTypeOf(sendTransaction) + .parameter(0) + .toMatchTypeOf< + Client< + Transport, + Chain | undefined, + Account | undefined, + BundlerRpcSchema + > + >() + expectTypeOf(sendTransaction) + .parameter(1) + .toMatchTypeOf>() + expectTypeOf(sendTransaction).returns.toMatchTypeOf>() + }) + test("sendTransactions", () => { + expectTypeOf(sendTransactions).toBeFunction() + expectTypeOf(sendTransactions) + .parameter(0) + .toMatchTypeOf< + Client< + Transport, + Chain | undefined, + Account | undefined, + BundlerRpcSchema + > + >() + expectTypeOf(sendTransactions) + .parameter(1) + .toMatchTypeOf< + SendTransactionsWithPaymasterParameters + >() + expectTypeOf(sendTransactions).returns.toMatchTypeOf>() + }) + test("writeContract", () => { + expectTypeOf(writeContract).toBeFunction() + expectTypeOf(writeContract) + .parameter(0) + .toMatchTypeOf< + Client< + Transport, + Chain | undefined, + Account | undefined, + BundlerRpcSchema + > + >() + expectTypeOf( + writeContract< + ENTRYPOINT_ADDRESS_V06_TYPE, + Chain, + SmartAccount, + Abi + > + ) + .parameter(1) + .toMatchTypeOf< + WriteContractWithPaymasterParameters< + ENTRYPOINT_ADDRESS_V06_TYPE, + Chain, + SmartAccount, + Abi + > + >() + expectTypeOf(writeContract).returns.toMatchTypeOf>() + }) + test("sendUserOperation", () => { + expectTypeOf(sendUserOperation).toBeFunction() + expectTypeOf(sendUserOperation) + .parameter(0) + .toMatchTypeOf< + Client< + Transport, + Chain | undefined, + Account | undefined, + BundlerRpcSchema + > + >() + expectTypeOf(sendUserOperation) + .parameter(1) + .toMatchTypeOf>() + expectTypeOf(sendUserOperation).returns.toMatchTypeOf>() + }) + test("signMessage", () => { + expectTypeOf(signMessage).toBeFunction() + expectTypeOf(signMessage) + .parameter(0) + .toMatchTypeOf< + Client< + Transport, + Chain | undefined, + Account | undefined, + BundlerRpcSchema + > + >() + expectTypeOf(signMessage) + .parameter(1) + .toMatchTypeOf>>() + expectTypeOf(signMessage).returns.toMatchTypeOf>() + }) + test("signTypedData", () => { + expectTypeOf(signTypedData).toBeFunction() + expectTypeOf(signTypedData) + .parameter(0) + .toMatchTypeOf< + Client< + Transport, + Chain | undefined, + Account | undefined, + BundlerRpcSchema + > + >() + expectTypeOf( + signTypedData + ) + .parameter(1) + .toMatchTypeOf< + SignTypedDataParameters + >() + expectTypeOf(signTypedData).returns.toMatchTypeOf>() + }) +}) From 43866b2112148d814659644d1ec021c573d98727 Mon Sep 17 00:00:00 2001 From: Garvit Khatri Date: Sat, 6 Jul 2024 10:37:14 +0100 Subject: [PATCH 5/8] Add client's tests --- .../clients/createBundlerClient.test.ts | 26 +++++++++ .../clients/createSmartAccountClient.test.ts | 55 +++++++++++++++++++ .../clients/decorators/smartAccount.test.ts | 55 +++++++++++++++++++ 3 files changed, 136 insertions(+) create mode 100644 packages/permissionless/clients/createBundlerClient.test.ts create mode 100644 packages/permissionless/clients/createSmartAccountClient.test.ts create mode 100644 packages/permissionless/clients/decorators/smartAccount.test.ts diff --git a/packages/permissionless/clients/createBundlerClient.test.ts b/packages/permissionless/clients/createBundlerClient.test.ts new file mode 100644 index 00000000..42cf80ea --- /dev/null +++ b/packages/permissionless/clients/createBundlerClient.test.ts @@ -0,0 +1,26 @@ +import { http } from "viem" +import { describe, expect } from "vitest" +import { testWithRpc } from "../../permissionless-test/src/testWithRpc" +import { ENTRYPOINT_ADDRESS_V06, ENTRYPOINT_ADDRESS_V07 } from "../utils" +import { createBundlerClient } from "./createBundlerClient" + +describe("createBundlerClient", () => { + testWithRpc("createBundlerClient_V06", async ({ rpc }) => { + const bundlerClientV06 = createBundlerClient({ + transport: http(rpc.altoRpc), + entryPoint: ENTRYPOINT_ADDRESS_V06 + }) + + const entryPoints = await bundlerClientV06.supportedEntryPoints() + expect(entryPoints).contain(ENTRYPOINT_ADDRESS_V06) + }) + + testWithRpc("createBundlerClient_V07", async ({ rpc }) => { + const bundlerClientV07 = createBundlerClient({ + transport: http(rpc.altoRpc), + entryPoint: ENTRYPOINT_ADDRESS_V07 + }) + const entryPoints = await bundlerClientV07.supportedEntryPoints() + expect(entryPoints).contain(ENTRYPOINT_ADDRESS_V07) + }) +}) diff --git a/packages/permissionless/clients/createSmartAccountClient.test.ts b/packages/permissionless/clients/createSmartAccountClient.test.ts new file mode 100644 index 00000000..194b93f9 --- /dev/null +++ b/packages/permissionless/clients/createSmartAccountClient.test.ts @@ -0,0 +1,55 @@ +import { http } from "viem" +import { generatePrivateKey, privateKeyToAccount } from "viem/accounts" +import { foundry } from "viem/chains" +import { describe, expect } from "vitest" +import { testWithRpc } from "../../permissionless-test/src/testWithRpc" +import { getPublicClient } from "../../permissionless-test/src/utils" +import { signerToSimpleSmartAccount } from "../accounts" +import { ENTRYPOINT_ADDRESS_V06, ENTRYPOINT_ADDRESS_V07 } from "../utils" +import { createSmartAccountClient } from "./createSmartAccountClient" + +describe("createSmartAccountClient", () => { + testWithRpc("createSmartAccountClient_V06", async ({ rpc }) => { + const publicClient = getPublicClient(rpc.anvilRpc) + + const simpleSmartAccount = await signerToSimpleSmartAccount( + publicClient, + { + entryPoint: ENTRYPOINT_ADDRESS_V06, + signer: privateKeyToAccount(generatePrivateKey()) + } + ) + + const smartAccountClient = createSmartAccountClient({ + chain: foundry, + account: simpleSmartAccount, + bundlerTransport: http(rpc.altoRpc) + }) + + expect(smartAccountClient.account.address).toBe( + simpleSmartAccount.address + ) + }) + + testWithRpc("createSmartAccountClient_V07", async ({ rpc }) => { + const publicClient = getPublicClient(rpc.anvilRpc) + + const simpleSmartAccount = await signerToSimpleSmartAccount( + publicClient, + { + entryPoint: ENTRYPOINT_ADDRESS_V07, + signer: privateKeyToAccount(generatePrivateKey()) + } + ) + + const smartAccountClient = createSmartAccountClient({ + chain: foundry, + account: simpleSmartAccount, + bundlerTransport: http(rpc.altoRpc) + }) + + expect(smartAccountClient.account.address).toBe( + simpleSmartAccount.address + ) + }) +}) diff --git a/packages/permissionless/clients/decorators/smartAccount.test.ts b/packages/permissionless/clients/decorators/smartAccount.test.ts new file mode 100644 index 00000000..d9f222ef --- /dev/null +++ b/packages/permissionless/clients/decorators/smartAccount.test.ts @@ -0,0 +1,55 @@ +import { http } from "viem" +import { generatePrivateKey, privateKeyToAccount } from "viem/accounts" +import { foundry } from "viem/chains" +import { describe, expect } from "vitest" +import { testWithRpc } from "../../../permissionless-test/src/testWithRpc" +import { getPublicClient } from "../../../permissionless-test/src/utils" +import { signerToSimpleSmartAccount } from "../../accounts" +import { ENTRYPOINT_ADDRESS_V06, ENTRYPOINT_ADDRESS_V07 } from "../../utils" +import { createSmartAccountClient } from "../createSmartAccountClient" + +describe("createSmartAccountClient", () => { + testWithRpc("createSmartAccountClient_V06", async ({ rpc }) => { + const publicClient = getPublicClient(rpc.anvilRpc) + + const simpleSmartAccount = await signerToSimpleSmartAccount( + publicClient, + { + entryPoint: ENTRYPOINT_ADDRESS_V06, + signer: privateKeyToAccount(generatePrivateKey()) + } + ) + + const smartAccountClient = createSmartAccountClient({ + chain: foundry, + account: simpleSmartAccount, + bundlerTransport: http(rpc.altoRpc) + }) + + expect(smartAccountClient.account.address).toBe( + simpleSmartAccount.address + ) + }) + + testWithRpc("createSmartAccountClient_V07", async ({ rpc }) => { + const publicClient = getPublicClient(rpc.anvilRpc) + + const simpleSmartAccount = await signerToSimpleSmartAccount( + publicClient, + { + entryPoint: ENTRYPOINT_ADDRESS_V07, + signer: privateKeyToAccount(generatePrivateKey()) + } + ) + + const smartAccountClient = createSmartAccountClient({ + chain: foundry, + account: simpleSmartAccount, + bundlerTransport: http(rpc.altoRpc) + }) + + expect(smartAccountClient.account.address).toBe( + simpleSmartAccount.address + ) + }) +}) From 46d6697768df000458041220dddbe34b6ddea80a Mon Sep 17 00:00:00 2001 From: Garvit Khatri Date: Sat, 6 Jul 2024 12:40:13 +0100 Subject: [PATCH 6/8] Add util tests --- .../permissionless/utils/deepHexlify.test.ts | 57 +++++ .../utils/encode7579CallData.test.ts | 68 ++++++ .../utils/encode7579CallData.ts | 2 +- ...ressFromInitCodeOrPaymasterAndData.test.ts | 31 +++ .../utils/getEntryPointVersion.test.ts | 52 +++++ .../utils/getPackedUserOperation.test.ts | 204 ++++++++++++++++++ .../utils/getRequiredPrefund.test.ts | 0 7 files changed, 413 insertions(+), 1 deletion(-) create mode 100644 packages/permissionless/utils/deepHexlify.test.ts create mode 100644 packages/permissionless/utils/encode7579CallData.test.ts create mode 100644 packages/permissionless/utils/getAddressFromInitCodeOrPaymasterAndData.test.ts create mode 100644 packages/permissionless/utils/getEntryPointVersion.test.ts create mode 100644 packages/permissionless/utils/getPackedUserOperation.test.ts create mode 100644 packages/permissionless/utils/getRequiredPrefund.test.ts diff --git a/packages/permissionless/utils/deepHexlify.test.ts b/packages/permissionless/utils/deepHexlify.test.ts new file mode 100644 index 00000000..0b3d3917 --- /dev/null +++ b/packages/permissionless/utils/deepHexlify.test.ts @@ -0,0 +1,57 @@ +import { toHex } from "viem" +import { describe, expect, test } from "vitest" +import { deepHexlify } from "../utils" + +describe("deepHexlify", () => { + test("should return undefined for function input", async () => { + const func = () => {} + expect(deepHexlify(func)).toBeUndefined() + }) + + test("should return null for null input", () => { + expect(deepHexlify(null)).toBeNull() + }) + + test("should return the same string for string input", () => { + const str = "test" + expect(deepHexlify(str)).toBe(str) + }) + + test("should return the same boolean for boolean input", () => { + const bool = true + expect(deepHexlify(bool)).toBe(bool) + }) + + test("should return hex string for bigint input", () => { + const bigint = BigInt(12345) + expect(deepHexlify(bigint)).toBe(toHex(bigint)) + }) + + test("should return hex string for number input", () => { + const number = 12345 + expect(deepHexlify(number)).toBe(toHex(number).replace(/^0x0/, "0x")) + }) + + test("should return array with hex values for array input", () => { + const array = [12345, BigInt(67890)] + expect(deepHexlify(array)).toEqual([ + toHex(12345).replace(/^0x0/, "0x"), + toHex(BigInt(67890)) + ]) + }) + + test("should return object with hex values for object input", () => { + const obj = { + a: 12345, + b: BigInt(67890), + c: "string", + d: true + } + expect(deepHexlify(obj)).toEqual({ + a: toHex(12345).replace(/^0x0/, "0x"), + b: toHex(BigInt(67890)), + c: "string", + d: true + }) + }) +}) diff --git a/packages/permissionless/utils/encode7579CallData.test.ts b/packages/permissionless/utils/encode7579CallData.test.ts new file mode 100644 index 00000000..e2afd8e5 --- /dev/null +++ b/packages/permissionless/utils/encode7579CallData.test.ts @@ -0,0 +1,68 @@ +import { describe, expect, test } from "vitest" +import { encode7579CallData } from "./encode7579CallData" + +describe("encode7579CallData", () => { + test("single call", async () => { + const callData = encode7579CallData({ + mode: { + type: "call" + }, + callData: { + to: "0x1234567890123456789012345678901234567890", + value: BigInt(12345), + data: "0x1234567890123456789012345678901234567890" + } + }) + + expect(callData).toBe( + "0xe9ae5c53000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000048123456789012345678901234567890123456789000000000000000000000000000000000000000000000000000000000000030391234567890123456789012345678901234567890000000000000000000000000000000000000000000000000" + ) + }) + + test("batch call", async () => { + const callData = encode7579CallData({ + mode: { + type: "batchcall" + }, + callData: [ + { + to: "0x1234567890123456789012345678901234567890", + value: BigInt(12345), + data: "0x1234567890123456789012345678901234567890" + }, + { + to: "0x1234567890123456789012345678901234567890", + value: BigInt(12345), + data: "0x1234567890123456789012345678901234567890" + } + ] + }) + + expect(callData).toBe( + "0xe9ae5c53ff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000001234567890123456789012345678901234567890000000000000000000000000000000000000000000000000000000000000303900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000014123456789012345678901234567890123456789000000000000000000000000000000000000000000000000012345678901234567890123456789012345678900000000000000000000000000000000000000000000000000000000000003039000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000141234567890123456789012345678901234567890000000000000000000000000" + ) + }) + + test("should throw error for batch call with different mode", async () => { + expect(() => { + encode7579CallData({ + mode: { + type: "call" + }, + // @ts-expect-error + callData: [ + { + to: "0x1234567890123456789012345678901234567890", + value: BigInt(12345), + data: "0x1234567890123456789012345678901234567890" + }, + { + to: "0x1234567890123456789012345678901234567890", + value: BigInt(12345), + data: "0x1234567890123456789012345678901234567890" + } + ] + }) + }).toThrowError("batchcall calldata") + }) +}) diff --git a/packages/permissionless/utils/encode7579CallData.ts b/packages/permissionless/utils/encode7579CallData.ts index 2b67887f..8390bad2 100644 --- a/packages/permissionless/utils/encode7579CallData.ts +++ b/packages/permissionless/utils/encode7579CallData.ts @@ -33,7 +33,7 @@ export function encode7579CallData({ }: EncodeCallDataParams): Hex { if (Array.isArray(callData) && mode?.type !== "batchcall") { throw new Error( - `mode ${mode} does not supported for batchcall calldata` + `mode ${JSON.stringify(mode)} does not supported for batchcall calldata` ) } diff --git a/packages/permissionless/utils/getAddressFromInitCodeOrPaymasterAndData.test.ts b/packages/permissionless/utils/getAddressFromInitCodeOrPaymasterAndData.test.ts new file mode 100644 index 00000000..972af375 --- /dev/null +++ b/packages/permissionless/utils/getAddressFromInitCodeOrPaymasterAndData.test.ts @@ -0,0 +1,31 @@ +import { describe, expect, test } from "vitest" +import { getAddressFromInitCodeOrPaymasterAndData } from "./getAddressFromInitCodeOrPaymasterAndData" + +describe("getAddressFromInitCodeOrPaymasterAndData", () => { + test("should return undefined for undefined data", () => { + expect( + // @ts-expect-error + getAddressFromInitCodeOrPaymasterAndData(undefined) + ).toBeUndefined() + }) + + test("should return undefined for empty string data", () => { + // @ts-expect-error + expect(getAddressFromInitCodeOrPaymasterAndData("")).toBeUndefined() + }) + + test("should return undefined for data shorter than 42 characters", () => { + const shortData = "0x123456789" + expect( + getAddressFromInitCodeOrPaymasterAndData(shortData) + ).toBeUndefined() + }) + + test("should return the correct address for data exactly 42 characters", () => { + const expectedAddress = "0x1234567890123456789012345678901234567890" + const exactData = `${expectedAddress}_mocked_address_for` + expect(getAddressFromInitCodeOrPaymasterAndData(exactData)).toBe( + expectedAddress + ) + }) +}) diff --git a/packages/permissionless/utils/getEntryPointVersion.test.ts b/packages/permissionless/utils/getEntryPointVersion.test.ts new file mode 100644 index 00000000..0a642c5e --- /dev/null +++ b/packages/permissionless/utils/getEntryPointVersion.test.ts @@ -0,0 +1,52 @@ +import { describe, expect, test } from "vitest" +import { + ENTRYPOINT_ADDRESS_V06, + ENTRYPOINT_ADDRESS_V07, + getEntryPointVersion, + isUserOperationVersion06, + isUserOperationVersion07 +} from "./getEntryPointVersion" + +describe("getEntryPointVersion", () => { + describe("getEntryPointVersion", () => { + test('should return "v0.6" for ENTRYPOINT_ADDRESS_V06', () => { + expect(getEntryPointVersion(ENTRYPOINT_ADDRESS_V06)).toBe("v0.6") + }) + + test('should return "v0.7" for ENTRYPOINT_ADDRESS_V07', () => { + expect(getEntryPointVersion(ENTRYPOINT_ADDRESS_V07)).toBe("v0.7") + }) + }) + + describe("isUserOperationVersion06", () => { + test('should return true for ENTRYPOINT_ADDRESS_V06 and UserOperation<"v0.6">', () => { + const userOperation = {} as any // mock UserOperation<"v0.6"> + expect( + isUserOperationVersion06(ENTRYPOINT_ADDRESS_V06, userOperation) + ).toBe(true) + }) + + test('should return false for ENTRYPOINT_ADDRESS_V07 and UserOperation<"v0.6">', () => { + const userOperation = {} as any // mock UserOperation<"v0.6"> + expect( + isUserOperationVersion06(ENTRYPOINT_ADDRESS_V07, userOperation) + ).toBe(false) + }) + }) + + describe("isUserOperationVersion07", () => { + test('should return false for ENTRYPOINT_ADDRESS_V06 and UserOperation<"v0.6">', () => { + const userOperation = {} as any // mock UserOperation<"v0.6"> + expect( + isUserOperationVersion07(ENTRYPOINT_ADDRESS_V06, userOperation) + ).toBe(false) + }) + + test('should return true for ENTRYPOINT_ADDRESS_V07 and UserOperation<"v0.7">', () => { + const userOperation = {} as any // mock UserOperation<"v0.7"> + expect( + isUserOperationVersion07(ENTRYPOINT_ADDRESS_V07, userOperation) + ).toBe(true) + }) + }) +}) diff --git a/packages/permissionless/utils/getPackedUserOperation.test.ts b/packages/permissionless/utils/getPackedUserOperation.test.ts new file mode 100644 index 00000000..9b08c0ed --- /dev/null +++ b/packages/permissionless/utils/getPackedUserOperation.test.ts @@ -0,0 +1,204 @@ +import { describe, expect, test } from "vitest" +import type { UserOperation } from "../types/userOperation" +import { + getAccountGasLimits, + getGasLimits, + getInitCode, + getPackedUserOperation, + getPaymasterAndData, + unPackInitCode, + unpackAccountGasLimits, + unpackGasLimits, + unpackPaymasterAndData +} from "./getPackedUserOperation" + +describe("getPackedUserOperation", () => { + describe("getInitCode", () => { + test("should return concatenated factory and factoryData", () => { + const userOperation = { + factory: "0x60e04be163dBb94688952449bbD9755D1Cd63231", + factoryData: "0xfedcba0987654321" + } + expect(getInitCode(userOperation as UserOperation<"v0.7">)).toBe( + "0x60e04be163dBb94688952449bbD9755D1Cd63231fedcba0987654321" + ) + }) + + test("should return factory with default factoryData when factoryData is missing", () => { + const userOperation = { + factory: "0x1234567890abcdef" + } + expect(getInitCode(userOperation as UserOperation<"v0.7">)).toBe( + "0x1234567890abcdef" + ) + }) + + test('should return "0x" when factory is missing', () => { + const userOperation = {} + expect(getInitCode(userOperation as UserOperation<"v0.7">)).toBe( + "0x" + ) + }) + }) + + describe("unPackInitCode", () => { + test('should return nulls for factory and factoryData when initCode is "0x"', () => { + expect(unPackInitCode("0x")).toEqual({ + factory: null, + factoryData: null + }) + }) + + test("should return sliced factory and factoryData from initCode", () => { + const initCode = + "0x60e04be163dBb94688952449bbD9755D1Cd63231fedcba0987654321" + expect(unPackInitCode(initCode)).toEqual({ + factory: "0x60e04be163dBb94688952449bbD9755D1Cd63231", + factoryData: "0xfedcba0987654321" + }) + }) + }) + + describe("getAccountGasLimits", () => { + test("should return concatenated padded verificationGasLimit and callGasLimit", () => { + const userOperation = { + verificationGasLimit: BigInt(1000), + callGasLimit: BigInt(2000) + } + expect( + getAccountGasLimits(userOperation as UserOperation<"v0.7">) + ).toBe( + "0x000000000000000000000000000003e8000000000000000000000000000007d0" + ) + }) + }) + + describe("unpackAccountGasLimits", () => { + test("should return verificationGasLimit and callGasLimit from sliced accountGasLimits", () => { + const accountGasLimits = + "0x000000000000000000000000000003e8000000000000000000000000000007d0" + expect(unpackAccountGasLimits(accountGasLimits)).toEqual({ + verificationGasLimit: BigInt(1000), + callGasLimit: BigInt(2000) + }) + }) + }) + + describe("getGasLimits", () => { + test("should return concatenated padded maxPriorityFeePerGas and maxFeePerGas", () => { + const userOperation = { + maxPriorityFeePerGas: BigInt(1000), + maxFeePerGas: BigInt(2000) + } + expect(getGasLimits(userOperation as UserOperation<"v0.7">)).toBe( + "0x000000000000000000000000000003e8000000000000000000000000000007d0" + ) + }) + }) + + describe("unpackGasLimits", () => { + test("should return maxPriorityFeePerGas and maxFeePerGas from sliced gasLimits", () => { + const gasLimits = + "0x000000000000000000000000000003e8000000000000000000000000000007d0" + expect(unpackGasLimits(gasLimits)).toEqual({ + maxPriorityFeePerGas: BigInt(1000), + maxFeePerGas: BigInt(2000) + }) + }) + }) + + describe("getPaymasterAndData", () => { + test("should return concatenated paymaster, verificationGasLimit, postOpGasLimit and paymasterData", () => { + const userOperation = { + paymaster: "0x60e04be163dBb94688952449bbD9755D1Cd63231", + paymasterVerificationGasLimit: BigInt(1000), + paymasterPostOpGasLimit: BigInt(2000), + paymasterData: "0xabcdef" + } + expect( + getPaymasterAndData(userOperation as UserOperation<"v0.7">) + ).toBe( + "0x60e04be163dBb94688952449bbD9755D1Cd63231000000000000000000000000000003e8000000000000000000000000000007d0abcdef" + ) + }) + + test("should return concatenated paymaster with default values when some fields are missing", () => { + const userOperation = { + paymaster: "0x1234567890abcdef" + } + expect( + getPaymasterAndData(userOperation as UserOperation<"v0.7">) + ).toBe( + "0x1234567890abcdef0000000000000000000000000000000000000000000000000000000000000000" + ) + }) + + test('should return "0x" when paymaster is missing', () => { + const userOperation = {} + expect( + getPaymasterAndData(userOperation as UserOperation<"v0.7">) + ).toBe("0x") + }) + }) + + describe("unpackPaymasterAndData", () => { + test('should return nulls for all fields when paymasterAndData is "0x"', () => { + expect(unpackPaymasterAndData("0x")).toEqual({ + paymaster: null, + paymasterVerificationGasLimit: null, + paymasterPostOpGasLimit: null, + paymasterData: null + }) + }) + + test("should return sliced fields from paymasterAndData", () => { + const paymasterAndData = + "0x60e04be163dBb94688952449bbD9755D1Cd63231000000000000000000000000000003e8000000000000000000000000000007d0abcdef" + expect(unpackPaymasterAndData(paymasterAndData)).toEqual({ + paymaster: "0x60e04be163dBb94688952449bbD9755D1Cd63231", + paymasterVerificationGasLimit: BigInt(1000), + paymasterPostOpGasLimit: BigInt(2000), + paymasterData: "0xabcdef" + }) + }) + }) + + describe("getPackedUserOperation", () => { + test("should return packed user operation", () => { + const userOperation = { + sender: "0xsender", + nonce: BigInt(1), + factory: "0xfactory", + factoryData: "0xfactoryData", + verificationGasLimit: BigInt(1000), + callGasLimit: BigInt(2000), + callData: "0xcallData", + preVerificationGas: BigInt(500), + maxPriorityFeePerGas: BigInt(3000), + maxFeePerGas: BigInt(4000), + paymaster: "0xpaymaster", + paymasterVerificationGasLimit: BigInt(5000), + paymasterPostOpGasLimit: BigInt(6000), + paymasterData: "0xpaymasterData", + signature: "0xsignature" + } + const packedUserOperation = getPackedUserOperation( + userOperation as UserOperation<"v0.7"> + ) + expect(packedUserOperation).toEqual({ + sender: "0xsender", + callData: "0xcallData", + nonce: BigInt(1), + initCode: "0xfactoryfactoryData", + accountGasLimits: + "0x000000000000000000000000000003e8000000000000000000000000000007d0", + preVerificationGas: BigInt(500), + gasFees: + "0x00000000000000000000000000000bb800000000000000000000000000000fa0", + paymasterAndData: + "0xpaymaster0000000000000000000000000000138800000000000000000000000000001770paymasterData", + signature: "0xsignature" + }) + }) + }) +}) diff --git a/packages/permissionless/utils/getRequiredPrefund.test.ts b/packages/permissionless/utils/getRequiredPrefund.test.ts new file mode 100644 index 00000000..e69de29b From 8a989a55893dda352ac06d91d5545a08d871c25e Mon Sep 17 00:00:00 2001 From: Garvit Khatri Date: Sat, 6 Jul 2024 12:53:49 +0100 Subject: [PATCH 7/8] Add getRequiredPrefund test --- .../utils/getRequiredPrefund.test.ts | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/packages/permissionless/utils/getRequiredPrefund.test.ts b/packages/permissionless/utils/getRequiredPrefund.test.ts index e69de29b..b75ff09f 100644 --- a/packages/permissionless/utils/getRequiredPrefund.test.ts +++ b/packages/permissionless/utils/getRequiredPrefund.test.ts @@ -0,0 +1,71 @@ +import { describe, expect, test } from "vitest" +import { getRequiredPrefund } from "./getRequiredPrefund" +import { + ENTRYPOINT_ADDRESS_V06, + ENTRYPOINT_ADDRESS_V07 +} from "./getEntryPointVersion" +import type { UserOperation } from "../types/userOperation" + +describe("getRequiredPrefund", () => { + describe("v0.6 UserOperation", () => { + test("should calculate the required prefund without paymasterAndData", () => { + const userOperation = { + callGasLimit: BigInt(1000), + verificationGasLimit: BigInt(2000), + preVerificationGas: BigInt(500), + maxFeePerGas: BigInt(10), + paymasterAndData: "0x" + } + const result = getRequiredPrefund({ + userOperation: userOperation as UserOperation<"v0.6">, + entryPoint: ENTRYPOINT_ADDRESS_V06 + }) + const expectedGas = + BigInt(1000) + BigInt(2000) * BigInt(1) + BigInt(500) + const expectedResult = expectedGas * BigInt(10) + expect(result).toBe(expectedResult) + }) + + test("should calculate the required prefund with paymasterAndData", () => { + const userOperation = { + callGasLimit: BigInt(1000), + verificationGasLimit: BigInt(2000), + preVerificationGas: BigInt(500), + maxFeePerGas: BigInt(10), + paymasterAndData: "0x1234" + } + const result = getRequiredPrefund({ + userOperation: userOperation as UserOperation<"v0.6">, + entryPoint: ENTRYPOINT_ADDRESS_V06 + }) + const multiplier = BigInt(3) + const expectedGas = + BigInt(1000) + BigInt(2000) * multiplier + BigInt(500) + const expectedResult = expectedGas * BigInt(10) + expect(result).toBe(expectedResult) + }) + + test("should calculate the required prefund with paymaster", () => { + const userOperation = { + callGasLimit: BigInt(1000), + verificationGasLimit: BigInt(2000), + preVerificationGas: BigInt(500), + maxFeePerGas: BigInt(10), + paymaster: "0xPaymasterAddress", + paymasterPostOpGasLimit: BigInt(100), + paymasterVerificationGasLimit: BigInt(200) + } + const result = getRequiredPrefund({ + userOperation: userOperation as UserOperation<"v0.7">, + entryPoint: ENTRYPOINT_ADDRESS_V07 + }) + const multiplier = BigInt(3) + const verificationGasLimit = + BigInt(2000) + BigInt(100) + BigInt(200) + const expectedGas = + BigInt(1000) + verificationGasLimit * multiplier + BigInt(500) + const expectedResult = expectedGas * BigInt(10) + expect(result).toBe(expectedResult) + }) + }) +}) From 36672338214fafdec94417565053ca45f0c60cf7 Mon Sep 17 00:00:00 2001 From: Garvit Khatri Date: Sat, 6 Jul 2024 12:53:57 +0100 Subject: [PATCH 8/8] Add getRequiredPrefund test --- packages/permissionless/utils/getRequiredPrefund.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/permissionless/utils/getRequiredPrefund.test.ts b/packages/permissionless/utils/getRequiredPrefund.test.ts index b75ff09f..d89d872f 100644 --- a/packages/permissionless/utils/getRequiredPrefund.test.ts +++ b/packages/permissionless/utils/getRequiredPrefund.test.ts @@ -1,10 +1,10 @@ import { describe, expect, test } from "vitest" -import { getRequiredPrefund } from "./getRequiredPrefund" +import type { UserOperation } from "../types/userOperation" import { ENTRYPOINT_ADDRESS_V06, ENTRYPOINT_ADDRESS_V07 } from "./getEntryPointVersion" -import type { UserOperation } from "../types/userOperation" +import { getRequiredPrefund } from "./getRequiredPrefund" describe("getRequiredPrefund", () => { describe("v0.6 UserOperation", () => {