Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Retry logic #320

Merged
merged 8 commits into from
Oct 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 91 additions & 4 deletions src/executor/executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,21 @@ import {
isVersion06,
maxBigInt,
parseViemError,
scaleBigIntByPercent,
toPackedUserOperation
} from "@alto/utils"
import * as sentry from "@sentry/node"
import { Mutex } from "async-mutex"
import {
type Account,
FeeCapTooLowError,
InsufficientFundsError,
IntrinsicGasTooLowError,
NonceTooLowError,
encodeFunctionData,
getContract
getContract,
type Account,
type Hex,
TransactionExecutionError
} from "viem"
import {
type CompressedFilterOpsAndEstimateGasParams,
Expand All @@ -50,6 +53,7 @@ import {
flushStuckTransaction,
simulatedOpsToResults
} from "./utils"
import type { SendTransactionErrorType } from "viem"
import type { AltoConfig } from "../createConfig"

export interface GasEstimateResult {
Expand Down Expand Up @@ -497,6 +501,87 @@ export class Executor {
await Promise.all(promises)
}

async sendHandleOpsTransaction(
userOps: PackedUserOperation[],
isUserOpVersion06: boolean,
entryPoint: Address,
opts:
| {
gasPrice: bigint
maxFeePerGas?: undefined
maxPriorityFeePerGas?: undefined
account: Account
gas: bigint
nonce: number
}
| {
maxFeePerGas: bigint
maxPriorityFeePerGas: bigint
gasPrice?: undefined
account: Account
gas: bigint
nonce: number
}
) {
const request =
await this.config.walletClient.prepareTransactionRequest({
to: entryPoint,
data: encodeFunctionData({
abi: isUserOpVersion06
? EntryPointV06Abi
: EntryPointV07Abi,
functionName: "handleOps",
args: [userOps, opts.account.address]
}),
...opts
})

let attempts = 0
let transactionHash: Hex | undefined
const maxAttempts = 3

// Try sending the transaction and updating relevant fields if there is an error.
while (attempts < maxAttempts) {
try {
transactionHash =
await this.config.walletClient.sendTransaction(request)

break
} catch (e: unknown) {
const error = e as SendTransactionErrorType
let isErrorHandled = false

if (error instanceof TransactionExecutionError) {
const cause = error.cause

if (cause instanceof NonceTooLowError) {
this.logger.warn("Nonce too low, retrying")
request.nonce += 1
isErrorHandled = true
}

if (cause instanceof IntrinsicGasTooLowError) {
this.logger.warn("Intrinsic gas too low, retrying")
request.gas = scaleBigIntByPercent(request.gas, 150)
isErrorHandled = true
}
}

attempts++
if (attempts === maxAttempts || !isErrorHandled) {
throw error
}
}
}

// needed for TS
if (!transactionHash) {
throw new Error("Transaction hash not assigned")
}

return transactionHash as Hex
}

async bundle(
entryPoint: Address,
ops: UserOperation[]
Expand Down Expand Up @@ -679,8 +764,10 @@ export class Executor {
)
) as PackedUserOperation[]

transactionHash = await ep.write.handleOps(
[userOps, wallet.address],
transactionHash = await this.sendHandleOpsTransaction(
userOps,
isUserOpVersion06,
entryPoint,
opts
)

Expand Down
6 changes: 5 additions & 1 deletion src/utils/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ import {
getContract,
serializeTransaction,
toBytes,
toFunctionSelector
toFunctionSelector,
InternalRpcError
} from "viem"
import { base, baseGoerli, baseSepolia } from "viem/chains"
import { maxBigInt, minBigInt, scaleBigIntByPercent } from "./bigInt"
Expand Down Expand Up @@ -706,6 +707,9 @@ export function parseViemError(err: unknown) {
if (e instanceof EstimateGasExecutionError) {
return e
}
if (e instanceof InternalRpcError) {
return e
}
return
}
return
Expand Down
Loading