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