Skip to content

Commit

Permalink
create UserOperationBundle type
Browse files Browse the repository at this point in the history
  • Loading branch information
mouseless0x committed Jan 26, 2025
1 parent 8b2248d commit e571611
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 137 deletions.
40 changes: 16 additions & 24 deletions src/executor/executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import {
type TransactionInfo,
type UserOperation,
type UserOperationV07,
type GasPriceParameters
type GasPriceParameters,
UserOperationBundle
} from "@alto/types"
import type { Logger, Metrics } from "@alto/utils"
import {
Expand Down Expand Up @@ -521,21 +522,12 @@ export class Executor {

async bundle(
wallet: Account,
entryPoint: Address,
ops: UserOperation[]
bundle: UserOperationBundle
): Promise<BundleResult> {
// Find bundle EntryPoint version.
const firstOpVersion = isVersion06(ops[0])
const allSameVersion = ops.every(
(op) => isVersion06(op) === firstOpVersion
)
if (!allSameVersion) {
throw new Error("All user operations must be of the same version")
}
const isUserOpV06 = firstOpVersion
const { entryPoint, userOperations, version } = bundle

const ep = getContract({
abi: isUserOpV06 ? EntryPointV06Abi : EntryPointV07Abi,
abi: version === "0.6" ? EntryPointV06Abi : EntryPointV07Abi,
address: entryPoint,
client: {
public: this.config.publicClient,
Expand All @@ -544,7 +536,7 @@ export class Executor {
})

let childLogger = this.logger.child({
userOperations: this.getOpHashes(ops),
userOperations: this.getOpHashes(userOperations),
entryPoint
})
childLogger.debug("bundling user operation")
Expand All @@ -568,15 +560,15 @@ export class Executor {
return {
status: "bundle_resubmit",
reason: "Failed to get parameters for bundling",
userOps: ops
userOps: userOperations
}
}

let estimateResult = await filterOpsAndEstimateGas({
isUserOpV06,
isUserOpV06: version === "0.6",
ops: userOperations,
ep,
wallet,
ops,
nonce,
maxFeePerGas: gasPriceParameters.maxFeePerGas,
maxPriorityFeePerGas: gasPriceParameters.maxPriorityFeePerGas,
Expand All @@ -592,7 +584,7 @@ export class Executor {
return {
status: "bundle_failure",
reason: "INTERNAL FAILURE",
userOps: ops
userOps: userOperations
}
}

Expand All @@ -603,7 +595,7 @@ export class Executor {
return {
status: "bundle_failure",
reason: "INTERNAL FAILURE",
userOps: ops
userOps: userOperations
}
}

Expand Down Expand Up @@ -655,7 +647,7 @@ export class Executor {

// TODO: move this to a seperate utility
const userOps = opsToBundle.map((op) => {
if (isUserOpV06) {
if (version === "0.6") {
return op
}
return toPackedUserOperation(op as UserOperationV07)
Expand All @@ -665,7 +657,7 @@ export class Executor {
txParam: {
ops: userOps,
isReplacementTx: false,
isUserOpVersion06: isUserOpV06,
isUserOpVersion06: version === "0.6",
entryPoint
},
opts
Expand All @@ -685,7 +677,7 @@ export class Executor {
return {
status: "bundle_resubmit",
reason: InsufficientFundsError.name,
userOps: ops
userOps: userOperations
}
}

Expand All @@ -697,7 +689,7 @@ export class Executor {
return {
status: "bundle_failure",
reason: "INTERNAL FAILURE",
userOps: ops
userOps: userOperations
}
}

Expand All @@ -713,7 +705,7 @@ export class Executor {

const transactionInfo: TransactionInfo = {
entryPoint,
isVersion06: isUserOpV06,
isVersion06: version === "0.6",
transactionHash: transactionHash,
previousTransactionHashes: [],
transactionRequest: {
Expand Down
173 changes: 68 additions & 105 deletions src/executor/executorManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,14 @@ import type {
Monitor
} from "@alto/mempool"
import {
type BundleResult,
type BundlingMode,
EntryPointV06Abi,
type HexData32,
type UserOperation,
type SubmittedUserOperation,
type TransactionInfo,
type UserOperationInfo,
RejectedUserOperation
RejectedUserOperation,
UserOperationBundle
} from "@alto/types"
import type { BundlingStatus, Logger, Metrics } from "@alto/utils"
import {
Expand All @@ -31,8 +30,7 @@ import {
TransactionReceiptNotFoundError,
type WatchBlocksReturnType,
getAbiItem,
Hex,
Account
Hex
} from "viem"
import type { Executor, ReplaceTransactionResult } from "./executor"
import type { AltoConfig } from "../createConfig"
Expand Down Expand Up @@ -141,10 +139,17 @@ export class ExecutorManager {
const opsCount: number = bundles
.map(({ userOperations }) => userOperations.length)
.reduce((a, b) => a + b)
const timestamp: number = Date.now()
this.opsCount.push(...Array(opsCount).fill(timestamp)) // Add timestamps for each task

await this.sendBundles(bundles)
// Add timestamps for each task
const timestamp = Date.now()
this.opsCount.push(...Array(opsCount).fill(timestamp))

// Send bundles to executor
await Promise.all(
bundles.map(async (bundle) => {
await this.sendBundleToExecutor(bundle)
})
)
}

const rpm: number = this.opsCount.length
Expand All @@ -159,20 +164,17 @@ export class ExecutorManager {
}
}

async getMempoolBundles(maxBundleCount?: number) {
async getMempoolBundles(
maxBundleCount?: number
): Promise<UserOperationBundle[]> {
const bundlePromises = this.config.entrypoints.map(
async (entryPoint) => {
const mempoolBundles = await this.mempool.process({
return await this.mempool.process({
entryPoint,
maxGasLimit: this.config.maxGasPerBundle,
minOpsPerBundle: 1,
maxBundleCount
})

return mempoolBundles.map((userOperations) => ({
entryPoint,
userOperations
}))
}
)

Expand All @@ -186,16 +188,11 @@ export class ExecutorManager {
async sendBundleNow(): Promise<Hash> {
const bundle = (await this.getMempoolBundles(1))[0]

const { entryPoint, userOperations } = bundle
if (userOperations.length === 0) {
if (bundle.userOperations.length === 0) {
throw new Error("no ops to bundle")
}

const txHashes = await this.sendBundleToExecutor(
entryPoint,
userOperations.map((op) => op.userOperation)
)
const txHash = txHashes[0]
const txHash = await this.sendBundleToExecutor(bundle)

if (!txHash) {
throw new Error("no tx hash")
Expand All @@ -212,101 +209,67 @@ export class ExecutorManager {
)
}

async sendBundleToExecutor(bundle: {
entryPoint: Address
userOps: UserOperation[]
}): Promise<Hex[]> {
if (bundle.userOps.length === 0) {
return []
async sendBundleToExecutor(
bundleToSend: UserOperationBundle
): Promise<Hex | undefined> {
const { entryPoint, userOperations } = bundleToSend
if (userOperations.length === 0) {
return undefined
}

const bundles: { wallet: Account; bundle: BundleResult }[] = []
if (userOps.length > 0) {
const wallet = await this.senderManager.getWallet()
bundles.push({
wallet,
bundle: await this.executor.bundle(wallet, entryPoint, userOps)
})
const wallet = await this.senderManager.getWallet()
const bundle = await this.executor.bundle(wallet, bundleToSend)

switch (bundle.status) {
case "bundle_success":
this.metrics.bundlesSubmitted
.labels({ status: "success" })
.inc()
break
case "bundle_failure":
this.metrics.bundlesSubmitted.labels({ status: "failed" }).inc()
break
case "bundle_resubmit":
this.metrics.bundlesSubmitted
.labels({ status: "resubmit" })
.inc()
break
}

let txHashes: Hex[] = []
for (const { wallet, bundle } of bundles) {
switch (bundle.status) {
case "bundle_success":
this.metrics.bundlesSubmitted
.labels({ status: "success" })
.inc()
break
case "bundle_failure":
this.metrics.bundlesSubmitted
.labels({ status: "failed" })
.inc()
break
case "bundle_resubmit":
this.metrics.bundlesSubmitted
.labels({ status: "resubmit" })
.inc()
break
}
// Free wallet if the wallet did not make a succesful bundle tx.
if (
bundle.status === "bundle_failure" ||
bundle.status === "bundle_resubmit"
) {
this.senderManager.markWalletProcessed(wallet)
}

// Free wallet if the wallet did not make a succesful bundle tx.
if (
bundle.status === "bundle_failure" ||
bundle.status === "bundle_resubmit"
) {
this.senderManager.markWalletProcessed(wallet)
}
if (bundle.status === "bundle_resubmit") {
const { userOps: userOperations, reason } = bundle
this.resubmitUserOperations(userOperations, entryPoint, reason)
}

if (bundle.status === "bundle_resubmit") {
const { userOps: userOperations, reason } = bundle
this.resubmitUserOperations(userOperations, entryPoint, reason)
}
if (bundle.status === "bundle_failure") {
const { userOps, reason } = bundle

if (bundle.status === "bundle_failure") {
const { userOps, reason } = bundle
const droppedUserOperations = userOps.map((op) => ({
userOperation: op,
reason
}))
this.dropUserOperations(droppedUserOperations)
}

const droppedUserOperations = userOps.map((op) => ({
userOperation: op,
reason
}))
this.dropUserOperations(droppedUserOperations)
}
if (bundle.status === "bundle_success") {
const { userOpsBundled, rejectedUserOperations, transactionInfo } =
bundle

if (bundle.status === "bundle_success") {
const {
userOpsBundled,
rejectedUserOperations,
transactionInfo
} = bundle
txHashes.push(transactionInfo.transactionHash)

this.markUserOperationsAsSubmitted(
userOpsBundled,
transactionInfo
)
this.markUserOperationsAsSubmitted(userOpsBundled, transactionInfo)
this.dropUserOperations(rejectedUserOperations)

this.dropUserOperations(rejectedUserOperations)
}
return transactionInfo.transactionHash
}

return txHashes
}

async sendBundles(
bundles: {
entryPoint: Address
userOperations: UserOperationInfo[]
}[] = []
) {
await Promise.all(
bundles.map(async (bundle) => {
const { entryPoint, userOperations } = bundle
await this.sendBundleToExecutor(
entryPoint,
userOperations.map((op) => op.userOperation)
)
})
)
return undefined
}

startWatchingBlocks(handleBlock: (block: Block) => void): void {
Expand Down
Loading

0 comments on commit e571611

Please sign in to comment.