From 8bab802a1b74f68d08d70a7069fa9e88ff4c7b04 Mon Sep 17 00:00:00 2001 From: mouseless <97399882+mouseless-eth@users.noreply.github.com> Date: Sat, 21 Sep 2024 18:25:56 +0100 Subject: [PATCH 01/12] add passthrough mode --- src/cli/config/bundler.ts | 3 +- src/cli/config/options.ts | 6 ++ src/cli/setupServer.ts | 1 + src/executor/executorManager.ts | 8 +- src/rpc/rpcHandler.ts | 158 +++++++++++++++++++++----------- src/types/mempool.ts | 2 +- src/utils/userop.ts | 9 +- 7 files changed, 124 insertions(+), 63 deletions(-) diff --git a/src/cli/config/bundler.ts b/src/cli/config/bundler.ts index 65c1bba0..6cdaa2a5 100644 --- a/src/cli/config/bundler.ts +++ b/src/cli/config/bundler.ts @@ -117,7 +117,8 @@ export const bundlerArgsSchema = z.object({ )}` ), "refilling-wallets": z.boolean().default(true), - "aa95-gas-multiplier": z.string().transform((val) => BigInt(val)) + "aa95-gas-multiplier": z.string().transform((val) => BigInt(val)), + "instant-bundling": z.boolean() }) export const compatibilityArgsSchema = z.object({ diff --git a/src/cli/config/options.ts b/src/cli/config/options.ts index a32b76a2..88b613ea 100644 --- a/src/cli/config/options.ts +++ b/src/cli/config/options.ts @@ -179,6 +179,12 @@ export const bundlerOptions: CliCommandOptions<IBundlerArgsInput> = { type: "string", require: false, default: "125" + }, + "instant-bundling": { + description: + "Send bundling tx as soon as userOperation passes mempool validation", + type: "boolean", + default: false } } diff --git a/src/cli/setupServer.ts b/src/cli/setupServer.ts index 82f98999..3d41b906 100644 --- a/src/cli/setupServer.ts +++ b/src/cli/setupServer.ts @@ -377,6 +377,7 @@ const getRpcHandler = ({ parsedArgs["chain-type"], parsedArgs["paymaster-gas-limit-multiplier"], eventManager, + parsedArgs["instant-bundling"], parsedArgs["dangerous-skip-user-operation-validation"] ) } diff --git a/src/executor/executorManager.ts b/src/executor/executorManager.ts index 481e674e..5ad194f9 100644 --- a/src/executor/executorManager.ts +++ b/src/executor/executorManager.ts @@ -22,7 +22,7 @@ import { receiptSchema, type GetUserOperationReceiptResponseResult } from "@alto/types" -import { getAAError, getBundleStatus } from "@alto/utils" +import { getAAError, getBundleStatus, userOperationToJson } from "@alto/utils" import { decodeEventLog, encodeEventTopics, @@ -265,10 +265,8 @@ export class ExecutorManager { }) this.logger.warn( { - userOperation: JSON.stringify( - result.error.userOperation, - (_k, v) => - typeof v === "bigint" ? v.toString() : v + userOperation: userOperationToJson( + result.error.userOperation ), userOpHash, reason diff --git a/src/rpc/rpcHandler.ts b/src/rpc/rpcHandler.ts index 9ece96c0..b3a31150 100644 --- a/src/rpc/rpcHandler.ts +++ b/src/rpc/rpcHandler.ts @@ -11,7 +11,10 @@ import type { UserOperationV06, GasPriceMultipliers, ChainType, - UserOperationV07 + UserOperationV07, + Success, + TransactionInfo, + UserOperationInfo } from "@alto/types" import { EntryPointV06Abi, @@ -131,6 +134,7 @@ export class RpcHandler implements IRpcEndpoint { gasPriceMultipliers: GasPriceMultipliers paymasterGasLimitMultiplier: bigint eventManager: EventManager + instantBundlingMode: boolean constructor( entryPoints: Address[], @@ -154,6 +158,7 @@ export class RpcHandler implements IRpcEndpoint { chainType: ChainType, paymasterGasLimitMultiplier: bigint, eventManager: EventManager, + instantBundlingMode: boolean, dangerousSkipUserOperationValidation = false ) { this.entryPoints = entryPoints @@ -180,6 +185,7 @@ export class RpcHandler implements IRpcEndpoint { this.gasPriceManager = gasPriceManager this.paymasterGasLimitMultiplier = paymasterGasLimitMultiplier this.eventManager = eventManager + this.instantBundlingMode = instantBundlingMode } async handleMethod( @@ -821,6 +827,7 @@ export class RpcHandler implements IRpcEndpoint { this.eventManager.emitFailedValidation(opHash, reason, "AA25") throw new RpcError(reason, ValidationErrors.InvalidFields) } + if (userOperationNonceValue > currentNonceValue + 10n) { const reason = "UserOperation failed validaiton with reason: AA25 invalid account nonce" @@ -841,71 +848,112 @@ export class RpcHandler implements IRpcEndpoint { } if ( - userOperationNonceValue === + userOperationNonceValue > currentNonceValue + BigInt(queuedUserOperations.length) ) { - if (this.dangerousSkipUserOperationValidation) { - const [success, errorReason] = this.mempool.add(op, entryPoint) - if (!success) { - this.eventManager.emitFailedValidation( - opHash, - errorReason, - getAAError(errorReason) - ) - throw new RpcError( - `UserOperation reverted during simulation with reason: ${errorReason}`, - ValidationErrors.InvalidFields - ) - } - } else { - if (apiVersion !== "v1") { - await this.validator.validatePreVerificationGas( - userOperation, - entryPoint - ) - } - - const validationResult = - await this.validator.validateUserOperation( - apiVersion !== "v1", - userOperation, - queuedUserOperations, - entryPoint - ) + this.nonceQueuer.add(op, entryPoint) + return "queued" + } - await this.reputationManager.checkReputation( - userOperation, - entryPoint, - validationResult + if (this.dangerousSkipUserOperationValidation) { + const [success, errorReason] = this.mempool.add(op, entryPoint) + if (!success) { + this.eventManager.emitFailedValidation( + opHash, + errorReason, + getAAError(errorReason) ) - - await this.mempool.checkEntityMultipleRoleViolation( - userOperation + throw new RpcError( + `UserOperation reverted during simulation with reason: ${errorReason}`, + ValidationErrors.InvalidFields ) + } + return "added" + } + + if (apiVersion !== "v1") { + await this.validator.validatePreVerificationGas( + userOperation, + entryPoint + ) + } + + const validationResult = await this.validator.validateUserOperation( + apiVersion !== "v1", + userOperation, + queuedUserOperations, + entryPoint + ) + + await this.reputationManager.checkReputation( + userOperation, + entryPoint, + validationResult + ) + + await this.mempool.checkEntityMultipleRoleViolation(userOperation) + + if (this.instantBundlingMode) { + const result = ( + await this.executor.bundle(entryPoint, [userOperation]) + )[0] + + const status = result.status === "success" ? "success" : "failed" + this.metrics.userOperationsSubmitted.labels({ status }).inc() + this.metrics.bundlesSubmitted + .labels({ + status + }) + .inc() + + if (result.status === "failure") { + const reason = result.error.reason - const [success, errorReason] = this.mempool.add( - op, - entryPoint, - validationResult.referencedContracts + this.monitor.setUserOperationStatus(opHash, { + status: "rejected", + transactionHash: null + }) + this.eventManager.emitDropped( + opHash, + reason, + getAAError(reason) ) - if (!success) { - this.eventManager.emitFailedValidation( - opHash, - errorReason, - getAAError(errorReason) - ) - throw new RpcError( - `UserOperation reverted during simulation with reason: ${errorReason}`, - ValidationErrors.InvalidFields - ) - } - return "added" + throw new RpcError( + `UserOperation reverted during simulation with reason: ${reason}`, + ValidationErrors.InvalidFields + ) } + + const res = result as Success<{ + userOperation: UserOperationInfo + transactionInfo: TransactionInfo + }> + const transactionInfo = res.value.transactionInfo + this.mempool.markSubmitted(opHash, transactionInfo) + + return "added" + } + + const [success, errorReason] = this.mempool.add( + op, + entryPoint, + validationResult.referencedContracts + ) + + if (!success) { + this.eventManager.emitFailedValidation( + opHash, + errorReason, + getAAError(errorReason) + ) + throw new RpcError( + `UserOperation reverted during simulation with reason: ${errorReason}`, + ValidationErrors.InvalidFields + ) } - this.nonceQueuer.add(op, entryPoint) - return "queued" + return "added" } async pimlico_sendCompressedUserOperation( diff --git a/src/types/mempool.ts b/src/types/mempool.ts index 3b77df47..e0aab7aa 100644 --- a/src/types/mempool.ts +++ b/src/types/mempool.ts @@ -70,7 +70,7 @@ export type SubmittedUserOperation = { type Result<T, E, R> = Success<T> | Failure<E> | Resubmit<R> -interface Success<T> { +export interface Success<T> { status: "success" value: T } diff --git a/src/utils/userop.ts b/src/utils/userop.ts index 330d7078..17726e4b 100644 --- a/src/utils/userop.ts +++ b/src/utils/userop.ts @@ -5,7 +5,8 @@ import { type UserOperation, type UserOperationV07, EntryPointV07Abi, - type PackedUserOperation + type PackedUserOperation, + type MempoolUserOperation } from "@alto/types" import * as sentry from "@sentry/node" import { @@ -494,6 +495,12 @@ export const getUserOperationHashV07 = ( ) } +export function userOperationToJson(userOperation: MempoolUserOperation) { + return JSON.stringify(userOperation, (_k, v) => + typeof v === "bigint" ? v.toString() : v + ) +} + export const getUserOperationHash = ( userOperation: UserOperation, entryPointAddress: Address, From 872dbbc177034e91218d10921957528a46ff4162 Mon Sep 17 00:00:00 2001 From: mouseless <97399882+mouseless-eth@users.noreply.github.com> Date: Sat, 21 Sep 2024 21:17:34 +0100 Subject: [PATCH 02/12] revert changes --- src/executor/executorManager.ts | 8 +- src/rpc/rpcHandler.ts | 158 +++++++++++--------------------- src/types/mempool.ts | 2 +- src/utils/userop.ts | 9 +- 4 files changed, 62 insertions(+), 115 deletions(-) diff --git a/src/executor/executorManager.ts b/src/executor/executorManager.ts index 5ad194f9..481e674e 100644 --- a/src/executor/executorManager.ts +++ b/src/executor/executorManager.ts @@ -22,7 +22,7 @@ import { receiptSchema, type GetUserOperationReceiptResponseResult } from "@alto/types" -import { getAAError, getBundleStatus, userOperationToJson } from "@alto/utils" +import { getAAError, getBundleStatus } from "@alto/utils" import { decodeEventLog, encodeEventTopics, @@ -265,8 +265,10 @@ export class ExecutorManager { }) this.logger.warn( { - userOperation: userOperationToJson( - result.error.userOperation + userOperation: JSON.stringify( + result.error.userOperation, + (_k, v) => + typeof v === "bigint" ? v.toString() : v ), userOpHash, reason diff --git a/src/rpc/rpcHandler.ts b/src/rpc/rpcHandler.ts index b3a31150..9ece96c0 100644 --- a/src/rpc/rpcHandler.ts +++ b/src/rpc/rpcHandler.ts @@ -11,10 +11,7 @@ import type { UserOperationV06, GasPriceMultipliers, ChainType, - UserOperationV07, - Success, - TransactionInfo, - UserOperationInfo + UserOperationV07 } from "@alto/types" import { EntryPointV06Abi, @@ -134,7 +131,6 @@ export class RpcHandler implements IRpcEndpoint { gasPriceMultipliers: GasPriceMultipliers paymasterGasLimitMultiplier: bigint eventManager: EventManager - instantBundlingMode: boolean constructor( entryPoints: Address[], @@ -158,7 +154,6 @@ export class RpcHandler implements IRpcEndpoint { chainType: ChainType, paymasterGasLimitMultiplier: bigint, eventManager: EventManager, - instantBundlingMode: boolean, dangerousSkipUserOperationValidation = false ) { this.entryPoints = entryPoints @@ -185,7 +180,6 @@ export class RpcHandler implements IRpcEndpoint { this.gasPriceManager = gasPriceManager this.paymasterGasLimitMultiplier = paymasterGasLimitMultiplier this.eventManager = eventManager - this.instantBundlingMode = instantBundlingMode } async handleMethod( @@ -827,7 +821,6 @@ export class RpcHandler implements IRpcEndpoint { this.eventManager.emitFailedValidation(opHash, reason, "AA25") throw new RpcError(reason, ValidationErrors.InvalidFields) } - if (userOperationNonceValue > currentNonceValue + 10n) { const reason = "UserOperation failed validaiton with reason: AA25 invalid account nonce" @@ -848,112 +841,71 @@ export class RpcHandler implements IRpcEndpoint { } if ( - userOperationNonceValue > + userOperationNonceValue === currentNonceValue + BigInt(queuedUserOperations.length) ) { - this.nonceQueuer.add(op, entryPoint) - return "queued" - } - - if (this.dangerousSkipUserOperationValidation) { - const [success, errorReason] = this.mempool.add(op, entryPoint) - if (!success) { - this.eventManager.emitFailedValidation( - opHash, - errorReason, - getAAError(errorReason) - ) - throw new RpcError( - `UserOperation reverted during simulation with reason: ${errorReason}`, - ValidationErrors.InvalidFields - ) - } - return "added" - } - - if (apiVersion !== "v1") { - await this.validator.validatePreVerificationGas( - userOperation, - entryPoint - ) - } - - const validationResult = await this.validator.validateUserOperation( - apiVersion !== "v1", - userOperation, - queuedUserOperations, - entryPoint - ) - - await this.reputationManager.checkReputation( - userOperation, - entryPoint, - validationResult - ) - - await this.mempool.checkEntityMultipleRoleViolation(userOperation) - - if (this.instantBundlingMode) { - const result = ( - await this.executor.bundle(entryPoint, [userOperation]) - )[0] - - const status = result.status === "success" ? "success" : "failed" - this.metrics.userOperationsSubmitted.labels({ status }).inc() - this.metrics.bundlesSubmitted - .labels({ - status - }) - .inc() + if (this.dangerousSkipUserOperationValidation) { + const [success, errorReason] = this.mempool.add(op, entryPoint) + if (!success) { + this.eventManager.emitFailedValidation( + opHash, + errorReason, + getAAError(errorReason) + ) + throw new RpcError( + `UserOperation reverted during simulation with reason: ${errorReason}`, + ValidationErrors.InvalidFields + ) + } + } else { + if (apiVersion !== "v1") { + await this.validator.validatePreVerificationGas( + userOperation, + entryPoint + ) + } - if (result.status === "failure") { - const reason = result.error.reason + const validationResult = + await this.validator.validateUserOperation( + apiVersion !== "v1", + userOperation, + queuedUserOperations, + entryPoint + ) - this.monitor.setUserOperationStatus(opHash, { - status: "rejected", - transactionHash: null - }) - this.eventManager.emitDropped( - opHash, - reason, - getAAError(reason) + await this.reputationManager.checkReputation( + userOperation, + entryPoint, + validationResult ) - throw new RpcError( - `UserOperation reverted during simulation with reason: ${reason}`, - ValidationErrors.InvalidFields + await this.mempool.checkEntityMultipleRoleViolation( + userOperation ) - } - - const res = result as Success<{ - userOperation: UserOperationInfo - transactionInfo: TransactionInfo - }> - const transactionInfo = res.value.transactionInfo - this.mempool.markSubmitted(opHash, transactionInfo) - return "added" - } - - const [success, errorReason] = this.mempool.add( - op, - entryPoint, - validationResult.referencedContracts - ) + const [success, errorReason] = this.mempool.add( + op, + entryPoint, + validationResult.referencedContracts + ) - if (!success) { - this.eventManager.emitFailedValidation( - opHash, - errorReason, - getAAError(errorReason) - ) - throw new RpcError( - `UserOperation reverted during simulation with reason: ${errorReason}`, - ValidationErrors.InvalidFields - ) + if (!success) { + this.eventManager.emitFailedValidation( + opHash, + errorReason, + getAAError(errorReason) + ) + throw new RpcError( + `UserOperation reverted during simulation with reason: ${errorReason}`, + ValidationErrors.InvalidFields + ) + } + return "added" + } } - return "added" + this.nonceQueuer.add(op, entryPoint) + return "queued" } async pimlico_sendCompressedUserOperation( diff --git a/src/types/mempool.ts b/src/types/mempool.ts index e0aab7aa..3b77df47 100644 --- a/src/types/mempool.ts +++ b/src/types/mempool.ts @@ -70,7 +70,7 @@ export type SubmittedUserOperation = { type Result<T, E, R> = Success<T> | Failure<E> | Resubmit<R> -export interface Success<T> { +interface Success<T> { status: "success" value: T } diff --git a/src/utils/userop.ts b/src/utils/userop.ts index 17726e4b..330d7078 100644 --- a/src/utils/userop.ts +++ b/src/utils/userop.ts @@ -5,8 +5,7 @@ import { type UserOperation, type UserOperationV07, EntryPointV07Abi, - type PackedUserOperation, - type MempoolUserOperation + type PackedUserOperation } from "@alto/types" import * as sentry from "@sentry/node" import { @@ -495,12 +494,6 @@ export const getUserOperationHashV07 = ( ) } -export function userOperationToJson(userOperation: MempoolUserOperation) { - return JSON.stringify(userOperation, (_k, v) => - typeof v === "bigint" ? v.toString() : v - ) -} - export const getUserOperationHash = ( userOperation: UserOperation, entryPointAddress: Address, From e685b8ff61d3b2d1c6dd55208f8901d96b0f4493 Mon Sep 17 00:00:00 2001 From: mouseless <97399882+mouseless-eth@users.noreply.github.com> Date: Mon, 23 Sep 2024 14:39:09 +0100 Subject: [PATCH 03/12] create pimlico_sendInstantBundle schema --- src/rpc/rpcHandler.ts | 14 +++++++++++++ src/types/schemas.ts | 48 ++++++++++++++++++++++++++++--------------- 2 files changed, 45 insertions(+), 17 deletions(-) diff --git a/src/rpc/rpcHandler.ts b/src/rpc/rpcHandler.ts index 9ece96c0..17bede42 100644 --- a/src/rpc/rpcHandler.ts +++ b/src/rpc/rpcHandler.ts @@ -301,6 +301,14 @@ export class RpcHandler implements IRpcEndpoint { ...request.params ) } + case "pimlico_sendInstantBundle": + return { + method, + result: await this.pimlico_sendInstantBundle( + apiVersion, + ...request.params + ) + } } } @@ -908,6 +916,12 @@ export class RpcHandler implements IRpcEndpoint { return "queued" } + async pimlico_sendInstantBundle( + apiVersion: ApiVersion, + userOperation: UserOperation, + entryPoint: Address + ) {} + async pimlico_sendCompressedUserOperation( apiVersion: ApiVersion, compressedCalldata: Hex, diff --git a/src/types/schemas.ts b/src/types/schemas.ts index 56a5d414..06f2cdea 100644 --- a/src/types/schemas.ts +++ b/src/types/schemas.ts @@ -343,6 +343,11 @@ const pimlicoSendCompressedUserOperationRequestSchema = z.object({ params: z.tuple([hexDataSchema, addressSchema, addressSchema]) }) +const pimlicoSendInstantBundleRequestSchema = z.object({ + method: z.literal("pimlico_sendInstantBundle"), + params: z.tuple([userOperationSchema, addressSchema]) +}) + export const altoVersions = z.enum(["v1", "v2"]) export type AltoVersions = z.infer<typeof altoVersions> @@ -363,7 +368,8 @@ const bundlerRequestSchema = z.discriminatedUnion("method", [ pimlicoGetStakeStatusRequestSchema, pimlicoGetUserOperationStatusRequestSchema, pimlicoGetUserOperationGasPriceRequestSchema, - pimlicoSendCompressedUserOperationRequestSchema + pimlicoSendCompressedUserOperationRequestSchema, + pimlicoSendInstantBundleRequestSchema ]) const chainIdResponseSchema = z.object({ @@ -443,23 +449,25 @@ const receiptSchema = z.object({ //type: hexNumberSchema }) +const userOperationReceiptSchema = z + .object({ + userOpHash: hexData32Schema, + entryPoint: addressSchema, + sender: addressSchema, + nonce: hexNumberSchema, + paymaster: addressSchema.optional(), + actualGasCost: hexNumberSchema, + actualGasUsed: hexNumberSchema, + success: z.boolean(), + reason: hexDataSchema.optional(), // revert reason + logs: z.array(logSchema), + receipt: receiptSchema + }) + .or(z.null()) + const getUserOperationReceiptResponseSchema = z.object({ method: z.literal("eth_getUserOperationReceipt"), - result: z - .object({ - userOpHash: hexData32Schema, - entryPoint: addressSchema, - sender: addressSchema, - nonce: hexNumberSchema, - paymaster: addressSchema.optional(), - actualGasCost: hexNumberSchema, - actualGasUsed: hexNumberSchema, - success: z.boolean(), - reason: hexDataSchema.optional(), // revert reason - logs: z.array(logSchema), - receipt: receiptSchema - }) - .or(z.null()) + result: userOperationReceiptSchema }) const bundlerClearStateResponseSchema = z.object({ @@ -570,6 +578,11 @@ const pimlicoSendCompressedUserOperationResponseSchema = z.object({ result: hexData32Schema }) +const pimlicoSendInstantBundleResponseSchema = z.object({ + method: z.literal("pimlico_sendInstantBundle"), + result: userOperationReceiptSchema +}) + const bundlerResponseSchema = z.discriminatedUnion("method", [ chainIdResponseSchema, supportedEntryPointsResponseSchema, @@ -587,7 +600,8 @@ const bundlerResponseSchema = z.discriminatedUnion("method", [ bundlerDumpReputationsResponseSchema, pimlicoGetUserOperationStatusResponseSchema, pimlicoGetUserOperationGasPriceResponseSchema, - pimlicoSendCompressedUserOperationResponseSchema + pimlicoSendCompressedUserOperationResponseSchema, + pimlicoSendInstantBundleResponseSchema ]) export type BundlingMode = z.infer< From 31585c07befe15419a04e1a2eefa50b37e18c5d8 Mon Sep 17 00:00:00 2001 From: mouseless <97399882+mouseless-eth@users.noreply.github.com> Date: Mon, 23 Sep 2024 18:59:49 +0100 Subject: [PATCH 04/12] setup sendInstantUserOperation flow --- pnpm-lock.yaml | 137 ++++++++++++++++++++++++--- src/executor/executorManager.ts | 99 +++----------------- src/package.json | 2 +- src/rpc/rpcHandler.ts | 160 ++++++++++++++++++++++++-------- src/types/schemas.ts | 4 +- src/utils/userop.ts | 114 ++++++++++++++++++++++- 6 files changed, 374 insertions(+), 142 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4a693bf2..4f7dd9a8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -55,7 +55,7 @@ importers: version: 18.16.3 viem: specifier: ^2.9.5 - version: 2.9.5(typescript@5.3.3)(zod@3.22.4) + version: 2.9.5(typescript@5.3.3) src: dependencies: @@ -138,8 +138,8 @@ importers: specifier: ^14.2.0 version: 14.2.0 viem: - specifier: ^2.9.5 - version: 2.9.5(typescript@5.3.3)(zod@3.22.4) + specifier: ^2.19.0 + version: 2.21.12(typescript@5.3.3)(zod@3.22.4) yargs: specifier: ^17.7.1 version: 17.7.2 @@ -210,7 +210,7 @@ packages: '@babel/traverse': 7.21.5 '@babel/types': 7.21.5 convert-source-map: 1.9.0 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4 gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -370,7 +370,7 @@ packages: '@babel/helper-split-export-declaration': 7.18.6 '@babel/parser': 7.21.5 '@babel/types': 7.21.5 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4 globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -855,11 +855,27 @@ packages: '@noble/hashes': 1.3.2 dev: false + /@noble/curves@1.4.0: + resolution: {integrity: sha512-p+4cb332SFCrReJkCYe8Xzm0OWi4Jji5jVdIZRL/PmacmDkFNw6MrrV+gGpiPxLHbV+zKFRywUWbaseT+tZRXg==} + dependencies: + '@noble/hashes': 1.4.0 + dev: false + /@noble/hashes@1.3.2: resolution: {integrity: sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==} engines: {node: '>= 16'} dev: false + /@noble/hashes@1.4.0: + resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==} + engines: {node: '>= 16'} + dev: false + + /@noble/hashes@1.5.0: + resolution: {integrity: sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA==} + engines: {node: ^14.21.3 || >=16} + dev: false + /@nodelib/fs.scandir@2.1.5: resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -1322,6 +1338,10 @@ packages: resolution: {integrity: sha512-Brj9FiG2W1MRQSTB212YVPRrcbjkv48FoZi/u4l/zds/ieRrqsh7aUf6CLwkAq61oKXr/ZlTzlY66gLIj3TFTQ==} dev: false + /@scure/base@1.1.9: + resolution: {integrity: sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==} + dev: false + /@scure/bip32@1.3.2: resolution: {integrity: sha512-N1ZhksgwD3OBlwTv3R6KFEcPojl/W4ElJOeCZdi+vuI5QmTFwLq3OFf2zd2ROpKvxFdgZ6hUpb0dx9bVNEwYCA==} dependencies: @@ -1330,6 +1350,14 @@ packages: '@scure/base': 1.1.5 dev: false + /@scure/bip32@1.4.0: + resolution: {integrity: sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==} + dependencies: + '@noble/curves': 1.4.0 + '@noble/hashes': 1.4.0 + '@scure/base': 1.1.9 + dev: false + /@scure/bip39@1.2.1: resolution: {integrity: sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==} dependencies: @@ -1337,6 +1365,13 @@ packages: '@scure/base': 1.1.5 dev: false + /@scure/bip39@1.4.0: + resolution: {integrity: sha512-BEEm6p8IueV/ZTfQLp/0vhw4NPnT9oWf5+28nvmeUICjP99f4vr2d+qc7AVGDDtwRep6ifR43Yed9ERVmiITzw==} + dependencies: + '@noble/hashes': 1.5.0 + '@scure/base': 1.1.9 + dev: false + /@sentry-internal/tracing@7.100.1: resolution: {integrity: sha512-+u9RRf5eL3StiyiRyAHZmdkAR7GTSGx4Mt4Lmi5NEtCcWlTGZ1QgW2r8ZbhouVmTiJkjhQgYCyej3cojtazeJg==} engines: {node: '>=8'} @@ -1575,8 +1610,22 @@ packages: zod: 3.22.4 dev: false - /abitype@1.0.0(typescript@5.3.3)(zod@3.22.4): + /abitype@1.0.0(typescript@5.3.3): resolution: {integrity: sha512-NMeMah//6bJ56H5XRj8QCV4AwuW6hB6zqz2LnhhLdcWVQOsXki6/Pn3APeqxCma62nXIcmZWdu1DlHWS74umVQ==} + peerDependencies: + typescript: '>=5.0.4' + zod: ^3 >=3.22.0 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + dependencies: + typescript: 5.3.3 + dev: false + + /abitype@1.0.5(typescript@5.3.3)(zod@3.22.4): + resolution: {integrity: sha512-YzDhti7cjlfaBhHutMaboYB21Ha3rXR9QTkNJFzYC4kC8YclaiwPBBBJY8ejFdu2wnJeZCVZSMlQJ7fi8S6hsw==} peerDependencies: typescript: '>=5.0.4' zod: ^3 >=3.22.0 @@ -1786,7 +1835,7 @@ packages: dependencies: '@fastify/error': 3.4.1 archy: 1.0.0 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4 fastq: 1.17.1 transitivePeerDependencies: - supports-color @@ -2115,6 +2164,17 @@ packages: resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==} dev: false + /debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + /debug@4.3.4(supports-color@5.5.0): resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} @@ -2139,6 +2199,7 @@ packages: dependencies: ms: 2.1.2 supports-color: 8.1.1 + dev: true /debug@4.3.6: resolution: {integrity: sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==} @@ -2760,6 +2821,7 @@ packages: /has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} + dev: true /has-property-descriptors@1.0.2: resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} @@ -2892,7 +2954,7 @@ packages: dependencies: '@ioredis/commands': 1.2.0 cluster-key-slot: 1.1.2 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4 denque: 2.1.0 lodash.defaults: 4.2.0 lodash.isarguments: 3.1.0 @@ -3093,6 +3155,14 @@ packages: ws: 8.13.0 dev: false + /isows@1.0.4(ws@8.17.1): + resolution: {integrity: sha512-hEzjY+x9u9hPmBom9IIAqdJCwNLax+xrPb51vEPpERoFlIxgmZcHzsT5jKG06nvInKOBGvReAVz80Umed5CczQ==} + peerDependencies: + ws: '*' + dependencies: + ws: 8.17.1 + dev: false + /istanbul-lib-coverage@3.2.0: resolution: {integrity: sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==} engines: {node: '>=8'} @@ -3142,7 +3212,7 @@ packages: resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} engines: {node: '>=10'} dependencies: - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4 istanbul-lib-coverage: 3.2.0 source-map: 0.6.1 transitivePeerDependencies: @@ -4419,6 +4489,7 @@ packages: engines: {node: '>=10'} dependencies: has-flag: 4.0.0 + dev: true /supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} @@ -4686,7 +4757,31 @@ packages: spdx-expression-parse: 3.0.1 dev: true - /viem@2.9.5(typescript@5.3.3)(zod@3.22.4): + /viem@2.21.12(typescript@5.3.3)(zod@3.22.4): + resolution: {integrity: sha512-yPZulbPdoDnuB8Gm2m+Qc9Mjgl6gC1YgNU6zcO+C8XZNYwaCNE6mokPiy30m8o5I1XkfvMc35jMmtT1zCb8yxA==} + peerDependencies: + typescript: '>=5.0.4' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@adraffy/ens-normalize': 1.10.0 + '@noble/curves': 1.4.0 + '@noble/hashes': 1.4.0 + '@scure/bip32': 1.4.0 + '@scure/bip39': 1.4.0 + abitype: 1.0.5(typescript@5.3.3)(zod@3.22.4) + isows: 1.0.4(ws@8.17.1) + typescript: 5.3.3 + webauthn-p256: 0.0.5 + ws: 8.17.1 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + dev: false + + /viem@2.9.5(typescript@5.3.3): resolution: {integrity: sha512-IKLmGZJEQkdt9fqvryjR+W1FsPZM+dhALT0pwR8xhBOLvVHM0/lEViGRWDpm2ArNsFszipFhc2EpKp80/PnXkw==} peerDependencies: typescript: '>=5.0.4' @@ -4699,7 +4794,7 @@ packages: '@noble/hashes': 1.3.2 '@scure/bip32': 1.3.2 '@scure/bip39': 1.2.1 - abitype: 1.0.0(typescript@5.3.3)(zod@3.22.4) + abitype: 1.0.0(typescript@5.3.3) isows: 1.0.3(ws@8.13.0) typescript: 5.3.3 ws: 8.13.0 @@ -4715,6 +4810,13 @@ packages: defaults: 1.0.4 dev: true + /webauthn-p256@0.0.5: + resolution: {integrity: sha512-drMGNWKdaixZNobeORVIqq7k5DsRC9FnG201K2QjeOoQLmtSDaSsVZdkg6n5jUALJKcAG++zBPJXmv6hy0nWFg==} + dependencies: + '@noble/curves': 1.4.0 + '@noble/hashes': 1.4.0 + dev: false + /webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} dev: true @@ -4820,6 +4922,19 @@ packages: optional: true dev: false + /ws@8.17.1: + resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + dev: false + /y18n@4.0.3: resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} dev: true diff --git a/src/executor/executorManager.ts b/src/executor/executorManager.ts index 481e674e..b6b36ec8 100644 --- a/src/executor/executorManager.ts +++ b/src/executor/executorManager.ts @@ -17,20 +17,17 @@ import { type UserOperation, type CompressedUserOperation, type UserOperationInfo, - EntryPointV06Abi, - logSchema, - receiptSchema, - type GetUserOperationReceiptResponseResult + EntryPointV06Abi } from "@alto/types" -import { getAAError, getBundleStatus } from "@alto/utils" import { - decodeEventLog, - encodeEventTopics, + getAAError, + getBundleStatus, + parseUserOperationReceipt +} from "@alto/utils" +import { getAbiItem, - parseAbi, type TransactionReceipt, TransactionReceiptNotFoundError, - zeroAddress, type Address, type Block, type Chain, @@ -40,8 +37,6 @@ import { type WatchBlocksReturnType } from "viem" import type { Executor, ReplaceTransactionResult } from "./executor" -import { z } from "zod" -import { fromZodError } from "zod-validation-error" function getTransactionsFromUserOperationEntries( entries: SubmittedUserOperation[] @@ -705,83 +700,11 @@ export class ExecutorManager { return null } - const userOperationRevertReasonAbi = parseAbi([ - "event UserOperationRevertReason(bytes32 indexed userOpHash, address indexed sender, uint256 nonce, bytes revertReason)" - ]) - - const userOperationRevertReasonTopicEvent = encodeEventTopics({ - abi: userOperationRevertReasonAbi - })[0] - - let entryPoint: Address = zeroAddress - let revertReason = undefined - - let startIndex = -1 - let endIndex = -1 - logs.forEach((log, index) => { - if (log?.topics[0] === userOperationEvent.topics[0]) { - // process UserOperationEvent - if (log.topics[1] === userOperationEvent.topics[1]) { - // it's our userOpHash. save as end of logs array - endIndex = index - entryPoint = log.address - } else if (endIndex === -1) { - // it's a different hash. remember it as beginning index, but only if we didn't find our end index yet. - startIndex = index - } - } - - if (log?.topics[0] === userOperationRevertReasonTopicEvent) { - // process UserOperationRevertReason - if (log.topics[1] === userOperationEvent.topics[1]) { - // it's our userOpHash. capture revert reason. - const decodedLog = decodeEventLog({ - abi: userOperationRevertReasonAbi, - data: log.data, - topics: log.topics - }) - - revertReason = decodedLog.args.revertReason - } - } - }) - if (endIndex === -1) { - throw new Error("fatal: no UserOperationEvent in logs") - } - - const filteredLogs = logs.slice(startIndex + 1, endIndex) - - const logsParsing = z.array(logSchema).safeParse(filteredLogs) - if (!logsParsing.success) { - const err = fromZodError(logsParsing.error) - throw err - } - - const receiptParsing = receiptSchema.safeParse({ - ...receipt, - status: receipt.status === "success" ? 1 : 0 - }) - if (!receiptParsing.success) { - const err = fromZodError(receiptParsing.error) - throw err - } - - let paymaster: Address | undefined = userOperationEvent.args.paymaster - paymaster = paymaster === zeroAddress ? undefined : paymaster - - const userOperationReceipt: GetUserOperationReceiptResponseResult = { - userOpHash: userOperationHash, - entryPoint, - sender: userOperationEvent.args.sender, - nonce: userOperationEvent.args.nonce, - paymaster, - actualGasUsed: userOperationEvent.args.actualGasUsed, - actualGasCost: userOperationEvent.args.actualGasCost, - success: userOperationEvent.args.success, - reason: revertReason, - logs: logsParsing.data, - receipt: receiptParsing.data - } + const userOperationReceipt = parseUserOperationReceipt( + userOperationHash, + logs, + receipt + ) return userOperationReceipt } diff --git a/src/package.json b/src/package.json index 9ccbe956..6231d57e 100644 --- a/src/package.json +++ b/src/package.json @@ -57,7 +57,7 @@ "pino-http": "^8.4.0", "pino-pretty": "^10.0.0", "prom-client": "^14.2.0", - "viem": "^2.9.5", + "viem": "^2.19.0", "yargs": "^17.7.1", "zod": "^3.21.4", "zod-validation-error": "^1.3.0" diff --git a/src/rpc/rpcHandler.ts b/src/rpc/rpcHandler.ts index 17bede42..d0709e70 100644 --- a/src/rpc/rpcHandler.ts +++ b/src/rpc/rpcHandler.ts @@ -11,7 +11,9 @@ import type { UserOperationV06, GasPriceMultipliers, ChainType, - UserOperationV07 + UserOperationV07, + UserOperationInfo, + TransactionInfo } from "@alto/types" import { EntryPointV06Abi, @@ -62,6 +64,7 @@ import { isVersion06, isVersion07, maxBigInt, + parseUserOperationReceipt, toUnpackedUserOperation } from "@alto/utils" import { @@ -301,10 +304,10 @@ export class RpcHandler implements IRpcEndpoint { ...request.params ) } - case "pimlico_sendInstantBundle": + case "pimlico_sendInstantUserOperation": return { method, - result: await this.pimlico_sendInstantBundle( + result: await this.pimlico_sendInstantUserOperation( apiVersion, ...request.params ) @@ -330,6 +333,48 @@ export class RpcHandler implements IRpcEndpoint { } } + // checks done before mempool goes through simulation + async preMempoolChecks( + opHash: Hex, + userOperation: UserOperation, + apiVersion: ApiVersion, + entryPoint: Address + ) { + if ( + this.legacyTransactions && + userOperation.maxFeePerGas !== userOperation.maxPriorityFeePerGas + ) { + const reason = + "maxPriorityFeePerGas must equal maxFeePerGas on chains that don't support EIP-1559" + this.eventManager.emitFailedValidation(opHash, reason) + throw new RpcError(reason) + } + + if (apiVersion !== "v1") { + await this.gasPriceManager.validateGasPrice({ + maxFeePerGas: userOperation.maxFeePerGas, + maxPriorityFeePerGas: userOperation.maxPriorityFeePerGas + }) + } + + if (userOperation.verificationGasLimit < 10000n) { + const reason = "verificationGasLimit must be at least 10000" + this.eventManager.emitFailedValidation(opHash, reason) + throw new RpcError(reason) + } + + this.logger.trace({ userOperation, entryPoint }, "beginning validation") + + if ( + userOperation.preVerificationGas === 0n || + userOperation.verificationGasLimit === 0n + ) { + const reason = "user operation gas limits must be larger than 0" + this.eventManager.emitFailedValidation(opHash, reason) + throw new RpcError(reason) + } + } + eth_chainId(): ChainIdResponseResult { return BigInt(this.chainId) } @@ -781,39 +826,12 @@ export class RpcHandler implements IRpcEndpoint { this.chainId ) - if ( - this.legacyTransactions && - userOperation.maxFeePerGas !== userOperation.maxPriorityFeePerGas - ) { - const reason = - "maxPriorityFeePerGas must equal maxFeePerGas on chains that don't support EIP-1559" - this.eventManager.emitFailedValidation(opHash, reason) - throw new RpcError(reason) - } - - if (apiVersion !== "v1") { - await this.gasPriceManager.validateGasPrice({ - maxFeePerGas: userOperation.maxFeePerGas, - maxPriorityFeePerGas: userOperation.maxPriorityFeePerGas - }) - } - - if (userOperation.verificationGasLimit < 10000n) { - const reason = "verificationGasLimit must be at least 10000" - this.eventManager.emitFailedValidation(opHash, reason) - throw new RpcError(reason) - } - - this.logger.trace({ userOperation, entryPoint }, "beginning validation") - - if ( - userOperation.preVerificationGas === 0n || - userOperation.verificationGasLimit === 0n - ) { - const reason = "user operation gas limits must be larger than 0" - this.eventManager.emitFailedValidation(opHash, reason) - throw new RpcError(reason) - } + await this.preMempoolChecks( + opHash, + userOperation, + apiVersion, + entryPoint + ) const currentNonceValue = await this.getNonceValue( userOperation, @@ -916,11 +934,77 @@ export class RpcHandler implements IRpcEndpoint { return "queued" } - async pimlico_sendInstantBundle( + async pimlico_sendInstantUserOperation( apiVersion: ApiVersion, userOperation: UserOperation, entryPoint: Address - ) {} + ) { + this.ensureEntryPointIsSupported(entryPoint) + + const opHash = getUserOperationHash( + userOperation, + entryPoint, + this.chainId + ) + + await this.preMempoolChecks( + opHash, + userOperation, + apiVersion, + entryPoint + ) + + const result = ( + await this.executor.bundle(entryPoint, [userOperation]) + )[0] + + if (result.status === "failure") { + const { userOpHash, reason } = result.error + this.monitor.setUserOperationStatus(userOpHash, { + status: "rejected", + transactionHash: null + }) + this.logger.warn( + { + userOperation: JSON.stringify( + result.error.userOperation, + (_k, v) => (typeof v === "bigint" ? v.toString() : v) + ), + userOpHash, + reason + }, + "user operation rejected" + ) + this.metrics.userOperationsSubmitted + .labels({ status: "failed" }) + .inc() + + const { error } = result + throw new RpcError( + `userOperation reverted during simulation with reason: ${error.reason}` + ) + } + + const res = result as unknown as { + status: "success" + userOperation: UserOperationInfo + transactionInfo: TransactionInfo + } + + // wait for receipt + const receipt = await this.publicClient.waitForTransactionReceipt({ + hash: res.transactionInfo.transactionHash, + pollingInterval: 100 + }) + + const userOperationReceipt = parseUserOperationReceipt( + opHash, + receipt.logs, + receipt + ) + + return userOperationReceipt + } async pimlico_sendCompressedUserOperation( apiVersion: ApiVersion, diff --git a/src/types/schemas.ts b/src/types/schemas.ts index 06f2cdea..6bf5eef6 100644 --- a/src/types/schemas.ts +++ b/src/types/schemas.ts @@ -344,7 +344,7 @@ const pimlicoSendCompressedUserOperationRequestSchema = z.object({ }) const pimlicoSendInstantBundleRequestSchema = z.object({ - method: z.literal("pimlico_sendInstantBundle"), + method: z.literal("pimlico_sendInstantUserOperation"), params: z.tuple([userOperationSchema, addressSchema]) }) @@ -579,7 +579,7 @@ const pimlicoSendCompressedUserOperationResponseSchema = z.object({ }) const pimlicoSendInstantBundleResponseSchema = z.object({ - method: z.literal("pimlico_sendInstantBundle"), + method: z.literal("pimlico_sendInstantUserOperation"), result: userOperationReceiptSchema }) diff --git a/src/utils/userop.ts b/src/utils/userop.ts index 330d7078..ffafea96 100644 --- a/src/utils/userop.ts +++ b/src/utils/userop.ts @@ -5,7 +5,10 @@ import { type UserOperation, type UserOperationV07, EntryPointV07Abi, - type PackedUserOperation + type PackedUserOperation, + type GetUserOperationReceiptResponseResult, + logSchema, + receiptSchema } from "@alto/types" import * as sentry from "@sentry/node" import { @@ -21,10 +24,17 @@ import { slice, pad, decodeErrorResult, - parseAbi + parseAbi, + encodeEventTopics, + zeroAddress, + type Log, + type TransactionReceipt, + parseEventLogs } from "viem" import { areAddressesEqual } from "./helpers" import type { Logger } from "pino" +import { z } from "zod" +import { fromZodError } from "zod-validation-error" // Type predicate check if the UserOperation is V06. export function isVersion06( @@ -586,3 +596,103 @@ export const getRequiredPrefund = (userOperation: UserOperation) => { return requiredGas * op.maxFeePerGas } + +export function parseUserOperationReceipt( + userOpHash: Hex, + logs: Log<bigint, number, false>[], + receipt: TransactionReceipt +) { + const userOperationRevertReasonAbi = parseAbi([ + "event UserOperationRevertReason(bytes32 indexed userOpHash, address indexed sender, uint256 nonce, bytes revertReason)" + ]) + const userOperationEventTopic = encodeEventTopics({ + abi: EntryPointV06Abi, + eventName: "UserOperationEvent" + }) + + const userOperationRevertReasonTopicEvent = encodeEventTopics({ + abi: userOperationRevertReasonAbi + })[0] + + let entryPoint: Address = zeroAddress + let revertReason = undefined + + let startIndex = -1 + let endIndex = -1 + logs.forEach((log, index) => { + if (log?.topics[0] === userOperationEventTopic[0]) { + // process UserOperationEvent + if (log.topics[1] === userOpHash) { + // it's our userOpHash. save as end of logs array + endIndex = index + entryPoint = log.address + } else if (endIndex === -1) { + // it's a different hash. remember it as beginning index, but only if we didn't find our end index yet. + startIndex = index + } + } + + if (log?.topics[0] === userOperationRevertReasonTopicEvent) { + // process UserOperationRevertReason + if (log.topics[1] === userOpHash) { + // it's our userOpHash. capture revert reason. + const decodedLog = decodeEventLog({ + abi: userOperationRevertReasonAbi, + data: log.data, + topics: log.topics + }) + + revertReason = decodedLog.args.revertReason + } + } + }) + + if (endIndex === -1) { + throw new Error("fatal: no UserOperationEvent in logs") + } + + const filteredLogs = logs.slice(startIndex + 1, endIndex) + + const logsParsing = z.array(logSchema).safeParse(filteredLogs) + if (!logsParsing.success) { + const err = fromZodError(logsParsing.error) + throw err + } + + const receiptParsing = receiptSchema.safeParse({ + ...receipt, + status: receipt.status === "success" ? 1 : 0 + }) + if (!receiptParsing.success) { + const err = fromZodError(receiptParsing.error) + throw err + } + + const userOperationEvent = parseEventLogs({ + abi: EntryPointV06Abi, + eventName: "UserOperationEvent", + args: { + userOpHash + }, + logs + })[0] + + let paymaster: Address | undefined = userOperationEvent.args.paymaster + paymaster = paymaster === zeroAddress ? undefined : paymaster + + const userOperationReceipt: GetUserOperationReceiptResponseResult = { + userOpHash, + entryPoint, + sender: userOperationEvent.args.sender, + nonce: userOperationEvent.args.nonce, + paymaster, + actualGasUsed: userOperationEvent.args.actualGasUsed, + actualGasCost: userOperationEvent.args.actualGasCost, + success: userOperationEvent.args.success, + reason: revertReason, + logs: logsParsing.data, + receipt: receiptParsing.data + } + + return userOperationReceipt +} From 163be4f9bc8b32e7612fc28ce0870260fe993e14 Mon Sep 17 00:00:00 2001 From: mouseless <97399882+mouseless-eth@users.noreply.github.com> Date: Mon, 23 Sep 2024 19:02:56 +0100 Subject: [PATCH 05/12] remove instant-bundling flag --- src/cli/config/bundler.ts | 3 +-- src/cli/config/options.ts | 6 ------ src/cli/setupServer.ts | 1 - 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/src/cli/config/bundler.ts b/src/cli/config/bundler.ts index 6cdaa2a5..65c1bba0 100644 --- a/src/cli/config/bundler.ts +++ b/src/cli/config/bundler.ts @@ -117,8 +117,7 @@ export const bundlerArgsSchema = z.object({ )}` ), "refilling-wallets": z.boolean().default(true), - "aa95-gas-multiplier": z.string().transform((val) => BigInt(val)), - "instant-bundling": z.boolean() + "aa95-gas-multiplier": z.string().transform((val) => BigInt(val)) }) export const compatibilityArgsSchema = z.object({ diff --git a/src/cli/config/options.ts b/src/cli/config/options.ts index 88b613ea..a32b76a2 100644 --- a/src/cli/config/options.ts +++ b/src/cli/config/options.ts @@ -179,12 +179,6 @@ export const bundlerOptions: CliCommandOptions<IBundlerArgsInput> = { type: "string", require: false, default: "125" - }, - "instant-bundling": { - description: - "Send bundling tx as soon as userOperation passes mempool validation", - type: "boolean", - default: false } } diff --git a/src/cli/setupServer.ts b/src/cli/setupServer.ts index 3d41b906..82f98999 100644 --- a/src/cli/setupServer.ts +++ b/src/cli/setupServer.ts @@ -377,7 +377,6 @@ const getRpcHandler = ({ parsedArgs["chain-type"], parsedArgs["paymaster-gas-limit-multiplier"], eventManager, - parsedArgs["instant-bundling"], parsedArgs["dangerous-skip-user-operation-validation"] ) } From 9f129c7999a37492e64bd235da90f539007a43c5 Mon Sep 17 00:00:00 2001 From: mouseless <97399882+mouseless-eth@users.noreply.github.com> Date: Mon, 23 Sep 2024 19:05:25 +0100 Subject: [PATCH 06/12] remove unnecessary args --- src/executor/executorManager.ts | 1 - src/rpc/rpcHandler.ts | 6 +----- src/utils/userop.ts | 8 +++----- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/executor/executorManager.ts b/src/executor/executorManager.ts index b6b36ec8..c8d91ab2 100644 --- a/src/executor/executorManager.ts +++ b/src/executor/executorManager.ts @@ -702,7 +702,6 @@ export class ExecutorManager { const userOperationReceipt = parseUserOperationReceipt( userOperationHash, - logs, receipt ) diff --git a/src/rpc/rpcHandler.ts b/src/rpc/rpcHandler.ts index d0709e70..e23d70e9 100644 --- a/src/rpc/rpcHandler.ts +++ b/src/rpc/rpcHandler.ts @@ -997,11 +997,7 @@ export class RpcHandler implements IRpcEndpoint { pollingInterval: 100 }) - const userOperationReceipt = parseUserOperationReceipt( - opHash, - receipt.logs, - receipt - ) + const userOperationReceipt = parseUserOperationReceipt(opHash, receipt) return userOperationReceipt } diff --git a/src/utils/userop.ts b/src/utils/userop.ts index ffafea96..cba42e5d 100644 --- a/src/utils/userop.ts +++ b/src/utils/userop.ts @@ -27,7 +27,6 @@ import { parseAbi, encodeEventTopics, zeroAddress, - type Log, type TransactionReceipt, parseEventLogs } from "viem" @@ -599,7 +598,6 @@ export const getRequiredPrefund = (userOperation: UserOperation) => { export function parseUserOperationReceipt( userOpHash: Hex, - logs: Log<bigint, number, false>[], receipt: TransactionReceipt ) { const userOperationRevertReasonAbi = parseAbi([ @@ -619,7 +617,7 @@ export function parseUserOperationReceipt( let startIndex = -1 let endIndex = -1 - logs.forEach((log, index) => { + receipt.logs.forEach((log, index) => { if (log?.topics[0] === userOperationEventTopic[0]) { // process UserOperationEvent if (log.topics[1] === userOpHash) { @@ -651,7 +649,7 @@ export function parseUserOperationReceipt( throw new Error("fatal: no UserOperationEvent in logs") } - const filteredLogs = logs.slice(startIndex + 1, endIndex) + const filteredLogs = receipt.logs.slice(startIndex + 1, endIndex) const logsParsing = z.array(logSchema).safeParse(filteredLogs) if (!logsParsing.success) { @@ -674,7 +672,7 @@ export function parseUserOperationReceipt( args: { userOpHash }, - logs + logs: receipt.logs })[0] let paymaster: Address | undefined = userOperationEvent.args.paymaster From 14feed6d0988bf9a04750d8ba6c4a4230dcac4b5 Mon Sep 17 00:00:00 2001 From: mouseless <97399882+mouseless-eth@users.noreply.github.com> Date: Mon, 23 Sep 2024 23:25:22 +0100 Subject: [PATCH 07/12] update test to use permissionless 0.2 --- pnpm-lock.yaml | 999 +++++++++++++++++- test/e2e/package.json | 7 +- test/e2e/src/testPaymaster.ts | 10 +- test/e2e/src/utils.ts | 152 ++- .../eth_estimateUserOperationGas.test.ts | 224 ++-- .../tests/eth_getUserOperationByHash.test.ts | 171 +-- .../tests/eth_getUserOperationReceipt.test.ts | 173 +-- test/e2e/tests/eth_sendUserOperation.test.ts | 587 +++++----- .../pimlico_getUserOperationGasPrice.test.ts | 110 +- ...imlico_sendCompressedUserOperation.test.ts | 100 +- .../pimlico_sendInstantUserOperation.test.ts | 56 + 11 files changed, 1773 insertions(+), 816 deletions(-) create mode 100644 test/e2e/tests/pimlico_sendInstantUserOperation.test.ts diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4f7dd9a8..c35b2100 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -169,6 +169,33 @@ importers: specifier: ^5.3.3 version: 5.3.3 + test/e2e: + dependencies: + '@pimlico/alto': + specifier: workspace:./src/ + version: link:src + '@types/node': + specifier: ^18.16.3 + version: 18.16.3 + e2e: + specifier: 'link:' + version: 'link:' + permissionless: + specifier: ^0.2.1 + version: 0.2.1(viem@2.21.12) + ts-node: + specifier: ^10.9.2 + version: 10.9.2(@types/node@18.16.3)(typescript@5.3.3) + viem: + specifier: ^2.9.5 + version: 2.21.12(typescript@5.3.3) + vitest: + specifier: ^1.6.0 + version: 1.6.0(@types/node@18.16.3) + wait-port: + specifier: ^1.1.0 + version: 1.1.0 + packages: /@adraffy/ens-normalize@1.10.0: @@ -701,7 +728,213 @@ packages: engines: {node: '>=12'} dependencies: '@jridgewell/trace-mapping': 0.3.9 - dev: true + + /@esbuild/aix-ppc64@0.21.5: + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + requiresBuild: true + dev: false + optional: true + + /@esbuild/android-arm64@0.21.5: + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: false + optional: true + + /@esbuild/android-arm@0.21.5: + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: false + optional: true + + /@esbuild/android-x64@0.21.5: + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: false + optional: true + + /@esbuild/darwin-arm64@0.21.5: + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@esbuild/darwin-x64@0.21.5: + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@esbuild/freebsd-arm64@0.21.5: + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: false + optional: true + + /@esbuild/freebsd-x64@0.21.5: + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: false + optional: true + + /@esbuild/linux-arm64@0.21.5: + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@esbuild/linux-arm@0.21.5: + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@esbuild/linux-ia32@0.21.5: + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@esbuild/linux-loong64@0.21.5: + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@esbuild/linux-mips64el@0.21.5: + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@esbuild/linux-ppc64@0.21.5: + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@esbuild/linux-riscv64@0.21.5: + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@esbuild/linux-s390x@0.21.5: + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@esbuild/linux-x64@0.21.5: + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@esbuild/netbsd-x64@0.21.5: + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: false + optional: true + + /@esbuild/openbsd-x64@0.21.5: + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: false + optional: true + + /@esbuild/sunos-x64@0.21.5: + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: false + optional: true + + /@esbuild/win32-arm64@0.21.5: + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@esbuild/win32-ia32@0.21.5: + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@esbuild/win32-x64@0.21.5: + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: false + optional: true /@fastify/ajv-compiler@3.5.0: resolution: {integrity: sha512-ebbEtlI7dxXF5ziNdr05mOY8NnDiPB1XvAlLHctRt/Rc+C3LCOVW5imUVX+mhvUhnNzmPBHewUkOFgGlCxgdAA==} @@ -784,6 +1017,13 @@ packages: engines: {node: '>=8'} dev: true + /@jest/schemas@29.6.3: + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@sinclair/typebox': 0.27.8 + dev: false + /@jridgewell/gen-mapping@0.3.3: resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} engines: {node: '>=6.0.0'} @@ -796,7 +1036,6 @@ packages: /@jridgewell/resolve-uri@3.1.0: resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} engines: {node: '>=6.0.0'} - dev: true /@jridgewell/set-array@1.1.2: resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} @@ -809,7 +1048,10 @@ packages: /@jridgewell/sourcemap-codec@1.4.15: resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} - dev: true + + /@jridgewell/sourcemap-codec@1.5.0: + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + dev: false /@jridgewell/trace-mapping@0.3.18: resolution: {integrity: sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==} @@ -823,7 +1065,6 @@ packages: dependencies: '@jridgewell/resolve-uri': 3.1.0 '@jridgewell/sourcemap-codec': 1.4.15 - dev: true /@js-sdsl/ordered-map@4.4.2: resolution: {integrity: sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==} @@ -1286,6 +1527,134 @@ packages: resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} dev: false + /@rollup/rollup-android-arm-eabi@4.22.4: + resolution: {integrity: sha512-Fxamp4aEZnfPOcGA8KSNEohV8hX7zVHOemC8jVBoBUHu5zpJK/Eu3uJwt6BMgy9fkvzxDaurgj96F/NiLukF2w==} + cpu: [arm] + os: [android] + requiresBuild: true + dev: false + optional: true + + /@rollup/rollup-android-arm64@4.22.4: + resolution: {integrity: sha512-VXoK5UMrgECLYaMuGuVTOx5kcuap1Jm8g/M83RnCHBKOqvPPmROFJGQaZhGccnsFtfXQ3XYa4/jMCJvZnbJBdA==} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: false + optional: true + + /@rollup/rollup-darwin-arm64@4.22.4: + resolution: {integrity: sha512-xMM9ORBqu81jyMKCDP+SZDhnX2QEVQzTcC6G18KlTQEzWK8r/oNZtKuZaCcHhnsa6fEeOBionoyl5JsAbE/36Q==} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@rollup/rollup-darwin-x64@4.22.4: + resolution: {integrity: sha512-aJJyYKQwbHuhTUrjWjxEvGnNNBCnmpHDvrb8JFDbeSH3m2XdHcxDd3jthAzvmoI8w/kSjd2y0udT+4okADsZIw==} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@rollup/rollup-linux-arm-gnueabihf@4.22.4: + resolution: {integrity: sha512-j63YtCIRAzbO+gC2L9dWXRh5BFetsv0j0va0Wi9epXDgU/XUi5dJKo4USTttVyK7fGw2nPWK0PbAvyliz50SCQ==} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@rollup/rollup-linux-arm-musleabihf@4.22.4: + resolution: {integrity: sha512-dJnWUgwWBX1YBRsuKKMOlXCzh2Wu1mlHzv20TpqEsfdZLb3WoJW2kIEsGwLkroYf24IrPAvOT/ZQ2OYMV6vlrg==} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@rollup/rollup-linux-arm64-gnu@4.22.4: + resolution: {integrity: sha512-AdPRoNi3NKVLolCN/Sp4F4N1d98c4SBnHMKoLuiG6RXgoZ4sllseuGioszumnPGmPM2O7qaAX/IJdeDU8f26Aw==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@rollup/rollup-linux-arm64-musl@4.22.4: + resolution: {integrity: sha512-Gl0AxBtDg8uoAn5CCqQDMqAx22Wx22pjDOjBdmG0VIWX3qUBHzYmOKh8KXHL4UpogfJ14G4wk16EQogF+v8hmA==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@rollup/rollup-linux-powerpc64le-gnu@4.22.4: + resolution: {integrity: sha512-3aVCK9xfWW1oGQpTsYJJPF6bfpWfhbRnhdlyhak2ZiyFLDaayz0EP5j9V1RVLAAxlmWKTDfS9wyRyY3hvhPoOg==} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@rollup/rollup-linux-riscv64-gnu@4.22.4: + resolution: {integrity: sha512-ePYIir6VYnhgv2C5Xe9u+ico4t8sZWXschR6fMgoPUK31yQu7hTEJb7bCqivHECwIClJfKgE7zYsh1qTP3WHUA==} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@rollup/rollup-linux-s390x-gnu@4.22.4: + resolution: {integrity: sha512-GqFJ9wLlbB9daxhVlrTe61vJtEY99/xB3C8e4ULVsVfflcpmR6c8UZXjtkMA6FhNONhj2eA5Tk9uAVw5orEs4Q==} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@rollup/rollup-linux-x64-gnu@4.22.4: + resolution: {integrity: sha512-87v0ol2sH9GE3cLQLNEy0K/R0pz1nvg76o8M5nhMR0+Q+BBGLnb35P0fVz4CQxHYXaAOhE8HhlkaZfsdUOlHwg==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@rollup/rollup-linux-x64-musl@4.22.4: + resolution: {integrity: sha512-UV6FZMUgePDZrFjrNGIWzDo/vABebuXBhJEqrHxrGiU6HikPy0Z3LfdtciIttEUQfuDdCn8fqh7wiFJjCNwO+g==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@rollup/rollup-win32-arm64-msvc@4.22.4: + resolution: {integrity: sha512-BjI+NVVEGAXjGWYHz/vv0pBqfGoUH0IGZ0cICTn7kB9PyjrATSkX+8WkguNjWoj2qSr1im/+tTGRaY+4/PdcQw==} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@rollup/rollup-win32-ia32-msvc@4.22.4: + resolution: {integrity: sha512-SiWG/1TuUdPvYmzmYnmd3IEifzR61Tragkbx9D3+R8mzQqDBz8v+BvZNDlkiTtI9T15KYZhP0ehn3Dld4n9J5g==} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@rollup/rollup-win32-x64-msvc@4.22.4: + resolution: {integrity: sha512-j8pPKp53/lq9lMXN57S8cFz0MynJk8OWNuUnXct/9KCpKU7DgU3bYMJhwWmcqC0UU29p8Lr0/7KEVcaM6bf47Q==} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: false + optional: true + /@rometools/cli-darwin-arm64@12.1.3: resolution: {integrity: sha512-AmFTUDYjBuEGQp/Wwps+2cqUr+qhR7gyXAUnkL5psCuNCz3807TrUq/ecOoct5MIavGJTH6R4aaSL6+f+VlBEg==} cpu: [arm64] @@ -1411,6 +1780,10 @@ packages: '@sentry/types': 7.100.1 dev: false + /@sinclair/typebox@0.27.8: + resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + dev: false + /@swc/core-darwin-arm64@1.3.105: resolution: {integrity: sha512-buWeweLVDXXmcnfIemH4PGnpjwsDTUGitnPchdftb0u1FU8zSSP/lw/pUCBDG/XvWAp7c/aFxgN4CyG0j7eayA==} engines: {node: '>=10'} @@ -1536,19 +1909,23 @@ packages: /@tsconfig/node10@1.0.9: resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} - dev: true /@tsconfig/node12@1.0.11: resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} - dev: true /@tsconfig/node14@1.0.3: resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} - dev: true /@tsconfig/node16@1.0.3: resolution: {integrity: sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==} - dev: true + + /@types/estree@1.0.5: + resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + dev: false + + /@types/estree@1.0.6: + resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} + dev: false /@types/minimist@1.2.5: resolution: {integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==} @@ -1593,6 +1970,45 @@ packages: '@types/yargs-parser': 21.0.3 dev: true + /@vitest/expect@1.6.0: + resolution: {integrity: sha512-ixEvFVQjycy/oNgHjqsL6AZCDduC+tflRluaHIzKIsdbzkLn2U/iBnVeJwB6HsIjQBdfMR8Z0tRxKUsvFJEeWQ==} + dependencies: + '@vitest/spy': 1.6.0 + '@vitest/utils': 1.6.0 + chai: 4.5.0 + dev: false + + /@vitest/runner@1.6.0: + resolution: {integrity: sha512-P4xgwPjwesuBiHisAVz/LSSZtDjOTPYZVmNAnpHHSR6ONrf8eCJOFRvUwdHn30F5M1fxhqtl7QZQUk2dprIXAg==} + dependencies: + '@vitest/utils': 1.6.0 + p-limit: 5.0.0 + pathe: 1.1.2 + dev: false + + /@vitest/snapshot@1.6.0: + resolution: {integrity: sha512-+Hx43f8Chus+DCmygqqfetcAZrDJwvTj0ymqjQq4CvmpKFSTVteEOBzCusu1x2tt4OJcvBflyHUE0DZSLgEMtQ==} + dependencies: + magic-string: 0.30.11 + pathe: 1.1.2 + pretty-format: 29.7.0 + dev: false + + /@vitest/spy@1.6.0: + resolution: {integrity: sha512-leUTap6B/cqi/bQkXUu6bQV5TZPx7pmMBKBQiI0rJA8c3pB56ZsaTbREnF7CJfmvAS4V2cXIBAh/3rVwrrCYgw==} + dependencies: + tinyspy: 2.2.1 + dev: false + + /@vitest/utils@1.6.0: + resolution: {integrity: sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==} + dependencies: + diff-sequences: 29.6.3 + estree-walker: 3.0.3 + loupe: 2.3.7 + pretty-format: 29.7.0 + dev: false + /abbrev@1.1.1: resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} dev: true @@ -1624,6 +2040,20 @@ packages: typescript: 5.3.3 dev: false + /abitype@1.0.5(typescript@5.3.3): + resolution: {integrity: sha512-YzDhti7cjlfaBhHutMaboYB21Ha3rXR9QTkNJFzYC4kC8YclaiwPBBBJY8ejFdu2wnJeZCVZSMlQJ7fi8S6hsw==} + peerDependencies: + typescript: '>=5.0.4' + zod: ^3 >=3.22.0 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + dependencies: + typescript: 5.3.3 + dev: false + /abitype@1.0.5(typescript@5.3.3)(zod@3.22.4): resolution: {integrity: sha512-YzDhti7cjlfaBhHutMaboYB21Ha3rXR9QTkNJFzYC4kC8YclaiwPBBBJY8ejFdu2wnJeZCVZSMlQJ7fi8S6hsw==} peerDependencies: @@ -1669,7 +2099,13 @@ packages: /acorn-walk@8.2.0: resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} engines: {node: '>=0.4.0'} - dev: true + + /acorn-walk@8.3.4: + resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} + engines: {node: '>=0.4.0'} + dependencies: + acorn: 8.12.1 + dev: false /acorn@8.12.1: resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} @@ -1738,6 +2174,11 @@ packages: dependencies: color-convert: 2.0.1 + /ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + dev: false + /anymatch@3.1.3: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} @@ -1758,7 +2199,6 @@ packages: /arg@4.1.3: resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} - dev: true /argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} @@ -1812,6 +2252,10 @@ packages: engines: {node: '>=0.10.0'} dev: true + /assertion-error@1.1.0: + resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + dev: false + /async-mutex@0.4.1: resolution: {integrity: sha512-WfoBo4E/TbCX1G95XTjbWTE3X2XLG0m1Xbv2cwOtuPdyH9CZvnaA5nCt1ucjaKEgW2A5IF71hxrRhr83Je5xjA==} dependencies: @@ -1913,6 +2357,11 @@ packages: ieee754: 1.2.1 dev: false + /cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + dev: false + /caching-transform@4.0.0: resolution: {integrity: sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==} engines: {node: '>=8'} @@ -1957,6 +2406,19 @@ packages: resolution: {integrity: sha512-KCqHwRnaa1InZBtqXzP98LPg0ajCVujMKjqKDhZEthIpAsJl/YEIa3YvXjGXPVqzZVguccuu7ga9KOE1J9rKPQ==} dev: true + /chai@4.5.0: + resolution: {integrity: sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==} + engines: {node: '>=4'} + dependencies: + assertion-error: 1.1.0 + check-error: 1.0.3 + deep-eql: 4.1.4 + get-func-name: 2.0.2 + loupe: 2.3.7 + pathval: 1.1.1 + type-detect: 4.1.0 + dev: false + /chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -1972,12 +2434,17 @@ packages: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 - dev: true /chardet@0.7.0: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} dev: true + /check-error@1.0.3: + resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} + dependencies: + get-func-name: 2.0.2 + dev: false + /chokidar@3.5.3: resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} engines: {node: '>= 8.10.0'} @@ -2067,7 +2534,6 @@ packages: /commander@9.5.0: resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} engines: {node: ^12.20.0 || >=14} - dev: true /commondir@1.0.1: resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} @@ -2077,6 +2543,10 @@ packages: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} dev: true + /confbox@0.1.7: + resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} + dev: false + /convert-source-map@1.9.0: resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} dev: true @@ -2088,7 +2558,6 @@ packages: /create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} - dev: true /cross-spawn@5.1.0: resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==} @@ -2105,7 +2574,6 @@ packages: path-key: 3.1.1 shebang-command: 2.0.0 which: 2.0.2 - dev: true /csv-generate@3.4.3: resolution: {integrity: sha512-w/T+rqR0vwvHqWs/1ZyMDWtHHSJaN06klRqJXBEpDJaM/+dZkso0OKh1VcuuYvK3XM53KysVNq8Ko/epCK8wOw==} @@ -2231,6 +2699,13 @@ packages: engines: {node: '>=10'} dev: true + /deep-eql@4.1.4: + resolution: {integrity: sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==} + engines: {node: '>=6'} + dependencies: + type-detect: 4.1.0 + dev: false + /default-require-extensions@3.0.1: resolution: {integrity: sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==} engines: {node: '>=8'} @@ -2272,10 +2747,14 @@ packages: engines: {node: '>=8'} dev: true + /diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: false + /diff@4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} - dev: true /diff@5.0.0: resolution: {integrity: sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==} @@ -2446,6 +2925,37 @@ packages: resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==} dev: true + /esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/aix-ppc64': 0.21.5 + '@esbuild/android-arm': 0.21.5 + '@esbuild/android-arm64': 0.21.5 + '@esbuild/android-x64': 0.21.5 + '@esbuild/darwin-arm64': 0.21.5 + '@esbuild/darwin-x64': 0.21.5 + '@esbuild/freebsd-arm64': 0.21.5 + '@esbuild/freebsd-x64': 0.21.5 + '@esbuild/linux-arm': 0.21.5 + '@esbuild/linux-arm64': 0.21.5 + '@esbuild/linux-ia32': 0.21.5 + '@esbuild/linux-loong64': 0.21.5 + '@esbuild/linux-mips64el': 0.21.5 + '@esbuild/linux-ppc64': 0.21.5 + '@esbuild/linux-riscv64': 0.21.5 + '@esbuild/linux-s390x': 0.21.5 + '@esbuild/linux-x64': 0.21.5 + '@esbuild/netbsd-x64': 0.21.5 + '@esbuild/openbsd-x64': 0.21.5 + '@esbuild/sunos-x64': 0.21.5 + '@esbuild/win32-arm64': 0.21.5 + '@esbuild/win32-ia32': 0.21.5 + '@esbuild/win32-x64': 0.21.5 + dev: false + /escalade@3.1.1: resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} engines: {node: '>=6'} @@ -2466,6 +2976,12 @@ packages: hasBin: true dev: true + /estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + dependencies: + '@types/estree': 1.0.6 + dev: false + /event-target-shim@5.0.1: resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} engines: {node: '>=6'} @@ -2476,6 +2992,21 @@ packages: engines: {node: '>=0.8.x'} dev: false + /execa@8.0.1: + resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} + engines: {node: '>=16.17'} + dependencies: + cross-spawn: 7.0.3 + get-stream: 8.0.1 + human-signals: 5.0.0 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.3.0 + onetime: 6.0.0 + signal-exit: 4.1.0 + strip-final-newline: 3.0.0 + dev: false + /extendable-error@0.1.7: resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} dev: true @@ -2682,7 +3213,6 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] requiresBuild: true - dev: true optional: true /function-bind@1.1.2: @@ -2711,6 +3241,10 @@ packages: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} + /get-func-name@2.0.2: + resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} + dev: false + /get-intrinsic@1.2.4: resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} engines: {node: '>= 0.4'} @@ -2727,6 +3261,11 @@ packages: engines: {node: '>=8.0.0'} dev: true + /get-stream@8.0.1: + resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} + engines: {node: '>=16'} + dev: false + /get-symbol-description@1.0.2: resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} engines: {node: '>= 0.4'} @@ -2821,7 +3360,6 @@ packages: /has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} - dev: true /has-property-descriptors@1.0.2: resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} @@ -2881,6 +3419,11 @@ packages: resolution: {integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==} dev: true + /human-signals@5.0.0: + resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} + engines: {node: '>=16.17.0'} + dev: false + /iconv-lite@0.4.24: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} @@ -3091,6 +3634,11 @@ packages: engines: {node: '>=8'} dev: true + /is-stream@3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: false + /is-string@1.0.7: resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} engines: {node: '>= 0.4'} @@ -3145,7 +3693,6 @@ packages: /isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - dev: true /isows@1.0.3(ws@8.13.0): resolution: {integrity: sha512-2cKei4vlmg2cxEjm3wVSqn8pcoRF/LX/wpifuuNquFO4SQmPwarClT+SUCA2lt+l581tTeZIPIZuIDo2jWN1fg==} @@ -3236,6 +3783,10 @@ packages: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} dev: true + /js-tokens@9.0.0: + resolution: {integrity: sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==} + dev: false + /js-yaml@3.14.1: resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} hasBin: true @@ -3315,6 +3866,14 @@ packages: strip-bom: 3.0.0 dev: true + /local-pkg@0.5.0: + resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==} + engines: {node: '>=14'} + dependencies: + mlly: 1.7.1 + pkg-types: 1.2.0 + dev: false + /locate-path@5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} @@ -3365,6 +3924,12 @@ packages: resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==} dev: false + /loupe@2.3.7: + resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} + dependencies: + get-func-name: 2.0.2 + dev: false + /lru-cache@4.1.5: resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} dependencies: @@ -3384,6 +3949,12 @@ packages: dependencies: yallist: 4.0.0 + /magic-string@0.30.11: + resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==} + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + dev: false + /make-dir@3.1.0: resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} engines: {node: '>=8'} @@ -3393,7 +3964,6 @@ packages: /make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} - dev: true /map-obj@1.0.1: resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} @@ -3422,6 +3992,10 @@ packages: yargs-parser: 18.1.3 dev: true + /merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + dev: false + /merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -3435,6 +4009,11 @@ packages: picomatch: 2.3.1 dev: true + /mimic-fn@4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + dev: false + /min-indent@1.0.1: resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} engines: {node: '>=4'} @@ -3470,6 +4049,15 @@ packages: engines: {node: '>= 8.0.0'} dev: true + /mlly@1.7.1: + resolution: {integrity: sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==} + dependencies: + acorn: 8.12.1 + pathe: 1.1.2 + pkg-types: 1.2.0 + ufo: 1.5.4 + dev: false + /mnemonist@0.39.6: resolution: {integrity: sha512-A/0v5Z59y63US00cRSLiloEIw3t5G+MiKz4BhX21FI+YBJXBOGW0ohFxTxO08dsOYlzxo87T7vGfZKYp2bcAWA==} dependencies: @@ -3526,6 +4114,12 @@ packages: hasBin: true dev: true + /nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + dev: false + /node-fetch@2.7.0: resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} engines: {node: 4.x || >=6.0.0} @@ -3587,6 +4181,13 @@ packages: engines: {node: '>=0.10.0'} dev: true + /npm-run-path@5.3.0: + resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + path-key: 4.0.0 + dev: false + /nyc@15.1.0: resolution: {integrity: sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==} engines: {node: '>=8.9'} @@ -3656,6 +4257,13 @@ packages: dependencies: wrappy: 1.0.2 + /onetime@6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + dependencies: + mimic-fn: 4.0.0 + dev: false + /opentelemetry-instrumentation-fetch-node@1.2.3(@opentelemetry/api@1.9.0): resolution: {integrity: sha512-Qb11T7KvoCevMaSeuamcLsAD+pZnavkhDnlVL0kRozfhl42dKG5Q3anUklAFKJZjY3twLR+BnRa6DlwwkIE/+A==} engines: {node: '>18.0.0'} @@ -3699,6 +4307,13 @@ packages: yocto-queue: 0.1.0 dev: true + /p-limit@5.0.0: + resolution: {integrity: sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==} + engines: {node: '>=18'} + dependencies: + yocto-queue: 1.1.1 + dev: false + /p-locate@4.1.0: resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} engines: {node: '>=8'} @@ -3763,7 +4378,11 @@ packages: /path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} - dev: true + + /path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + dev: false /path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} @@ -3773,9 +4392,28 @@ packages: engines: {node: '>=8'} dev: true + /pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + dev: false + + /pathval@1.1.1: + resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + dev: false + + /permissionless@0.2.1(viem@2.21.12): + resolution: {integrity: sha512-fBwSKQl86zvnbC+m0e/eHvVeJKCBuVR6DzrpGWcoc1seU94wAgbvDUycgo95Y8a9tQLd0h4sazUKd1I9FazTkw==} + peerDependencies: + viem: ^2.20.0 + dependencies: + viem: 2.21.12(typescript@5.3.3) + dev: false + /picocolors@1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} - dev: true + + /picocolors@1.1.0: + resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==} + dev: false /picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} @@ -3851,6 +4489,14 @@ packages: find-up: 4.1.0 dev: true + /pkg-types@1.2.0: + resolution: {integrity: sha512-+ifYuSSqOQ8CqP4MbZA5hDpb97n3E8SVWdJe+Wms9kj745lmd3b7EZJiqvmLwAlmRfjrI7Hi5z3kdBJ93lFNPA==} + dependencies: + confbox: 0.1.7 + mlly: 1.7.1 + pathe: 1.1.2 + dev: false + /plimit-lit@1.6.1: resolution: {integrity: sha512-B7+VDyb8Tl6oMJT9oSO2CW8XC/T4UcJGrwOVoNGwOQsQYhlpfajmrMj5xeejqaASq3V/EqThyOeATEOMuSEXiA==} engines: {node: '>=12'} @@ -3863,6 +4509,15 @@ packages: engines: {node: '>= 0.4'} dev: true + /postcss@8.4.47: + resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.7 + picocolors: 1.1.0 + source-map-js: 1.2.1 + dev: false + /preferred-pm@3.1.3: resolution: {integrity: sha512-MkXsENfftWSRpzCzImcp4FRsCc3y1opwB73CfCNWyzMqArju2CrlMHlqB7VexKiPEOjGMbttv1r9fSCn5S610w==} engines: {node: '>=10'} @@ -3879,6 +4534,15 @@ packages: hasBin: true dev: true + /pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/schemas': 29.6.3 + ansi-styles: 5.2.0 + react-is: 18.3.1 + dev: false + /process-on-spawn@1.0.0: resolution: {integrity: sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==} engines: {node: '>=8'} @@ -3977,6 +4641,10 @@ packages: safe-buffer: 5.2.1 dev: true + /react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + dev: false + /read-pkg-up@7.0.1: resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} engines: {node: '>=8'} @@ -4136,6 +4804,32 @@ packages: glob: 7.2.3 dev: true + /rollup@4.22.4: + resolution: {integrity: sha512-vD8HJ5raRcWOyymsR6Z3o6+RzfEPCnVLMFJ6vRslO1jt4LO6dUo5Qnpg7y4RkZFM2DMe3WUirkI5c16onjrc6A==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + dependencies: + '@types/estree': 1.0.5 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.22.4 + '@rollup/rollup-android-arm64': 4.22.4 + '@rollup/rollup-darwin-arm64': 4.22.4 + '@rollup/rollup-darwin-x64': 4.22.4 + '@rollup/rollup-linux-arm-gnueabihf': 4.22.4 + '@rollup/rollup-linux-arm-musleabihf': 4.22.4 + '@rollup/rollup-linux-arm64-gnu': 4.22.4 + '@rollup/rollup-linux-arm64-musl': 4.22.4 + '@rollup/rollup-linux-powerpc64le-gnu': 4.22.4 + '@rollup/rollup-linux-riscv64-gnu': 4.22.4 + '@rollup/rollup-linux-s390x-gnu': 4.22.4 + '@rollup/rollup-linux-x64-gnu': 4.22.4 + '@rollup/rollup-linux-x64-musl': 4.22.4 + '@rollup/rollup-win32-arm64-msvc': 4.22.4 + '@rollup/rollup-win32-ia32-msvc': 4.22.4 + '@rollup/rollup-win32-x64-msvc': 4.22.4 + fsevents: 2.3.3 + dev: false + /rome@12.1.3: resolution: {integrity: sha512-e+ff72hxDpe/t5/Us7YRBVw3PBET7SeczTQNn6tvrWdrCaAw3qOukQQ+tDCkyFtS4yGsnhjrJbm43ctNbz27Yg==} engines: {node: '>=14.*'} @@ -4268,7 +4962,6 @@ packages: engines: {node: '>=8'} dependencies: shebang-regex: 3.0.0 - dev: true /shebang-regex@1.0.0: resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} @@ -4278,7 +4971,6 @@ packages: /shebang-regex@3.0.0: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - dev: true /shimmer@1.2.1: resolution: {integrity: sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==} @@ -4294,10 +4986,19 @@ packages: object-inspect: 1.13.1 dev: true + /siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + dev: false + /signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} dev: true + /signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + dev: false + /simple-update-notifier@2.0.0: resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==} engines: {node: '>=10'} @@ -4329,6 +5030,11 @@ packages: atomic-sleep: 1.0.0 dev: false + /source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + dev: false + /source-map@0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} @@ -4384,6 +5090,10 @@ packages: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} dev: true + /stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + dev: false + /stackframe@1.3.4: resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} dev: true @@ -4392,6 +5102,10 @@ packages: resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==} dev: false + /std-env@3.7.0: + resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} + dev: false + /stream-shift@1.0.3: resolution: {integrity: sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==} dev: false @@ -4459,6 +5173,11 @@ packages: engines: {node: '>=8'} dev: true + /strip-final-newline@3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} + dev: false + /strip-indent@3.0.0: resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} engines: {node: '>=8'} @@ -4470,6 +5189,12 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + /strip-literal@2.1.0: + resolution: {integrity: sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==} + dependencies: + js-tokens: 9.0.0 + dev: false + /supports-color@5.5.0: resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} engines: {node: '>=4'} @@ -4482,7 +5207,6 @@ packages: engines: {node: '>=8'} dependencies: has-flag: 4.0.0 - dev: true /supports-color@8.1.1: resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} @@ -4521,6 +5245,20 @@ packages: real-require: 0.2.0 dev: false + /tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + dev: false + + /tinypool@0.8.4: + resolution: {integrity: sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==} + engines: {node: '>=14.0.0'} + dev: false + + /tinyspy@2.2.1: + resolution: {integrity: sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==} + engines: {node: '>=14.0.0'} + dev: false + /tmp@0.0.33: resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} engines: {node: '>=0.6.0'} @@ -4593,6 +5331,37 @@ packages: yn: 3.1.1 dev: true + /ts-node@10.9.2(@types/node@18.16.3)(typescript@5.3.3): + resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.9 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.3 + '@types/node': 18.16.3 + acorn: 8.12.1 + acorn-walk: 8.2.0 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.3.3 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + dev: false + /tsc-alias@1.8.8: resolution: {integrity: sha512-OYUOd2wl0H858NvABWr/BoSKNERw3N9GTi3rHPK8Iv4O1UyUXIrTTOAZNHsjlVpXFOhpJBVARI1s+rzwLivN3Q==} hasBin: true @@ -4632,6 +5401,11 @@ packages: yargs: 17.7.2 dev: true + /type-detect@4.1.0: + resolution: {integrity: sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==} + engines: {node: '>=4'} + dev: false + /type-fest@0.13.1: resolution: {integrity: sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==} engines: {node: '>=10'} @@ -4702,6 +5476,10 @@ packages: engines: {node: '>=14.17'} hasBin: true + /ufo@1.5.4: + resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==} + dev: false + /unbox-primitive@1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} dependencies: @@ -4748,7 +5526,6 @@ packages: /v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} - dev: true /validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} @@ -4757,6 +5534,30 @@ packages: spdx-expression-parse: 3.0.1 dev: true + /viem@2.21.12(typescript@5.3.3): + resolution: {integrity: sha512-yPZulbPdoDnuB8Gm2m+Qc9Mjgl6gC1YgNU6zcO+C8XZNYwaCNE6mokPiy30m8o5I1XkfvMc35jMmtT1zCb8yxA==} + peerDependencies: + typescript: '>=5.0.4' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@adraffy/ens-normalize': 1.10.0 + '@noble/curves': 1.4.0 + '@noble/hashes': 1.4.0 + '@scure/bip32': 1.4.0 + '@scure/bip39': 1.4.0 + abitype: 1.0.5(typescript@5.3.3) + isows: 1.0.4(ws@8.17.1) + typescript: 5.3.3 + webauthn-p256: 0.0.5 + ws: 8.17.1 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + dev: false + /viem@2.21.12(typescript@5.3.3)(zod@3.22.4): resolution: {integrity: sha512-yPZulbPdoDnuB8Gm2m+Qc9Mjgl6gC1YgNU6zcO+C8XZNYwaCNE6mokPiy30m8o5I1XkfvMc35jMmtT1zCb8yxA==} peerDependencies: @@ -4804,6 +5605,136 @@ packages: - zod dev: false + /vite-node@1.6.0(@types/node@18.16.3): + resolution: {integrity: sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + dependencies: + cac: 6.7.14 + debug: 4.3.6 + pathe: 1.1.2 + picocolors: 1.0.0 + vite: 5.4.7(@types/node@18.16.3) + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + dev: false + + /vite@5.4.7(@types/node@18.16.3): + resolution: {integrity: sha512-5l2zxqMEPVENgvzTuBpHer2awaetimj2BGkhBPdnwKbPNOlHsODU+oiazEZzLK7KhAnOrO+XGYJYn4ZlUhDtDQ==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': 18.16.3 + esbuild: 0.21.5 + postcss: 8.4.47 + rollup: 4.22.4 + optionalDependencies: + fsevents: 2.3.3 + dev: false + + /vitest@1.6.0(@types/node@18.16.3): + resolution: {integrity: sha512-H5r/dN06swuFnzNFhq/dnz37bPXnq8xB2xB5JOVk8K09rUtoeNN+LHWkoQ0A/i3hvbUKKcCei9KpbxqHMLhLLA==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/node': ^18.0.0 || >=20.0.0 + '@vitest/browser': 1.6.0 + '@vitest/ui': 1.6.0 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + dependencies: + '@types/node': 18.16.3 + '@vitest/expect': 1.6.0 + '@vitest/runner': 1.6.0 + '@vitest/snapshot': 1.6.0 + '@vitest/spy': 1.6.0 + '@vitest/utils': 1.6.0 + acorn-walk: 8.3.4 + chai: 4.5.0 + debug: 4.3.6 + execa: 8.0.1 + local-pkg: 0.5.0 + magic-string: 0.30.11 + pathe: 1.1.2 + picocolors: 1.0.0 + std-env: 3.7.0 + strip-literal: 2.1.0 + tinybench: 2.9.0 + tinypool: 0.8.4 + vite: 5.4.7(@types/node@18.16.3) + vite-node: 1.6.0(@types/node@18.16.3) + why-is-node-running: 2.3.0 + transitivePeerDependencies: + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + dev: false + + /wait-port@1.1.0: + resolution: {integrity: sha512-3e04qkoN3LxTMLakdqeWth8nih8usyg+sf1Bgdf9wwUkp05iuK1eSY/QpLvscT/+F/gA89+LpUmmgBtesbqI2Q==} + engines: {node: '>=10'} + hasBin: true + dependencies: + chalk: 4.1.2 + commander: 9.5.0 + debug: 4.3.6 + transitivePeerDependencies: + - supports-color + dev: false + /wcwidth@1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} dependencies: @@ -4814,7 +5745,7 @@ packages: resolution: {integrity: sha512-drMGNWKdaixZNobeORVIqq7k5DsRC9FnG201K2QjeOoQLmtSDaSsVZdkg6n5jUALJKcAG++zBPJXmv6hy0nWFg==} dependencies: '@noble/curves': 1.4.0 - '@noble/hashes': 1.4.0 + '@noble/hashes': 1.5.0 dev: false /webidl-conversions@3.0.1: @@ -4874,7 +5805,15 @@ packages: hasBin: true dependencies: isexe: 2.0.0 - dev: true + + /why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + dev: false /workerpool@6.2.1: resolution: {integrity: sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==} @@ -5026,13 +5965,17 @@ packages: /yn@3.1.1: resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} engines: {node: '>=6'} - dev: true /yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} dev: true + /yocto-queue@1.1.1: + resolution: {integrity: sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==} + engines: {node: '>=12.20'} + dev: false + /zod-validation-error@1.5.0(zod@3.22.4): resolution: {integrity: sha512-/7eFkAI4qV0tcxMBB/3+d2c1P6jzzZYdYSlBuAklzMuCrJu5bzJfHS0yVAS87dRHVlhftd6RFJDIvv03JgkSbw==} engines: {node: '>=16.0.0'} diff --git a/test/e2e/package.json b/test/e2e/package.json index 22fa81d3..78417ce8 100644 --- a/test/e2e/package.json +++ b/test/e2e/package.json @@ -3,13 +3,14 @@ "version": "0.0.0", "private": true, "scripts": { - "test": "vitest dev -c ./vitest.config.ts", + "test": "vitest dev -c ./vitest.config.ts pimlico_sendInstantUserOperation.test.ts", "test:ci": "CI=true && vitest -c ./vitest.config.ts" }, "dependencies": { - "@types/node": "^18.16.3", "@pimlico/alto": "workspace:./src/", - "permissionless": "^0.1.25", + "@types/node": "^18.16.3", + "e2e": "link:", + "permissionless": "^0.2.1", "ts-node": "^10.9.2", "viem": "^2.9.5", "vitest": "^1.6.0", diff --git a/test/e2e/src/testPaymaster.ts b/test/e2e/src/testPaymaster.ts index 38d8cce3..f6935af6 100644 --- a/test/e2e/src/testPaymaster.ts +++ b/test/e2e/src/testPaymaster.ts @@ -2,8 +2,8 @@ // This paymaster will sponsor all UserOperations import { - Address, - Hex, + type Address, + type Hex, concat, createPublicClient, getCreate2Address, @@ -14,7 +14,7 @@ import { import { ANVIL_RPC } from "./constants" import { foundry } from "viem/chains" import { getAnvilWalletClient } from "./utils" -import { ENTRYPOINT_ADDRESS_V06 } from "permissionless" +import { entryPoint06Address } from "viem/account-abstraction" const PAYMASTER_V06_BYTECODE: Hex = "0x60a060405234801561001057600080fd5b50604051610a87380380610a8783398101604081905261002f916100c2565b80328061005657604051631e4fbdf760e01b81526000600482015260240160405180910390fd5b61005f81610072565b506001600160a01b0316608052506100f2565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000602082840312156100d457600080fd5b81516001600160a01b03811681146100eb57600080fd5b9392505050565b60805161094961013e6000396000818161015f01528181610251015281816102e801528181610364015281816103f80152818161046f015281816104fc015261063701526109496000f3fe6080604052600436106100a75760003560e01c8063bb9fe6bf11610064578063bb9fe6bf14610181578063c23a5cea14610196578063c399ec88146101b6578063d0e30db0146101d9578063f2fde38b146101e1578063f465c77e1461020157600080fd5b80630396cb60146100ac578063205c2878146100c1578063715018a6146100e15780638da5cb5b146100f6578063a9a234091461012d578063b0d691fe1461014d575b600080fd5b6100bf6100ba366004610737565b61022f565b005b3480156100cd57600080fd5b506100bf6100dc366004610779565b6102ba565b3480156100ed57600080fd5b506100bf61032c565b34801561010257600080fd5b506000546001600160a01b03165b6040516001600160a01b0390911681526020015b60405180910390f35b34801561013957600080fd5b506100bf6101483660046107a5565b610340565b34801561015957600080fd5b506101107f000000000000000000000000000000000000000000000000000000000000000081565b34801561018d57600080fd5b506100bf61035a565b3480156101a257600080fd5b506100bf6101b1366004610834565b6103d1565b3480156101c257600080fd5b506101cb610457565b604051908152602001610124565b6100bf6104e7565b3480156101ed57600080fd5b506100bf6101fc366004610834565b610549565b34801561020d57600080fd5b5061022161021c366004610851565b61058c565b6040516101249291906108a5565b6102376105af565b604051621cb65b60e51b815263ffffffff821660048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690630396cb609034906024016000604051808303818588803b15801561029e57600080fd5b505af11580156102b2573d6000803e3d6000fd5b505050505050565b6102c26105af565b60405163040b850f60e31b81526001600160a01b038381166004830152602482018390527f0000000000000000000000000000000000000000000000000000000000000000169063205c287890604401600060405180830381600087803b15801561029e57600080fd5b6103346105af565b61033e60006105dc565b565b61034861062c565b6103548484848461069c565b50505050565b6103626105af565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663bb9fe6bf6040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156103bd57600080fd5b505af1158015610354573d6000803e3d6000fd5b6103d96105af565b60405163611d2e7560e11b81526001600160a01b0382811660048301527f0000000000000000000000000000000000000000000000000000000000000000169063c23a5cea90602401600060405180830381600087803b15801561043c57600080fd5b505af1158015610450573d6000803e3d6000fd5b5050505050565b6040516370a0823160e01b81523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa1580156104be573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104e291906108fa565b905090565b60405163b760faf960e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063b760faf99034906024016000604051808303818588803b15801561043c57600080fd5b6105516105af565b6001600160a01b03811661058057604051631e4fbdf760e01b8152600060048201526024015b60405180910390fd5b610589816105dc565b50565b6060600061059861062c565b6105a38585856106d4565b91509150935093915050565b6000546001600160a01b0316331461033e5760405163118cdaa760e01b8152336004820152602401610577565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461033e5760405162461bcd60e51b815260206004820152601560248201527414d95b99195c881b9bdd08115b9d1c9e541bda5b9d605a1b6044820152606401610577565b60405162461bcd60e51b815260206004820152600d60248201526c6d757374206f7665727269646560981b6044820152606401610577565b606060006106e560008060006106ff565b604080516020810190915260008152969095509350505050565b600060d08265ffffffffffff16901b60a08465ffffffffffff16901b8561072757600061072a565b60015b60ff161717949350505050565b60006020828403121561074957600080fd5b813563ffffffff8116811461075d57600080fd5b9392505050565b6001600160a01b038116811461058957600080fd5b6000806040838503121561078c57600080fd5b823561079781610764565b946020939093013593505050565b600080600080606085870312156107bb57600080fd5b8435600381106107ca57600080fd5b9350602085013567ffffffffffffffff808211156107e757600080fd5b818701915087601f8301126107fb57600080fd5b81358181111561080a57600080fd5b88602082850101111561081c57600080fd5b95986020929092019750949560400135945092505050565b60006020828403121561084657600080fd5b813561075d81610764565b60008060006060848603121561086657600080fd5b833567ffffffffffffffff81111561087d57600080fd5b8401610160818703121561089057600080fd5b95602085013595506040909401359392505050565b604081526000835180604084015260005b818110156108d357602081870181015160608684010152016108b6565b506000606082850101526060601f19601f8301168401019150508260208301529392505050565b60006020828403121561090c57600080fd5b505191905056fea264697066735822122053527306dfabc8c0f532c474c7e3966caf999d7e7b30655a671e45f0b4b36e0f64736f6c63430008170033" @@ -32,7 +32,7 @@ export const deployPaymaster = async ( ): Promise<Address> => { let createBytecode = PAYMASTER_V07_BYTECODE - if (entryPoint === ENTRYPOINT_ADDRESS_V06) { + if (entryPoint === entryPoint06Address) { createBytecode = PAYMASTER_V06_BYTECODE } @@ -42,7 +42,7 @@ export const deployPaymaster = async ( bytecode: concat([createBytecode, pad(entryPoint)]) }) - const bytecode = await publicClient.getBytecode({ + const bytecode = await publicClient.getCode({ address: counterFactual }) diff --git a/test/e2e/src/utils.ts b/test/e2e/src/utils.ts index 39d65ccb..62dc8144 100644 --- a/test/e2e/src/utils.ts +++ b/test/e2e/src/utils.ts @@ -1,49 +1,32 @@ import { - Chain, - Hex, - Transport, + type Hex, + type Transport, + type Chain, createPublicClient, createTestClient, createWalletClient, - getCreate2Address, http, - parseEther, - parseGwei + parseEther } from "viem" import { generatePrivateKey, mnemonicToAccount, privateKeyToAccount } from "viem/accounts" +import { ALTO_RPC, ANVIL_RPC } from "./constants" +import { foundry } from "viem/chains" +import { createPimlicoClient } from "permissionless/clients/pimlico" +import { toSimpleSmartAccount } from "permissionless/accounts" import { - ALTO_RPC, - ANVIL_RPC, - SIMPLE_ACCOUNT_FACTORY_V06, - SIMPLE_ACCOUNT_FACTORY_V07 -} from "./constants" -import { anvil, foundry } from "viem/chains" -import { EntryPoint } from "permissionless/types" + type EntryPointVersion, + type SmartAccount, + entryPoint06Address, + entryPoint07Address +} from "viem/account-abstraction" import { - PimlicoBundlerClient, - PimlicoPaymasterClient, - createPimlicoBundlerClient -} from "permissionless/clients/pimlico" -import { - createSmartAccountClient, - SmartAccountClient, - getEntryPointVersion, - BundlerClient, - createBundlerClient + type SmartAccountClient, + createSmartAccountClient } from "permissionless" -import { - SmartAccount, - signerToSimpleSmartAccount -} from "permissionless/accounts" - -const publicClient = createPublicClient({ - transport: http(ANVIL_RPC), - chain: foundry -}) const anvilClient = createTestClient({ transport: http(ANVIL_RPC), @@ -51,28 +34,11 @@ const anvilClient = createTestClient({ mode: "anvil" }) -export type AAParamType<T extends EntryPoint> = { - entryPoint: T - paymasterClient?: PimlicoPaymasterClient<T> +export type AAParamType = { + entryPointVersion: EntryPointVersion privateKey?: Hex } -export const getFactoryAddress = ( - entryPoint: EntryPoint, - accountType: "simple" | "safe" -) => { - switch (accountType) { - case "simple": - return getEntryPointVersion(entryPoint) === "v0.6" - ? SIMPLE_ACCOUNT_FACTORY_V06 - : SIMPLE_ACCOUNT_FACTORY_V07 - case "safe": - break - } - - throw new Error("Parameters not recongized") -} - export const getAnvilWalletClient = (addressIndex: number) => { return createWalletClient({ account: mnemonicToAccount( @@ -86,49 +52,77 @@ export const getAnvilWalletClient = (addressIndex: number) => { }) } -export const getBundlerClient = <T extends EntryPoint>( - entryPoint: T -): BundlerClient<T, Chain> => - createBundlerClient({ +export const getPimlicoClient = <EntryPointVersion extends "0.6" | "0.7">({ + entryPointVersion +}: { + entryPointVersion: EntryPointVersion +}) => + createPimlicoClient({ chain: foundry, - entryPoint, + entryPoint: { + address: (entryPointVersion === "0.6" + ? entryPoint06Address + : entryPoint07Address) as EntryPointVersion extends "0.6" + ? typeof entryPoint06Address + : typeof entryPoint07Address, + version: entryPointVersion + }, transport: http(ALTO_RPC) - }) as BundlerClient<T, Chain> + }) -export const getPimlicoBundlerClient = <T extends EntryPoint>( - entryPoint: T -): PimlicoBundlerClient<T> => - createPimlicoBundlerClient({ +export const getPublicClient = (anvilRpc: string) => { + const transport = http(anvilRpc, { + //onFetchRequest: async (request) => { + // console.log("fetching", await request.json()) + //} + }) + + return createPublicClient({ chain: foundry, - entryPoint, - transport: http(ALTO_RPC) + transport: transport, + pollingInterval: 100 }) +} -export const getSmartAccountClient = async <T extends EntryPoint>({ - entryPoint, +export const getSmartAccountClient = async < + EntryPointVersion extends "0.6" | "0.7" +>({ + entryPointVersion, privateKey = generatePrivateKey() -}: AAParamType<T>): Promise< - SmartAccountClient<T, Transport, Chain, SmartAccount<T>> +}: AAParamType): Promise< + SmartAccountClient<Transport, Chain, SmartAccount> > => { - const smartAccount = await signerToSimpleSmartAccount<T, Transport, Chain>( - publicClient, - { - entryPoint, - signer: privateKeyToAccount(privateKey), - factoryAddress: getFactoryAddress(entryPoint, "simple") - } - ) + const account = await toSimpleSmartAccount<EntryPointVersion>({ + client: getPublicClient(ANVIL_RPC), + entryPoint: { + address: + entryPointVersion === "0.6" + ? entryPoint06Address + : entryPoint07Address, + version: (entryPointVersion === "0.6" + ? "0.6" + : "0.7") as EntryPointVersion + }, + owner: privateKeyToAccount(privateKey) + }) await anvilClient.setBalance({ - address: smartAccount.address, + address: account.address, value: parseEther("100") }) - // @ts-ignore return createSmartAccountClient({ + account, chain: foundry, - account: smartAccount, - bundlerTransport: http(ALTO_RPC) + bundlerTransport: http(ALTO_RPC), + userOperation: { + estimateFeesPerGas: async () => + ( + await getPimlicoClient({ + entryPointVersion + }).getUserOperationGasPrice() + ).fast + } }) } diff --git a/test/e2e/tests/eth_estimateUserOperationGas.test.ts b/test/e2e/tests/eth_estimateUserOperationGas.test.ts index 4cdc1d1c..4fbc0794 100644 --- a/test/e2e/tests/eth_estimateUserOperationGas.test.ts +++ b/test/e2e/tests/eth_estimateUserOperationGas.test.ts @@ -1,153 +1,117 @@ -import { test, describe, expect, beforeAll, beforeEach } from "vitest" -import { - ENTRYPOINT_ADDRESS_V06, - BundlerClient, - ENTRYPOINT_ADDRESS_V07, - EstimateUserOperationGasError -} from "permissionless" -import { - beforeEachCleanUp, - getBundlerClient, - getSmartAccountClient -} from "../src/utils" +import { test, describe, expect, beforeEach } from "vitest" +import { beforeEachCleanUp, getSmartAccountClient } from "../src/utils" +import type { EntryPointVersion } from "viem/account-abstraction" describe.each([ - { entryPoint: ENTRYPOINT_ADDRESS_V06, version: "v0.6" }, - { entryPoint: ENTRYPOINT_ADDRESS_V07, version: "v0.7" } -])("$version supports eth_estimateUserOperationGas", ({ entryPoint }) => { - let bundlerClient: BundlerClient<typeof entryPoint> - - beforeAll(async () => { - bundlerClient = getBundlerClient(entryPoint) - }) - - beforeEach(async () => { - await beforeEachCleanUp() - }) - - test("Can estimate with empty gasLimit values", async () => { - const smartAccountClient = await getSmartAccountClient({ - entryPoint - }) - - let op = await smartAccountClient.prepareUserOperationRequest({ - userOperation: { - callData: await smartAccountClient.account.encodeCallData({ - to: "0x23B608675a2B2fB1890d3ABBd85c5775c51691d5", - data: "0x", - value: 0n - }) - } + { + entryPointVersion: "0.6" as EntryPointVersion + }, + { + entryPointVersion: "0.7" as EntryPointVersion + } +])( + "$entryPointVersion supports eth_estimateUserOperationGas", + ({ entryPointVersion }) => { + beforeEach(async () => { + await beforeEachCleanUp() }) - op = { - ...op, - callGasLimit: 0n, - verificationGasLimit: 0n, - preVerificationGas: 0n - } - const gasParams = await bundlerClient.estimateUserOperationGas({ - // @ts-ignore - userOperation: op - }) + test("Can estimate with empty gasLimit values", async () => { + const smartAccountClient = await getSmartAccountClient({ + entryPointVersion + }) - expect(gasParams.verificationGasLimit).not.toBeNull() - expect(gasParams.preVerificationGas).not.toBeNull() - expect(gasParams.callGasLimit).not.toBeNull() - }) + const gasParams = await smartAccountClient.estimateUserOperationGas( + { + calls: [ + { + to: "0x23B608675a2B2fB1890d3ABBd85c5775c51691d5", + data: "0x", + value: 0n + } + ], + callGasLimit: 0n, + verificationGasLimit: 0n, + preVerificationGas: 0n + } + ) - test("Throws if gasPrices are set to zero", async () => { - const smartAccountClient = await getSmartAccountClient({ - entryPoint + expect(gasParams.verificationGasLimit).not.toBeNull() + expect(gasParams.preVerificationGas).not.toBeNull() + expect(gasParams.callGasLimit).not.toBeNull() }) - let op = await smartAccountClient.prepareUserOperationRequest({ - userOperation: { - callData: await smartAccountClient.account.encodeCallData({ - to: "0x23B608675a2B2fB1890d3ABBd85c5775c51691d5", - data: "0x", - value: 0n + test("Throws if gasPrices are set to zero", async () => { + const smartAccountClient = await getSmartAccountClient({ + entryPointVersion + }) + + await expect(async () => + smartAccountClient.estimateUserOperationGas({ + calls: [ + { + to: "0x23B608675a2B2fB1890d3ABBd85c5775c51691d5", + data: "0x", + value: 0n + } + ], + maxFeePerGas: 0n, + maxPriorityFeePerGas: 0n }) - } + ).rejects.toThrow() }) - op = { - ...op, - maxFeePerGas: 0n, - maxPriorityFeePerGas: 0n - } - await expect(async () => - bundlerClient.estimateUserOperationGas({ - // @ts-ignore - userOperation: op - }) - ).rejects.toThrow(EstimateUserOperationGasError) - }) - - // error occurs when calling contract that doesn't exist or due to low level evm revert. - // both of these scenarios return 0x when calling simulateHandleOp. - test("Gracefully handles cannot decode zero bytes 0x error", async () => { - let op + // error occurs when calling contract that doesn't exist or due to low level evm revert. + // both of these scenarios return 0x when calling simulateHandleOp. + test("Gracefully handles cannot decode zero bytes 0x error", async () => { + if (entryPointVersion === "0.7") { + return + } - if (entryPoint === ENTRYPOINT_ADDRESS_V06) { const smartAccountClient = await getSmartAccountClient({ - entryPoint + entryPointVersion }) - op = await smartAccountClient.prepareUserOperationRequest({ - userOperation: { - callData: await smartAccountClient.account.encodeCallData({ - to: "0x23B608675a2B2fB1890d3ABBd85c5775c51691d5", - data: "0x", - value: 0n - }) - } - }) + try { + await smartAccountClient.estimateUserOperationGas({ + calls: [ + { + to: "0x23B608675a2B2fB1890d3ABBd85c5775c51691d5", + data: "0x", + value: 0n + } + ], + initCode: "0x01" /* this causes the 0x */ + }) + } catch (e: any) { + expect(e.details).toBe( + "AA23 reverted: UserOperation called non-existant contract, or reverted with 0x" + ) + } + }) - // @ts-ignore - op = { - ...op, - initCode: "0x01" /* this causes the 0x */ + test("Empty paymaster data results in zero paymaster limits", async () => { + if (entryPointVersion === "0.6") { + return } - } else { - return - } - try { - await bundlerClient.estimateUserOperationGas({ - userOperation: op + const smartAccountClient = await getSmartAccountClient({ + entryPointVersion }) - } catch (e: any) { - console.log(e) - expect(e).toBeInstanceOf(EstimateUserOperationGasError) - expect(e.details).toBe( - "AA23 reverted: UserOperation called non-existant contract, or reverted with 0x" - ) - } - }) - - test("Empty paymaster data results in zero paymaster limits", async () => { - if (entryPoint === ENTRYPOINT_ADDRESS_V06) return; - - const smartAccountClient = await getSmartAccountClient({ - entryPoint - }) - const op = await smartAccountClient.prepareUserOperationRequest({ - userOperation: { - callData: await smartAccountClient.account.encodeCallData({ - to: "0x23B608675a2B2fB1890d3ABBd85c5775c51691d5", - data: "0x", - value: 0n + const estimation = + await smartAccountClient.estimateUserOperationGas({ + calls: [ + { + to: "0x23B608675a2B2fB1890d3ABBd85c5775c51691d5", + data: "0x", + value: 0n + } + ] }) - } - }) - const estimation = await bundlerClient.estimateUserOperationGas({ - userOperation: op + expect(estimation.paymasterPostOpGasLimit).toBe(0n) + expect(estimation.paymasterVerificationGasLimit).toBe(0n) }) - - expect(estimation.paymasterPostOpGasLimit).toBe(0n) - expect(estimation.paymasterVerificationGasLimit).toBe(0n) - }) -}) + } +) diff --git a/test/e2e/tests/eth_getUserOperationByHash.test.ts b/test/e2e/tests/eth_getUserOperationByHash.test.ts index 188df913..d0740c17 100644 --- a/test/e2e/tests/eth_getUserOperationByHash.test.ts +++ b/test/e2e/tests/eth_getUserOperationByHash.test.ts @@ -1,13 +1,19 @@ -import { test, describe, expect, beforeAll, beforeEach } from "vitest" -import { ENTRYPOINT_ADDRESS_V06, BundlerClient } from "permissionless" +import { test, describe, expect, beforeEach } from "vitest" import { beforeEachCleanUp, - getBundlerClient, + getPimlicoClient, getSmartAccountClient } from "../src/utils" -import { Hex, createTestClient, http } from "viem" +import { createTestClient, http } from "viem" import { foundry } from "viem/chains" import { ANVIL_RPC } from "../src/constants" +import { + type EntryPointVersion, + entryPoint06Address, + entryPoint07Address, + UserOperationNotFoundError, + UserOperationReceiptNotFoundError +} from "viem/account-abstraction" const anvilClient = createTestClient({ chain: foundry, @@ -16,93 +22,90 @@ const anvilClient = createTestClient({ }) describe.each([ - { entryPoint: ENTRYPOINT_ADDRESS_V06, version: "v0.6" } - //{ entryPoint: ENTRYPOINT_ADDRESS_V07, version: "v0.7" } -])("$version supports eth_getUserOperationByHash", ({ entryPoint }) => { - let bundlerClient: BundlerClient<typeof entryPoint> - - beforeAll(async () => { - bundlerClient = getBundlerClient(entryPoint) - }) - - beforeEach(async () => { - await beforeEachCleanUp() - }) - - test("Return null if hash not found", async () => { - const hash = - "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - const response = await bundlerClient.getUserOperationByHash({ hash }) - - expect(response).toBeNull() - }) - - test("Pending UserOperation should return null", async () => { - const smartAccountClient = await getSmartAccountClient({ - entryPoint - }) - const smartAccount = smartAccountClient.account - - await anvilClient.setAutomine(false) - await anvilClient.mine({ blocks: 1 }) - - const op = await smartAccountClient.prepareUserOperationRequest({ - userOperation: { - callData: await smartAccountClient.account.encodeCallData({ - to: "0x23B608675a2B2fB1890d3ABBd85c5775c51691d5", - data: "0x", - value: 0n - }) - } + { + entryPoint: entryPoint06Address, + entryPointVersion: "0.6" as EntryPointVersion + }, + { + entryPoint: entryPoint07Address, + entryPointVersion: "0.7" as EntryPointVersion + } +])( + "$entryPointVersion supports eth_getUserOperationByHash", + ({ entryPoint, entryPointVersion }) => { + beforeEach(async () => { + await beforeEachCleanUp() }) - op.signature = await smartAccount.signUserOperation(op) - const hash = await bundlerClient.sendUserOperation({ - userOperation: op - }) + test("Return null if hash not found", async () => { + const bundlerClient = getPimlicoClient({ + entryPointVersion + }) - await new Promise((resolve) => setTimeout(resolve, 1500)) + const hash = + "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - // check that no tx was mined - let opReceipt = await bundlerClient.getUserOperationByHash({ - hash + await expect(async () => { + await bundlerClient.getUserOperation({ + hash + }) + }).rejects.toThrow(UserOperationNotFoundError) }) - expect(opReceipt).toBeNull() - - await anvilClient.setAutomine(true) - }) - test("Return userOperation, entryPoint, blockNum, blockHash, txHash for mined tx", async () => { - const smartAccountClient = await getSmartAccountClient({ - entryPoint - }) - const smartAccount = smartAccountClient.account - - let op = await smartAccountClient.prepareUserOperationRequest({ - userOperation: { - callData: await smartAccountClient.account.encodeCallData({ - to: "0x23B608675a2B2fB1890d3ABBd85c5775c51691d5", - data: "0x", - value: 0n + test("Pending UserOperation should return null", async () => { + const smartAccountClient = await getSmartAccountClient({ + entryPointVersion + }) + + await anvilClient.setAutomine(false) + await anvilClient.mine({ blocks: 1 }) + + const hash = await smartAccountClient.sendUserOperation({ + calls: [ + { + to: "0x23B608675a2B2fB1890d3ABBd85c5775c51691d5", + data: "0x", + value: 0n + } + ] + }) + + await new Promise((resolve) => setTimeout(resolve, 1500)) + + await expect(async () => { + await smartAccountClient.getUserOperationReceipt({ + hash }) - } - }) - op.signature = await smartAccount.signUserOperation(op) + }).rejects.toThrow(UserOperationReceiptNotFoundError) - const hash = await bundlerClient.sendUserOperation({ - userOperation: op + await anvilClient.setAutomine(true) }) - await new Promise((resolve) => setTimeout(resolve, 1500)) - - const response = await bundlerClient.getUserOperationByHash({ hash }) - - expect(response).not.toBeNull() - expect(response?.entryPoint).toBe(entryPoint) - expect(response?.blockHash).not.toBeUndefined() - expect(response?.transactionHash).not.toBeUndefined() - - op.initCode = op.initCode.toLowerCase() as Hex - expect(response?.userOperation).toEqual(op) - }) -}) + test("Return userOperation, entryPoint, blockNum, blockHash, txHash for mined tx", async () => { + const smartAccountClient = await getSmartAccountClient({ + entryPointVersion + }) + + const hash = await smartAccountClient.sendUserOperation({ + calls: [ + { + to: "0x23B608675a2B2fB1890d3ABBd85c5775c51691d5", + data: "0x", + value: 0n + } + ] + }) + + await new Promise((resolve) => setTimeout(resolve, 1500)) + + const response = await smartAccountClient.getUserOperation({ + hash + }) + + expect(response).not.toBeNull() + expect(response?.entryPoint).toBe(entryPoint) + expect(response?.blockHash).not.toBeUndefined() + expect(response?.transactionHash).not.toBeUndefined() + }) + } +) diff --git a/test/e2e/tests/eth_getUserOperationReceipt.test.ts b/test/e2e/tests/eth_getUserOperationReceipt.test.ts index fa0f4d06..76bc775f 100644 --- a/test/e2e/tests/eth_getUserOperationReceipt.test.ts +++ b/test/e2e/tests/eth_getUserOperationReceipt.test.ts @@ -1,110 +1,113 @@ import { test, describe, expect, beforeAll, beforeEach } from "vitest" -import { - ENTRYPOINT_ADDRESS_V06, - BundlerClient, - ENTRYPOINT_ADDRESS_V07 -} from "permissionless" -import { - beforeEachCleanUp, - getBundlerClient, - getSmartAccountClient -} from "../src/utils" -import { Address, Hex } from "viem" +import { beforeEachCleanUp, getSmartAccountClient } from "../src/utils" +import type { Address, Hex } from "viem" import { deployRevertingContract, decodeRevert, getRevertCall } from "../src/revertingContract" import { deployPaymaster } from "../src/testPaymaster" +import { + type EntryPointVersion, + entryPoint06Address, + entryPoint07Address +} from "viem/account-abstraction" describe.each([ - { entryPoint: ENTRYPOINT_ADDRESS_V06, version: "v0.6" }, - { entryPoint: ENTRYPOINT_ADDRESS_V07, version: "v0.7" } -])("$version supports eth_getUserOperationReceipt", ({ entryPoint }) => { - let bundlerClient: BundlerClient<typeof entryPoint> - let revertingContract: Address - let paymaster: Address - - beforeAll(async () => { - revertingContract = await deployRevertingContract() - paymaster = await deployPaymaster(entryPoint) - bundlerClient = getBundlerClient(entryPoint) - }) + { + entryPoint: entryPoint06Address, + entryPointVersion: "0.6" as EntryPointVersion + }, + { + entryPoint: entryPoint07Address, + entryPointVersion: "0.7" as EntryPointVersion + } +])( + "$entryPointVersion supports eth_getUserOperationReceipt", + ({ entryPoint, entryPointVersion }) => { + let revertingContract: Address + let paymaster: Address - beforeEach(async () => { - await beforeEachCleanUp() - }) - - test("Returns revert bytes when UserOperation reverts", async () => { - const smartAccountClient = await getSmartAccountClient({ - entryPoint + beforeAll(async () => { + revertingContract = await deployRevertingContract() + paymaster = await deployPaymaster(entryPoint) }) - const smartAccount = smartAccountClient.account - let op = await smartAccountClient.prepareUserOperationRequest({ - userOperation: { - callData: "0x" - } + beforeEach(async () => { + await beforeEachCleanUp() }) - op = { - ...op, - callData: await smartAccountClient.account.encodeCallData({ - to: revertingContract, - data: getRevertCall("foobar"), - value: 0n - }), - callGasLimit: 500_000n - } - op.signature = await smartAccount.signUserOperation(op) - const hash = await bundlerClient.sendUserOperation({ - userOperation: op - }) + test("Returns revert bytes when UserOperation reverts", async () => { + const smartAccountClient = await getSmartAccountClient({ + entryPointVersion + }) - await new Promise((resolve) => setTimeout(resolve, 1500)) + const hash = await smartAccountClient.sendUserOperation({ + calls: [ + { + to: revertingContract, + data: getRevertCall("foobar"), + value: 0n + } + ], + callGasLimit: 500_000n, + verificationGasLimit: 500_000n, + preVerificationGas: 500_000n + }) - const receipt = await bundlerClient.getUserOperationReceipt({ hash }) + await new Promise((resolve) => setTimeout(resolve, 1500)) - expect(receipt).not.toBeNull() - expect(receipt?.success).toEqual(false) - expect(decodeRevert(receipt?.reason as Hex)).toEqual("foobar") - }) + const receipt = await smartAccountClient.getUserOperationReceipt({ + hash + }) - test("Returns paymaster when one is used", async () => { - const smartAccountClient = await getSmartAccountClient({ - entryPoint + expect(receipt).not.toBeNull() + expect(receipt?.success).toEqual(false) + expect(decodeRevert(receipt?.reason as Hex)).toEqual("foobar") }) - const smartAccount = smartAccountClient.account - let op = await smartAccountClient.prepareUserOperationRequest({ - userOperation: { - callData: await smartAccountClient.account.encodeCallData({ - to: "0x23B608675a2B2fB1890d3ABBd85c5775c51691d5", - data: "0x", - value: 0n + test("Returns paymaster when one is used", async () => { + const smartAccountClient = await getSmartAccountClient({ + entryPointVersion + }) + + let hash: Hex + if (entryPointVersion === "0.6") { + hash = await smartAccountClient.sendUserOperation({ + calls: [ + { + to: "0x23B608675a2B2fB1890d3ABBd85c5775c51691d5", + data: "0x", + value: 0n + } + ], + paymasterAndData: paymaster + }) + } else { + hash = await smartAccountClient.sendUserOperation({ + calls: [ + { + to: "0x23B608675a2B2fB1890d3ABBd85c5775c51691d5", + data: "0x", + value: 0n + } + ], + paymaster: paymaster, + paymasterVerificationGasLimit: 1_500_000n, + paymasterPostOpGasLimit: 500_000n }) } - }) - if (entryPoint === ENTRYPOINT_ADDRESS_V06) { - op.paymasterAndData = paymaster - } else { - op.paymaster = paymaster - op.paymasterVerificationGasLimit = 1_500_000n - op.paymasterPostOpGasLimit = 500_000n - } - op.signature = await smartAccount.signUserOperation(op) - const hash = await bundlerClient.sendUserOperation({ - userOperation: op - }) - - await new Promise((resolve) => setTimeout(resolve, 1500)) + await new Promise((resolve) => setTimeout(resolve, 1500)) - const receipt = await bundlerClient.getUserOperationReceipt({ hash }) + const receipt = await smartAccountClient.getUserOperationReceipt({ + hash + }) - expect(receipt).not.toBeNull() - expect(receipt?.success).toEqual(true) - expect(receipt?.reason).toBeUndefined() - expect(receipt?.paymaster).toEqual(paymaster) - }) -}) + expect(receipt).not.toBeNull() + expect(receipt?.success).toEqual(true) + expect(receipt?.reason).toBeUndefined() + expect(receipt?.paymaster).toEqual(paymaster) + }) + } +) diff --git a/test/e2e/tests/eth_sendUserOperation.test.ts b/test/e2e/tests/eth_sendUserOperation.test.ts index fe7957e0..2637acd5 100644 --- a/test/e2e/tests/eth_sendUserOperation.test.ts +++ b/test/e2e/tests/eth_sendUserOperation.test.ts @@ -1,18 +1,13 @@ -import { test, describe, expect, beforeAll, beforeEach } from "vitest" -import { - ENTRYPOINT_ADDRESS_V06, - BundlerClient, - ENTRYPOINT_ADDRESS_V07 -} from "permissionless" +import { test, describe, expect, beforeEach } from "vitest" import { beforeEachCleanUp, - getBundlerClient, getSmartAccountClient, sendBundleNow, setBundlingMode } from "../src/utils" import { foundry } from "viem/chains" import { + type Hex, createPublicClient, createTestClient, getContract, @@ -24,6 +19,14 @@ import { ANVIL_RPC } from "../src/constants" import { ENTRYPOINT_V06_ABI, ENTRYPOINT_V07_ABI } from "./utils/abi" import { getNonceKeyAndValue } from "./utils/userop" import { generatePrivateKey } from "viem/accounts" +import { + type EntryPointVersion, + entryPoint06Address, + entryPoint07Address, + UserOperationNotFoundError, + UserOperationReceiptNotFoundError +} from "viem/account-abstraction" +import { encodeNonce } from "permissionless/utils" const anvilClient = createTestClient({ chain: foundry, @@ -37,371 +40,349 @@ const publicClient = createPublicClient({ }) describe.each([ - { entryPoint: ENTRYPOINT_ADDRESS_V06, version: "v0.6" }, - { entryPoint: ENTRYPOINT_ADDRESS_V07, version: "v0.7" } -])("$version supports eth_sendUserOperation", ({ entryPoint, version }) => { - let bundlerClient: BundlerClient<typeof entryPoint> - - beforeAll(async () => { - bundlerClient = getBundlerClient(entryPoint) - }) - - beforeEach(async () => { - await beforeEachCleanUp() - }) - - test("Send UserOperation", async () => { - const smartAccountClient = await getSmartAccountClient({ - entryPoint - }) - const smartAccount = smartAccountClient.account - - const to = "0x23B608675a2B2fB1890d3ABBd85c5775c51691d5" - const value = parseEther("0.15") - - const op = await smartAccountClient.prepareUserOperationRequest({ - userOperation: { - callData: await smartAccount.encodeCallData({ - to, - value, - data: "0x" - }) - } - }) - op.signature = await smartAccount.signUserOperation(op) - - const hash = await bundlerClient.sendUserOperation({ - userOperation: op - }) - - await new Promise((resolve) => setTimeout(resolve, 1500)) - - await bundlerClient.waitForUserOperationReceipt({ hash }) - - expect( - await publicClient.getBalance({ address: to }) - ).toBeGreaterThanOrEqual(value) - }) - - test("Replace mempool transaction", async () => { - const smartAccountClient = await getSmartAccountClient({ - entryPoint + { + entryPoint: entryPoint06Address, + entryPointVersion: "0.6" as EntryPointVersion + }, + { + entryPoint: entryPoint07Address, + entryPointVersion: "0.7" as EntryPointVersion + } +])( + "$entryPointVersion supports eth_sendUserOperation", + ({ entryPoint, entryPointVersion }) => { + beforeEach(async () => { + await beforeEachCleanUp() }) - const smartAccount = smartAccountClient.account - await anvilClient.setAutomine(false) - await anvilClient.mine({ blocks: 1 }) + test("Send UserOperation", async () => { + const smartAccountClient = await getSmartAccountClient({ + entryPointVersion + }) - const to = "0x23B608675a2B2fB1890d3ABBd85c5775c51691d5" - const value = parseEther("0.15") + const to = "0x23B608675a2B2fB1890d3ABBd85c5775c51691d5" + const value = parseEther("0.15") - const op = await smartAccountClient.prepareUserOperationRequest({ - userOperation: { - callData: await smartAccount.encodeCallData({ - to, - value, - data: "0x" - }) - } - }) - op.signature = await smartAccount.signUserOperation(op) + const hash = await smartAccountClient.sendUserOperation({ + calls: [ + { + to, + value, + data: "0x" + } + ] + }) - const hash = await bundlerClient.sendUserOperation({ - userOperation: op - }) + await new Promise((resolve) => setTimeout(resolve, 1500)) - await new Promise((resolve) => setTimeout(resolve, 1500)) + await smartAccountClient.waitForUserOperationReceipt({ hash }) - // increase next block base fee whilst current tx is in mempool - await anvilClient.setNextBlockBaseFeePerGas({ - baseFeePerGas: parseGwei("150") + expect( + await publicClient.getBalance({ address: to }) + ).toBeGreaterThanOrEqual(value) }) - await new Promise((resolve) => setTimeout(resolve, 2500)) - // check that no tx was mined - let opReceipt = await bundlerClient.getUserOperationReceipt({ - hash - }) - expect(opReceipt).toBeNull() + test("Replace mempool transaction", async () => { + const smartAccountClient = await getSmartAccountClient({ + entryPointVersion + }) - // new block should trigger alto's mempool to replace the eoa tx with too low gasPrice - await anvilClient.mine({ blocks: 1 }) - await new Promise((resolve) => setTimeout(resolve, 5000)) - await anvilClient.mine({ blocks: 1 }) + await anvilClient.setAutomine(false) + await anvilClient.mine({ blocks: 1 }) - opReceipt = await bundlerClient.getUserOperationReceipt({ - hash - }) + const to = "0x23B608675a2B2fB1890d3ABBd85c5775c51691d5" + const value = parseEther("0.15") - expect(opReceipt?.success).equal(true) - expect( - await publicClient.getBalance({ address: to }) - ).toBeGreaterThanOrEqual(value) - }) + const hash = await smartAccountClient.sendUserOperation({ + calls: [ + { + to, + value, + data: "0x" + } + ] + }) - test("Send multiple UserOperations", async () => { - const firstClient = await getSmartAccountClient({ - entryPoint - }) - const secondClient = await getSmartAccountClient({ - entryPoint - }) + await new Promise((resolve) => setTimeout(resolve, 1500)) - const to = "0x23B608675a2B2fB1890d3ABBd85c5775c51691d5" - const value = parseEther("0.15") + // increase next block base fee whilst current tx is in mempool + await anvilClient.setNextBlockBaseFeePerGas({ + baseFeePerGas: parseGwei("150") + }) + await new Promise((resolve) => setTimeout(resolve, 2500)) - // create sender op - const firstOp = await firstClient.prepareUserOperationRequest({ - userOperation: { - callData: await firstClient.account.encodeCallData({ - to, - value: value, - data: "0x" + // check that no tx was mined + await expect(async () => { + await smartAccountClient.getUserOperationReceipt({ + hash }) - } - }) + }).rejects.toThrow(UserOperationReceiptNotFoundError) - firstOp.signature = await firstClient.account.signUserOperation(firstOp) + // new block should trigger alto's mempool to replace the eoa tx with too low gasPrice + await anvilClient.mine({ blocks: 1 }) + await new Promise((resolve) => setTimeout(resolve, 5000)) + await anvilClient.mine({ blocks: 1 }) - // create relayer op - const secondOp = await secondClient.prepareUserOperationRequest({ - userOperation: { - callData: await secondClient.account.encodeCallData({ - to, - value, - data: "0x" - }) - } + const opReceipt = await smartAccountClient.getUserOperationReceipt({ + hash + }) + + expect(opReceipt?.success).equal(true) + expect( + await publicClient.getBalance({ address: to }) + ).toBeGreaterThanOrEqual(value) }) - secondOp.signature = - await secondClient.account.signUserOperation(secondOp) + test("Send multiple UserOperations", async () => { + const firstClient = await getSmartAccountClient({ + entryPointVersion + }) + const secondClient = await getSmartAccountClient({ + entryPointVersion + }) - await setBundlingMode("manual") + const to = "0x23B608675a2B2fB1890d3ABBd85c5775c51691d5" + const value = parseEther("0.15") - const firstHash = await bundlerClient.sendUserOperation({ - userOperation: firstOp - }) - const secondHash = await bundlerClient.sendUserOperation({ - userOperation: secondOp - }) + await setBundlingMode("manual") - expect( - await bundlerClient.getUserOperationReceipt({ - hash: firstHash + const firstHash = await firstClient.sendUserOperation({ + calls: [ + { + to, + value, + data: "0x" + } + ] }) - ).toBeNull() - expect( - await bundlerClient.getUserOperationReceipt({ - hash: secondHash + const secondHash = await secondClient.sendUserOperation({ + calls: [ + { + to, + value, + data: "0x" + } + ] }) - ).toBeNull() - - await sendBundleNow() - expect( - ( - await bundlerClient.waitForUserOperationReceipt({ + await expect(async () => { + await firstClient.getUserOperationReceipt({ hash: firstHash }) - ).success - ).toEqual(true) - expect( - ( - await bundlerClient.waitForUserOperationReceipt({ + }).rejects.toThrow(UserOperationReceiptNotFoundError) + await expect(async () => { + await secondClient.getUserOperationReceipt({ hash: secondHash }) - ).success - ).toEqual(true) + }).rejects.toThrow(UserOperationReceiptNotFoundError) - expect( - await publicClient.getBalance({ address: to }) - ).toBeGreaterThanOrEqual(value * 2n) - }) + await sendBundleNow() - test('Send parallel UserOperations', async () => { - const nonceKeys = [10n, 12n, 4n, 1n] - - await setBundlingMode("manual") + expect( + ( + await firstClient.waitForUserOperationReceipt({ + hash: firstHash + }) + ).success + ).toEqual(true) + expect( + ( + await secondClient.waitForUserOperationReceipt({ + hash: secondHash + }) + ).success + ).toEqual(true) - const entryPointContract = getContract({ - address: entryPoint, - abi: version === 'v0.6' ? ENTRYPOINT_V06_ABI : ENTRYPOINT_V07_ABI, - client: { - public: publicClient - } + expect( + await publicClient.getBalance({ address: to }) + ).toBeGreaterThanOrEqual(value * 2n) }) - const privateKey = generatePrivateKey(); - - // Needs to deploy user op first - const client = await getSmartAccountClient({ - entryPoint, - privateKey - }) + test("Send parallel UserOperations", async () => { + const nonceKeys = [10n, 12n, 4n, 1n] - await client.sendUserOperation({ - userOperation: { - callData: await client.account.encodeCallData({ - to: client.account.address, - value: parseEther("0.01"), - data: "0x" - }) - } - }) + await setBundlingMode("manual") - await sendBundleNow() + const privateKey = generatePrivateKey() - const buildUserOperation = async (nonceKey: bigint) => { - const to = "0x23B608675a2B2fB1890d3ABBd85c5775c51691d5" - const value = parseEther("0.15") + const entryPointContract = getContract({ + address: entryPoint, + abi: + entryPointVersion === "0.6" + ? ENTRYPOINT_V06_ABI + : ENTRYPOINT_V07_ABI, + client: { + public: publicClient + } + }) - const client = await getSmartAccountClient({ - entryPoint, + // Needs to deploy user op first + const smartAccountClient = await getSmartAccountClient({ + entryPointVersion, privateKey }) - const nonce = await entryPointContract.read.getNonce([ - client.account.address, - nonceKey - ]) - - const userOp = await client.prepareUserOperationRequest({ - userOperation: { - nonce: nonce as bigint, - callData: await client.account.encodeCallData({ - to, - value, + await smartAccountClient.sendUserOperation({ + calls: [ + { + to: smartAccountClient.account.address, + value: parseEther("0.01"), data: "0x" - }) - } + } + ] }) - userOp.signature = await client.account.signUserOperation(userOp) - - return userOp; - } - - const ops = await Promise.all(nonceKeys.map(buildUserOperation)) - - const opHashes = await Promise.all(ops.map(op => bundlerClient.sendUserOperation({ userOperation: op }))); - - await sendBundleNow() - - const receipts = await Promise.all(opHashes.map(async hash => { - const receipt = await bundlerClient.waitForUserOperationReceipt({ hash }); - - return receipt; - })) - - expect( - receipts.every((receipt) => receipt.success) - ).toEqual(true) - - expect( - receipts.every((receipt) => receipt.receipt.transactionHash === receipts[0].receipt.transactionHash) - ).toEqual(true) + await sendBundleNow() + + const opHashes = await Promise.all( + nonceKeys.map((nonceKey) => + smartAccountClient.sendUserOperation({ + calls: [ + { + to: "0x23B608675a2B2fB1890d3ABBd85c5775c51691d5", + value: parseEther("0.15"), + data: "0x" + } + ], + nonce: encodeNonce({ + key: nonceKey, + sequence: 0n + }) + }) + ) + ) - // user ops whould be ordered by the nonce key - const logs = await entryPointContract.getEvents.UserOperationEvent() + await sendBundleNow() - // @ts-ignore - const bundleNonceKeys = logs.map((log) => getNonceKeyAndValue(log.args.nonce)[0]) + const receipts = await Promise.all( + opHashes.map(async (hash) => { + const receipt = + await smartAccountClient.waitForUserOperationReceipt({ + hash + }) - const sortedNonceKeys = [...nonceKeys].sort((a, b) => Number(a) - Number(b)) + return receipt + }) + ) - expect( - bundleNonceKeys - ).toEqual(sortedNonceKeys) - }) + expect(receipts.every((receipt) => receipt.success)).toEqual(true) - test('Send queued UserOperations', async () => { - // Doesn't work with v0.6 userops - if (version === 'v0.6') return; + expect( + receipts.every( + (receipt) => + receipt.receipt.transactionHash === + receipts[0].receipt.transactionHash + ) + ).toEqual(true) - await setBundlingMode("manual") + // user ops whould be ordered by the nonce key + const logs = await entryPointContract.getEvents.UserOperationEvent() - const entryPointContract = getContract({ - address: entryPoint, - abi: version === 'v0.6' ? ENTRYPOINT_V06_ABI : ENTRYPOINT_V07_ABI, - client: { - public: publicClient - } - }) + // @ts-ignore + const bundleNonceKeys = logs.map( + // @ts-ignore + (log) => getNonceKeyAndValue(log.args.nonce)[0] + ) - const privateKey = generatePrivateKey(); + const sortedNonceKeys = [...nonceKeys].sort( + (a, b) => Number(a) - Number(b) + ) - // Needs to deploy user op first - const client = await getSmartAccountClient({ - entryPoint, - privateKey + expect(bundleNonceKeys).toEqual(sortedNonceKeys) }) - await client.sendUserOperation({ - userOperation: { - callData: await client.account.encodeCallData({ - to: client.account.address, - value: parseEther("0.01"), - data: "0x" - }) + test("Send queued UserOperations", async () => { + // Doesn't work with v0.6 userops + if (entryPointVersion === "0.6") { + return } - }) - await sendBundleNow() + await setBundlingMode("manual") + + const entryPointContract = getContract({ + address: entryPoint, + abi: + // @ts-ignore + entryPointVersion === "0.6" + ? ENTRYPOINT_V06_ABI + : ENTRYPOINT_V07_ABI, + client: { + public: publicClient + } + }) - const nonceKey = 100n; - const nonceValueDiffs = [0n, 1n, 2n]; + const privateKey = generatePrivateKey() - // Send 3 sequential user ops - const sendUserOperation = async (nonceValueDiff: bigint) => { - const to = "0x23B608675a2B2fB1890d3ABBd85c5775c51691d5" - const value = parseEther("0.15") - - const client = await getSmartAccountClient({ - entryPoint, + // Needs to deploy user op first + const smartAccountClient = await getSmartAccountClient({ + entryPointVersion, privateKey }) - const nonce = await entryPointContract.read.getNonce([ - client.account.address, - nonceKey - ]) as bigint; - - const userOp = await client.prepareUserOperationRequest({ - userOperation: { - nonce: nonce + nonceValueDiff, - callData: await client.account.encodeCallData({ - to, - value, + await smartAccountClient.sendUserOperation({ + calls: [ + { + to: smartAccountClient.account.address, + value: parseEther("0.01"), data: "0x" - }) - } + } + ] }) - userOp.signature = await client.account.signUserOperation(userOp) + await sendBundleNow() + + const nonceKey = 100n + const nonceValueDiffs = [0n, 1n, 2n] + + // Send 3 sequential user ops + const sendUserOperation = async (nonceValueDiff: bigint) => { + const to = "0x23B608675a2B2fB1890d3ABBd85c5775c51691d5" + const value = parseEther("0.15") + + const nonce = (await entryPointContract.read.getNonce([ + smartAccountClient.account.address, + nonceKey + ])) as bigint + + return smartAccountClient.sendUserOperation({ + calls: [ + { + to, + value, + data: "0x" + } + ], + nonce: nonce + nonceValueDiff + }) + } + const opHashes: Hex[] = [] - return bundlerClient.sendUserOperation({ userOperation: userOp }); - } - const opHashes: Hex[] = []; + for (const nonceValueDiff of nonceValueDiffs) { + opHashes.push(await sendUserOperation(nonceValueDiff)) + } - for (const nonceValueDiff of nonceValueDiffs) { - opHashes.push(await sendUserOperation(nonceValueDiff)) - } + await sendBundleNow() - await sendBundleNow() + const receipts = await Promise.all( + opHashes.map(async (hash) => { + const receipt = + await smartAccountClient.waitForUserOperationReceipt({ + hash + }) - const receipts = await Promise.all(opHashes.map(async (hash) => { - const receipt = await bundlerClient.waitForUserOperationReceipt({ hash }); + return receipt + }) + ) - return receipt; - })) + expect(receipts.every((receipt) => receipt.success)).toEqual(true) - expect( - receipts.every((receipt) => receipt.success) - ).toEqual(true) - - expect( - receipts.every((receipt) => receipt.receipt.transactionHash === receipts[0].receipt.transactionHash) - ).toEqual(true) - }) -}) + expect( + receipts.every( + (receipt) => + receipt.receipt.transactionHash === + receipts[0].receipt.transactionHash + ) + ).toEqual(true) + }) + } +) diff --git a/test/e2e/tests/pimlico_getUserOperationGasPrice.test.ts b/test/e2e/tests/pimlico_getUserOperationGasPrice.test.ts index 1a563721..cfce9b39 100644 --- a/test/e2e/tests/pimlico_getUserOperationGasPrice.test.ts +++ b/test/e2e/tests/pimlico_getUserOperationGasPrice.test.ts @@ -1,10 +1,10 @@ import { test, describe, expect, beforeAll, beforeEach } from "vitest" -import { ENTRYPOINT_ADDRESS_V06, ENTRYPOINT_ADDRESS_V07 } from "permissionless" -import { beforeEachCleanUp, getPimlicoBundlerClient } from "../src/utils" +import { beforeEachCleanUp, getPimlicoClient } from "../src/utils" import { foundry } from "viem/chains" import { createPublicClient, http } from "viem" import { ANVIL_RPC } from "../src/constants" -import { PimlicoBundlerClient } from "permissionless/clients/pimlico" +import type { EntryPointVersion } from "viem/account-abstraction" +import type { PimlicoClient } from "permissionless/clients/pimlico" const publicClient = createPublicClient({ transport: http(ANVIL_RPC), @@ -12,59 +12,63 @@ const publicClient = createPublicClient({ }) describe.each([ - { entryPoint: ENTRYPOINT_ADDRESS_V06, version: "v0.6" }, - { entryPoint: ENTRYPOINT_ADDRESS_V07, version: "v0.7" } -])("$version supports eth_sendUserOperation", ({ entryPoint }) => { - let pimlicoBundlerClient: PimlicoBundlerClient<typeof entryPoint> + { entryPointVersion: "0.6" as EntryPointVersion }, + { entryPointVersion: "0.7" as EntryPointVersion } +])( + "$entryPointVersion supports eth_sendUserOperation", + ({ entryPointVersion }) => { + let pimlicoBundlerClient: PimlicoClient - beforeAll(async () => { - pimlicoBundlerClient = getPimlicoBundlerClient(entryPoint) - }) + beforeAll(() => { + pimlicoBundlerClient = getPimlicoClient({ entryPointVersion }) + }) - beforeEach(async () => { - await beforeEachCleanUp() - }) + beforeEach(async () => { + await beforeEachCleanUp() + }) - test("Get gasPrice", async () => { - const networkPrices = await publicClient.estimateFeesPerGas() - const gasPrice = await pimlicoBundlerClient.getUserOperationGasPrice() + test("Get gasPrice", async () => { + const networkPrices = await publicClient.estimateFeesPerGas() + const gasPrice = + await pimlicoBundlerClient.getUserOperationGasPrice() - const slow = gasPrice.slow - const standard = gasPrice.standard - const fast = gasPrice.fast + const slow = gasPrice.slow + const standard = gasPrice.standard + const fast = gasPrice.fast - expect(slow.maxFeePerGas).toBeGreaterThan(0) - expect(slow.maxPriorityFeePerGas).toBeGreaterThan(0) - expect(standard.maxFeePerGas).toBeGreaterThan(0) - expect(standard.maxPriorityFeePerGas).toBeGreaterThan(0) - expect(fast.maxFeePerGas).toBeGreaterThan(0) - expect(fast.maxPriorityFeePerGas).toBeGreaterThan(0) + expect(slow.maxFeePerGas).toBeGreaterThan(0) + expect(slow.maxPriorityFeePerGas).toBeGreaterThan(0) + expect(standard.maxFeePerGas).toBeGreaterThan(0) + expect(standard.maxPriorityFeePerGas).toBeGreaterThan(0) + expect(fast.maxFeePerGas).toBeGreaterThan(0) + expect(fast.maxPriorityFeePerGas).toBeGreaterThan(0) - expect( - slow.maxFeePerGas === standard.maxFeePerGas && - standard.maxFeePerGas === fast.maxFeePerGas - ) - expect( - slow.maxPriorityFeePerGas === standard.maxPriorityFeePerGas && - standard.maxPriorityFeePerGas === fast.maxPriorityFeePerGas - ) - expect(networkPrices.maxFeePerGas).toBeLessThanOrEqual( - slow.maxFeePerGas - ) - expect(networkPrices.maxPriorityFeePerGas).toBeLessThanOrEqual( - slow.maxPriorityFeePerGas - ) - expect(networkPrices.maxFeePerGas).toBeLessThanOrEqual( - standard.maxFeePerGas - ) - expect(networkPrices.maxPriorityFeePerGas).toBeLessThanOrEqual( - standard.maxPriorityFeePerGas - ) - expect(networkPrices.maxFeePerGas).toBeLessThanOrEqual( - fast.maxFeePerGas - ) - expect(networkPrices.maxPriorityFeePerGas).toBeLessThanOrEqual( - fast.maxPriorityFeePerGas - ) - }) -}) + expect( + slow.maxFeePerGas === standard.maxFeePerGas && + standard.maxFeePerGas === fast.maxFeePerGas + ) + expect( + slow.maxPriorityFeePerGas === standard.maxPriorityFeePerGas && + standard.maxPriorityFeePerGas === fast.maxPriorityFeePerGas + ) + expect(networkPrices.maxFeePerGas).toBeLessThanOrEqual( + slow.maxFeePerGas + ) + expect(networkPrices.maxPriorityFeePerGas).toBeLessThanOrEqual( + slow.maxPriorityFeePerGas + ) + expect(networkPrices.maxFeePerGas).toBeLessThanOrEqual( + standard.maxFeePerGas + ) + expect(networkPrices.maxPriorityFeePerGas).toBeLessThanOrEqual( + standard.maxPriorityFeePerGas + ) + expect(networkPrices.maxFeePerGas).toBeLessThanOrEqual( + fast.maxFeePerGas + ) + expect(networkPrices.maxPriorityFeePerGas).toBeLessThanOrEqual( + fast.maxPriorityFeePerGas + ) + }) + } +) diff --git a/test/e2e/tests/pimlico_sendCompressedUserOperation.test.ts b/test/e2e/tests/pimlico_sendCompressedUserOperation.test.ts index 1c76959f..bc39bf6f 100644 --- a/test/e2e/tests/pimlico_sendCompressedUserOperation.test.ts +++ b/test/e2e/tests/pimlico_sendCompressedUserOperation.test.ts @@ -1,13 +1,11 @@ import { describe, test, beforeAll, expect, beforeEach } from "vitest" -import type { ENTRYPOINT_ADDRESS_V06_TYPE } from "permissionless/types" import { beforeEachCleanUp, - getPimlicoBundlerClient, + getPimlicoClient, getSmartAccountClient, sendBundleNow, setBundlingMode } from "../src/utils" -import { ENTRYPOINT_ADDRESS_V06 } from "permissionless/utils" import { createPublicClient, createTestClient, @@ -20,7 +18,11 @@ import { } from "viem" import { ANVIL_RPC } from "../src/constants" import { foundry } from "viem/chains" -import type { PimlicoBundlerClient } from "permissionless/clients/pimlico" +import type { PimlicoClient } from "permissionless/clients/pimlico" +import { + UserOperationReceiptNotFoundError, + type UserOperation +} from "viem/account-abstraction" const publicClient = createPublicClient({ transport: http(ANVIL_RPC), @@ -71,10 +73,12 @@ const SIMPLE_INFLATOR_CONTRACT = getContract({ }) describe("V0.6 pimlico_sendCompressedUserOperation", () => { - let pimlicoBundlerClient: PimlicoBundlerClient<ENTRYPOINT_ADDRESS_V06_TYPE> + let pimlicoBundlerClient: PimlicoClient beforeAll(() => { - pimlicoBundlerClient = getPimlicoBundlerClient(ENTRYPOINT_ADDRESS_V06) + pimlicoBundlerClient = getPimlicoClient({ + entryPointVersion: "0.6" + }) }) beforeEach(async () => { @@ -83,25 +87,25 @@ describe("V0.6 pimlico_sendCompressedUserOperation", () => { test("Send compressed UserOperation", async () => { const smartAccountClient = await getSmartAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06 + entryPointVersion: "0.6" }) - const smartAccount = smartAccountClient.account const to = "0x23B608675a2B2fB1890d3ABBd85c5775c51691d5" const value = parseEther("0.15") - const op = await smartAccountClient.prepareUserOperationRequest({ - userOperation: { - callData: await smartAccount.encodeCallData({ + const op = (await smartAccountClient.prepareUserOperation({ + calls: [ + { to, value, data: "0x" - }) - } - }) - op.signature = await smartAccount.signUserOperation(op) + } + ] + })) as UserOperation<"0.6"> + op.signature = await smartAccountClient.account.signUserOperation(op) const compressedUserOperation = + // @ts-ignore: we know that op is properly typed await SIMPLE_INFLATOR_CONTRACT.read.compress([op]) const hash = await pimlicoBundlerClient.sendCompressedUserOperation({ @@ -130,7 +134,7 @@ describe("V0.6 pimlico_sendCompressedUserOperation", () => { test("Replace mempool transaction", async () => { const smartAccountClient = await getSmartAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06 + entryPointVersion: "0.6" }) const smartAccount = smartAccountClient.account @@ -140,18 +144,19 @@ describe("V0.6 pimlico_sendCompressedUserOperation", () => { const to = "0x23B608675a2B2fB1890d3ABBd85c5775c51691d5" const value = parseEther("0.15") - const op = await smartAccountClient.prepareUserOperationRequest({ - userOperation: { - callData: await smartAccount.encodeCallData({ + const op = (await smartAccountClient.prepareUserOperation({ + calls: [ + { to, value, data: "0x" - }) - } - }) + } + ] + })) as UserOperation<"0.6"> op.signature = await smartAccount.signUserOperation(op) const compressedUserOperation = + // @ts-ignore: we know that op is properly typed await SIMPLE_INFLATOR_CONTRACT.read.compress([op]) const hash = await pimlicoBundlerClient.sendCompressedUserOperation({ @@ -169,16 +174,17 @@ describe("V0.6 pimlico_sendCompressedUserOperation", () => { await new Promise((resolve) => setTimeout(resolve, 1500)) // check that no tx was mined - let opReceipt = await pimlicoBundlerClient.getUserOperationReceipt({ - hash - }) - expect(opReceipt).toBeNull() + await expect(async () => { + await pimlicoBundlerClient.getUserOperationReceipt({ + hash + }) + }).rejects.toThrow(UserOperationReceiptNotFoundError) // new block should trigger alto's mempool to replace the eoa tx with too low gasPrice await anvilClient.mine({ blocks: 1 }) await new Promise((resolve) => setTimeout(resolve, 1500)) - opReceipt = await pimlicoBundlerClient.getUserOperationReceipt({ + const opReceipt = await pimlicoBundlerClient.getUserOperationReceipt({ hash }) @@ -198,38 +204,38 @@ describe("V0.6 pimlico_sendCompressedUserOperation", () => { test("Send multiple compressedOps", async () => { const firstClient = await getSmartAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06 + entryPointVersion: "0.6" }) const secondClient = await getSmartAccountClient({ - entryPoint: ENTRYPOINT_ADDRESS_V06 + entryPointVersion: "0.6" }) const to = "0x23B608675a2B2fB1890d3ABBd85c5775c51691d5" const value = parseEther("0.15") // create sender op - const firstOp = await firstClient.prepareUserOperationRequest({ - userOperation: { - callData: await firstClient.account.encodeCallData({ - to, + const firstOp = (await firstClient.prepareUserOperation({ + calls: [ + { + to: "0x0000000000000000000000000000000000000000", value: value, data: "0x" - }) - } - }) + } + ] + })) as UserOperation<"0.6"> firstOp.signature = await firstClient.account.signUserOperation(firstOp) // create relayer op - const secondOp = await secondClient.prepareUserOperationRequest({ - userOperation: { - callData: await secondClient.account.encodeCallData({ + const secondOp = (await secondClient.prepareUserOperation({ + calls: [ + { to, value, data: "0x" - }) - } - }) + } + ] + })) as UserOperation<"0.6"> secondOp.signature = await secondClient.account.signUserOperation(secondOp) @@ -237,6 +243,7 @@ describe("V0.6 pimlico_sendCompressedUserOperation", () => { await setBundlingMode("manual") const firstCompressedOp = await SIMPLE_INFLATOR_CONTRACT.read.compress([ + // @ts-ignore: we know that firstOp is properly typed firstOp ]) const firstHash = @@ -245,6 +252,7 @@ describe("V0.6 pimlico_sendCompressedUserOperation", () => { inflatorAddress: SIMPLE_INFLATOR_CONTRACT.address }) const secondCompressedOp = await SIMPLE_INFLATOR_CONTRACT.read.compress( + // @ts-ignore: we know that firstOp is properly typed [secondOp] ) const secondHash = @@ -253,16 +261,16 @@ describe("V0.6 pimlico_sendCompressedUserOperation", () => { inflatorAddress: SIMPLE_INFLATOR_CONTRACT.address }) - expect( + await expect(async () => { await pimlicoBundlerClient.getUserOperationReceipt({ hash: firstHash }) - ).toBeNull() - expect( + }).rejects.toThrow(UserOperationReceiptNotFoundError) + await expect(async () => { await pimlicoBundlerClient.getUserOperationReceipt({ hash: secondHash }) - ).toBeNull() + }).rejects.toThrow(UserOperationReceiptNotFoundError) await sendBundleNow() diff --git a/test/e2e/tests/pimlico_sendInstantUserOperation.test.ts b/test/e2e/tests/pimlico_sendInstantUserOperation.test.ts new file mode 100644 index 00000000..21fcb68b --- /dev/null +++ b/test/e2e/tests/pimlico_sendInstantUserOperation.test.ts @@ -0,0 +1,56 @@ +import { describe, test, beforeEach } from "vitest" +import { beforeEachCleanUp, getSmartAccountClient } from "../src/utils" +import { + entryPoint06Address, + entryPoint07Address, + type EntryPointVersion, + type UserOperation +} from "viem/account-abstraction" +import { parseEther } from "viem" +import { deepHexlify } from "permissionless" + +describe.each([ + { + entryPoint: entryPoint06Address, + entryPointVersion: "0.6" as EntryPointVersion + }, + { + entryPoint: entryPoint07Address, + entryPointVersion: "0.7" as EntryPointVersion + } +])( + "$entryPointVersion supports pimlico_sendInstantUserOperation", + ({ entryPoint, entryPointVersion }) => { + beforeEach(async () => { + await beforeEachCleanUp() + }) + + test("Send instant userOperation", async () => { + const smartAccountClient = await getSmartAccountClient({ + entryPointVersion + }) + + const op = (await smartAccountClient.prepareUserOperation({ + calls: [ + { + to: "0x0000000000000000000000000000000000000000", + value: parseEther("0.15"), + data: "0x" + } + ] + })) as UserOperation<typeof entryPointVersion> + + op.signature = + await smartAccountClient.account.signUserOperation(op) + + const receipt = await smartAccountClient.request({ + // @ts-ignore + method: "pimlico_sendInstantUserOperation", + // @ts-ignore + params: [deepHexlify(op), entryPoint] + }) + + console.log(receipt) + }) + } +) From 3b0119114ff820aa9db46042d528dbda853e4711 Mon Sep 17 00:00:00 2001 From: mouseless <97399882+mouseless-eth@users.noreply.github.com> Date: Tue, 24 Sep 2024 11:53:34 +0100 Subject: [PATCH 08/12] add e2e test case --- biome.json | 3 ++ src/rpc/rpcHandler.ts | 8 +++-- test/e2e/tests/eth_sendUserOperation.test.ts | 1 - .../pimlico_sendInstantUserOperation.test.ts | 31 +++++++++++-------- 4 files changed, 26 insertions(+), 17 deletions(-) diff --git a/biome.json b/biome.json index b5e26477..f8654e5e 100644 --- a/biome.json +++ b/biome.json @@ -26,6 +26,9 @@ "all": true, "suspicious": { "noExplicitAny": "warn" + }, + "style": { + "useNamingConvention": "off" } } }, diff --git a/src/rpc/rpcHandler.ts b/src/rpc/rpcHandler.ts index e23d70e9..755ecb52 100644 --- a/src/rpc/rpcHandler.ts +++ b/src/rpc/rpcHandler.ts @@ -987,13 +987,15 @@ export class RpcHandler implements IRpcEndpoint { const res = result as unknown as { status: "success" - userOperation: UserOperationInfo - transactionInfo: TransactionInfo + value: { + userOperation: UserOperationInfo + transactionInfo: TransactionInfo + } } // wait for receipt const receipt = await this.publicClient.waitForTransactionReceipt({ - hash: res.transactionInfo.transactionHash, + hash: res.value.transactionInfo.transactionHash, pollingInterval: 100 }) diff --git a/test/e2e/tests/eth_sendUserOperation.test.ts b/test/e2e/tests/eth_sendUserOperation.test.ts index 2637acd5..45a723c8 100644 --- a/test/e2e/tests/eth_sendUserOperation.test.ts +++ b/test/e2e/tests/eth_sendUserOperation.test.ts @@ -23,7 +23,6 @@ import { type EntryPointVersion, entryPoint06Address, entryPoint07Address, - UserOperationNotFoundError, UserOperationReceiptNotFoundError } from "viem/account-abstraction" import { encodeNonce } from "permissionless/utils" diff --git a/test/e2e/tests/pimlico_sendInstantUserOperation.test.ts b/test/e2e/tests/pimlico_sendInstantUserOperation.test.ts index 21fcb68b..16ab9a99 100644 --- a/test/e2e/tests/pimlico_sendInstantUserOperation.test.ts +++ b/test/e2e/tests/pimlico_sendInstantUserOperation.test.ts @@ -1,19 +1,21 @@ -import { describe, test, beforeEach } from "vitest" +import { describe, test, beforeEach, expect } from "vitest" import { beforeEachCleanUp, getSmartAccountClient } from "../src/utils" import { - entryPoint06Address, entryPoint07Address, + UserOperationExpiredError, type EntryPointVersion, - type UserOperation + type UserOperation, + UserOperationReceipt } from "viem/account-abstraction" -import { parseEther } from "viem" +import { createClient, http, parseEther } from "viem" import { deepHexlify } from "permissionless" +import { ALTO_RPC } from "../src/constants" describe.each([ - { - entryPoint: entryPoint06Address, - entryPointVersion: "0.6" as EntryPointVersion - }, + //{ + // entryPoint: entryPoint06Address, + // entryPointVersion: "0.6" as EntryPointVersion + //}, { entryPoint: entryPoint07Address, entryPointVersion: "0.7" as EntryPointVersion @@ -39,18 +41,21 @@ describe.each([ } ] })) as UserOperation<typeof entryPointVersion> - op.signature = await smartAccountClient.account.signUserOperation(op) - const receipt = await smartAccountClient.request({ + const bundlerClient = createClient({ + transport: http(ALTO_RPC) + }) + + const receipt = (await bundlerClient.request({ // @ts-ignore method: "pimlico_sendInstantUserOperation", - // @ts-ignore params: [deepHexlify(op), entryPoint] - }) + })) as UserOperationReceipt - console.log(receipt) + expect(receipt).not.toBeNull() + expect(receipt?.success).toEqual(true) }) } ) From 7680e12cc030589ffb0fee39ebee18f978f8a62f Mon Sep 17 00:00:00 2001 From: mouseless <97399882+mouseless-eth@users.noreply.github.com> Date: Tue, 24 Sep 2024 12:04:16 +0100 Subject: [PATCH 09/12] add flag for pimlico_sendInstantUserOperation endpoint --- src/cli/config/bundler.ts | 3 ++- src/cli/config/options.ts | 6 ++++++ src/cli/setupServer.ts | 4 ++-- src/rpc/rpcHandler.ts | 10 ++++++++++ test/e2e/alto-config.json | 3 ++- 5 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/cli/config/bundler.ts b/src/cli/config/bundler.ts index 65c1bba0..cfb697d0 100644 --- a/src/cli/config/bundler.ts +++ b/src/cli/config/bundler.ts @@ -117,7 +117,8 @@ export const bundlerArgsSchema = z.object({ )}` ), "refilling-wallets": z.boolean().default(true), - "aa95-gas-multiplier": z.string().transform((val) => BigInt(val)) + "aa95-gas-multiplier": z.string().transform((val) => BigInt(val)), + "enable-instant-bundling-endpoint": z.boolean() }) export const compatibilityArgsSchema = z.object({ diff --git a/src/cli/config/options.ts b/src/cli/config/options.ts index a32b76a2..e3d67ead 100644 --- a/src/cli/config/options.ts +++ b/src/cli/config/options.ts @@ -179,6 +179,12 @@ export const bundlerOptions: CliCommandOptions<IBundlerArgsInput> = { type: "string", require: false, default: "125" + }, + "enable-instant-bundling-endpoint": { + description: + "Should the bundler enable the pimlico_sendInstantUserOperation endpoint", + type: "boolean", + default: false } } diff --git a/src/cli/setupServer.ts b/src/cli/setupServer.ts index 82f98999..3cf6c3ce 100644 --- a/src/cli/setupServer.ts +++ b/src/cli/setupServer.ts @@ -59,8 +59,7 @@ const getValidator = ({ logger, senderManager, metrics, - gasPriceManager, - walletClient + gasPriceManager }: { client: PublicClient<Transport, Chain> parsedArgs: IOptions @@ -377,6 +376,7 @@ const getRpcHandler = ({ parsedArgs["chain-type"], parsedArgs["paymaster-gas-limit-multiplier"], eventManager, + parsedArgs["enable-instant-bundling-endpoint"], parsedArgs["dangerous-skip-user-operation-validation"] ) } diff --git a/src/rpc/rpcHandler.ts b/src/rpc/rpcHandler.ts index 755ecb52..b77516cd 100644 --- a/src/rpc/rpcHandler.ts +++ b/src/rpc/rpcHandler.ts @@ -134,6 +134,7 @@ export class RpcHandler implements IRpcEndpoint { gasPriceMultipliers: GasPriceMultipliers paymasterGasLimitMultiplier: bigint eventManager: EventManager + enableInstantBundlingEndpoint: boolean constructor( entryPoints: Address[], @@ -157,6 +158,7 @@ export class RpcHandler implements IRpcEndpoint { chainType: ChainType, paymasterGasLimitMultiplier: bigint, eventManager: EventManager, + enableInstantBundlingEndpoint: boolean, dangerousSkipUserOperationValidation = false ) { this.entryPoints = entryPoints @@ -182,6 +184,7 @@ export class RpcHandler implements IRpcEndpoint { this.chainType = chainType this.gasPriceManager = gasPriceManager this.paymasterGasLimitMultiplier = paymasterGasLimitMultiplier + this.enableInstantBundlingEndpoint = enableInstantBundlingEndpoint this.eventManager = eventManager } @@ -939,6 +942,13 @@ export class RpcHandler implements IRpcEndpoint { userOperation: UserOperation, entryPoint: Address ) { + if (!this.enableInstantBundlingEndpoint) { + throw new RpcError( + "pimlico_sendInstantUserOperation endpoint is not enabled", + ValidationErrors.InvalidFields + ) + } + this.ensureEntryPointIsSupported(entryPoint) const opHash = getUserOperationHash( diff --git a/test/e2e/alto-config.json b/test/e2e/alto-config.json index 2593eded..57204412 100644 --- a/test/e2e/alto-config.json +++ b/test/e2e/alto-config.json @@ -22,5 +22,6 @@ "network-name": "0.0.0.0", "mempool-max-parallel-ops": 10, "mempool-max-queued-ops": 10, - "enforce-unique-senders-per-bundle": false + "enforce-unique-senders-per-bundle": false, + "enable-instant-bundling-endpoint": true } From bce2cf03fdb9cbf4e1c829b6992602aa15bb3f9b Mon Sep 17 00:00:00 2001 From: mouseless <97399882+mouseless-eth@users.noreply.github.com> Date: Tue, 24 Sep 2024 12:05:53 +0100 Subject: [PATCH 10/12] nit --- src/rpc/rpcHandler.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/rpc/rpcHandler.ts b/src/rpc/rpcHandler.ts index b77516cd..108625a6 100644 --- a/src/rpc/rpcHandler.ts +++ b/src/rpc/rpcHandler.ts @@ -336,7 +336,6 @@ export class RpcHandler implements IRpcEndpoint { } } - // checks done before mempool goes through simulation async preMempoolChecks( opHash: Hex, userOperation: UserOperation, From cd0b5d8f7956b147e25fdaf203555bedc202431e Mon Sep 17 00:00:00 2001 From: mouseless <97399882+mouseless-eth@users.noreply.github.com> Date: Tue, 24 Sep 2024 12:09:03 +0100 Subject: [PATCH 11/12] use all testcases --- test/e2e/package.json | 2 +- .../tests/pimlico_sendInstantUserOperation.test.ts | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/test/e2e/package.json b/test/e2e/package.json index 78417ce8..0b868e59 100644 --- a/test/e2e/package.json +++ b/test/e2e/package.json @@ -3,7 +3,7 @@ "version": "0.0.0", "private": true, "scripts": { - "test": "vitest dev -c ./vitest.config.ts pimlico_sendInstantUserOperation.test.ts", + "test": "vitest dev -c ./vitest.config.ts", "test:ci": "CI=true && vitest -c ./vitest.config.ts" }, "dependencies": { diff --git a/test/e2e/tests/pimlico_sendInstantUserOperation.test.ts b/test/e2e/tests/pimlico_sendInstantUserOperation.test.ts index 16ab9a99..3eabec17 100644 --- a/test/e2e/tests/pimlico_sendInstantUserOperation.test.ts +++ b/test/e2e/tests/pimlico_sendInstantUserOperation.test.ts @@ -1,21 +1,21 @@ import { describe, test, beforeEach, expect } from "vitest" import { beforeEachCleanUp, getSmartAccountClient } from "../src/utils" import { + entryPoint06Address, entryPoint07Address, - UserOperationExpiredError, type EntryPointVersion, type UserOperation, - UserOperationReceipt + type UserOperationReceipt } from "viem/account-abstraction" import { createClient, http, parseEther } from "viem" import { deepHexlify } from "permissionless" import { ALTO_RPC } from "../src/constants" describe.each([ - //{ - // entryPoint: entryPoint06Address, - // entryPointVersion: "0.6" as EntryPointVersion - //}, + { + entryPoint: entryPoint06Address, + entryPointVersion: "0.6" as EntryPointVersion + }, { entryPoint: entryPoint07Address, entryPointVersion: "0.7" as EntryPointVersion From e9fbfdc495a5c73329bab2f6c0c697f1fd1a95cb Mon Sep 17 00:00:00 2001 From: mouseless <97399882+mouseless-eth@users.noreply.github.com> Date: Tue, 24 Sep 2024 12:26:58 +0100 Subject: [PATCH 12/12] rename endpoint to pimlico_sendUserOperationNow --- src/cli/config/options.ts | 2 +- src/rpc/rpcHandler.ts | 8 ++++---- src/types/schemas.ts | 12 ++++++------ ....test.ts => pimlico_sendUserOperationNow.test.ts} | 4 ++-- 4 files changed, 13 insertions(+), 13 deletions(-) rename test/e2e/tests/{pimlico_sendInstantUserOperation.test.ts => pimlico_sendUserOperationNow.test.ts} (93%) diff --git a/src/cli/config/options.ts b/src/cli/config/options.ts index e3d67ead..263bd429 100644 --- a/src/cli/config/options.ts +++ b/src/cli/config/options.ts @@ -182,7 +182,7 @@ export const bundlerOptions: CliCommandOptions<IBundlerArgsInput> = { }, "enable-instant-bundling-endpoint": { description: - "Should the bundler enable the pimlico_sendInstantUserOperation endpoint", + "Should the bundler enable the pimlico_sendUserOperationNow endpoint", type: "boolean", default: false } diff --git a/src/rpc/rpcHandler.ts b/src/rpc/rpcHandler.ts index 108625a6..ecd40a22 100644 --- a/src/rpc/rpcHandler.ts +++ b/src/rpc/rpcHandler.ts @@ -307,10 +307,10 @@ export class RpcHandler implements IRpcEndpoint { ...request.params ) } - case "pimlico_sendInstantUserOperation": + case "pimlico_sendUserOperationNow": return { method, - result: await this.pimlico_sendInstantUserOperation( + result: await this.pimlico_sendUserOperationNow( apiVersion, ...request.params ) @@ -936,14 +936,14 @@ export class RpcHandler implements IRpcEndpoint { return "queued" } - async pimlico_sendInstantUserOperation( + async pimlico_sendUserOperationNow( apiVersion: ApiVersion, userOperation: UserOperation, entryPoint: Address ) { if (!this.enableInstantBundlingEndpoint) { throw new RpcError( - "pimlico_sendInstantUserOperation endpoint is not enabled", + "pimlico_sendUserOperationNow endpoint is not enabled", ValidationErrors.InvalidFields ) } diff --git a/src/types/schemas.ts b/src/types/schemas.ts index 6bf5eef6..e8c90dc0 100644 --- a/src/types/schemas.ts +++ b/src/types/schemas.ts @@ -343,8 +343,8 @@ const pimlicoSendCompressedUserOperationRequestSchema = z.object({ params: z.tuple([hexDataSchema, addressSchema, addressSchema]) }) -const pimlicoSendInstantBundleRequestSchema = z.object({ - method: z.literal("pimlico_sendInstantUserOperation"), +const pimlicoSendUserOperationNowRequestSchema = z.object({ + method: z.literal("pimlico_sendUserOperationNow"), params: z.tuple([userOperationSchema, addressSchema]) }) @@ -369,7 +369,7 @@ const bundlerRequestSchema = z.discriminatedUnion("method", [ pimlicoGetUserOperationStatusRequestSchema, pimlicoGetUserOperationGasPriceRequestSchema, pimlicoSendCompressedUserOperationRequestSchema, - pimlicoSendInstantBundleRequestSchema + pimlicoSendUserOperationNowRequestSchema ]) const chainIdResponseSchema = z.object({ @@ -578,8 +578,8 @@ const pimlicoSendCompressedUserOperationResponseSchema = z.object({ result: hexData32Schema }) -const pimlicoSendInstantBundleResponseSchema = z.object({ - method: z.literal("pimlico_sendInstantUserOperation"), +const pimlicoSendUserOperationNowResponseSchema = z.object({ + method: z.literal("pimlico_sendUserOperationNow"), result: userOperationReceiptSchema }) @@ -601,7 +601,7 @@ const bundlerResponseSchema = z.discriminatedUnion("method", [ pimlicoGetUserOperationStatusResponseSchema, pimlicoGetUserOperationGasPriceResponseSchema, pimlicoSendCompressedUserOperationResponseSchema, - pimlicoSendInstantBundleResponseSchema + pimlicoSendUserOperationNowResponseSchema ]) export type BundlingMode = z.infer< diff --git a/test/e2e/tests/pimlico_sendInstantUserOperation.test.ts b/test/e2e/tests/pimlico_sendUserOperationNow.test.ts similarity index 93% rename from test/e2e/tests/pimlico_sendInstantUserOperation.test.ts rename to test/e2e/tests/pimlico_sendUserOperationNow.test.ts index 3eabec17..b7502e2d 100644 --- a/test/e2e/tests/pimlico_sendInstantUserOperation.test.ts +++ b/test/e2e/tests/pimlico_sendUserOperationNow.test.ts @@ -21,7 +21,7 @@ describe.each([ entryPointVersion: "0.7" as EntryPointVersion } ])( - "$entryPointVersion supports pimlico_sendInstantUserOperation", + "$entryPointVersion supports pimlico_sendUserOperationNow", ({ entryPoint, entryPointVersion }) => { beforeEach(async () => { await beforeEachCleanUp() @@ -50,7 +50,7 @@ describe.each([ const receipt = (await bundlerClient.request({ // @ts-ignore - method: "pimlico_sendInstantUserOperation", + method: "pimlico_sendUserOperationNow", params: [deepHexlify(op), entryPoint] })) as UserOperationReceipt