Skip to content

Commit

Permalink
Merge pull request #284 from pimlicolabs/fix/resubmit-when-AA95
Browse files Browse the repository at this point in the history
Fix/resubmit when aa95
  • Loading branch information
mouseless-eth authored Aug 22, 2024
2 parents a063ca0 + 2632b3d commit e25ee21
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 21 deletions.
27 changes: 18 additions & 9 deletions src/cli/config/bundler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ import { z } from "zod"

const logLevel = z.enum(["trace", "debug", "info", "warn", "error", "fatal"])

const rpcMethodNames = bundlerRequestSchema.options
.map((s) => s.shape.method._def.value) as [string, ...string[]]
const rpcMethodNames = bundlerRequestSchema.options.map(
(s) => s.shape.method._def.value
) as [string, ...string[]]

export const bundlerArgsSchema = z.object({
entrypoints: z
Expand Down Expand Up @@ -93,21 +94,29 @@ export const bundlerArgsSchema = z.object({
.string()
.nullable()
.transform((val: string | null) => {
if (val === null) return null;
if (val === null) return null

return val.split(",")
})
.refine((values) => {
if (values === null) return true;
if (values === null) return true

return values.length > 0
}, "Must contain at least one method if specified")
.refine((values) => {
if (values === null) return true;

return values.every((value: string) => rpcMethodNames.includes(value))
}, `Unknown method specified, available methods: ${rpcMethodNames.join(",")}`),
.refine(
(values) => {
if (values === null) return true

return values.every((value: string) =>
rpcMethodNames.includes(value)
)
},
`Unknown method specified, available methods: ${rpcMethodNames.join(
","
)}`
),
"refilling-wallets": z.boolean().default(true),
"aa95-gas-multiplier": z.string().transform((val) => BigInt(val))
})

export const compatibilityArgsSchema = z.object({
Expand Down
10 changes: 9 additions & 1 deletion src/cli/config/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,8 @@ export const bundlerOptions: CliCommandOptions<IBundlerArgsInput> = {
default: "100,100,100"
},
"gas-price-refresh-interval": {
description: "How to often to refresh the gas prices (seconds). If 0, then gas prices are refreshed on every request",
description:
"How to often to refresh the gas prices (seconds). If 0, then gas prices are refreshed on every request",
type: "number",
require: false,
default: 0
Expand Down Expand Up @@ -166,6 +167,13 @@ export const bundlerOptions: CliCommandOptions<IBundlerArgsInput> = {
require: false,
default: true
},
"aa95-gas-multiplier": {
description:
"Amount to multiply the current gas limit by if the bundling tx fails due to AA95",
type: "string",
require: false,
default: "125"
}
}

export const compatibilityOptions: CliCommandOptions<ICompatibilityArgsInput> =
Expand Down
5 changes: 3 additions & 2 deletions src/cli/setupServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,8 @@ const getExecutorManager = ({
parsedArgs["max-bundle-wait"],
parsedArgs["max-gas-per-bundle"],
gasPriceManager,
eventManager
eventManager,
parsedArgs["aa95-gas-multiplier"]
)
}

Expand Down Expand Up @@ -460,7 +461,7 @@ export const setupServer = async ({
walletClient,
parsedArgs["min-executor-balance"]
)
}, parsedArgs["executor-refill-interval"] * 1000)
}, parsedArgs["executor-refill-interval"] * 1000)
}

const monitor = getMonitor()
Expand Down
3 changes: 2 additions & 1 deletion src/executor/executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,8 @@ export class Executor {
// sometimes the estimation rounds down, adding a fixed constant accounts for this
gasLimit += 10_000n

newRequest.gas = gasLimit
// ensures that we don't submit again with too low of a gas value
newRequest.gas = maxBigInt(newRequest.gas, gasLimit)

// update calldata to include only ops that pass simulation
if (transactionInfo.transactionType === "default") {
Expand Down
22 changes: 20 additions & 2 deletions src/executor/executorManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export class ExecutorManager {
private maxGasLimitPerBundle: bigint
private gasPriceManager: GasPriceManager
private eventManager: EventManager
private aa95ResubmitMultiplier: bigint

constructor(
executor: Executor,
Expand All @@ -74,7 +75,8 @@ export class ExecutorManager {
bundlerFrequency: number,
maxGasLimitPerBundle: bigint,
gasPriceManager: GasPriceManager,
eventManager: EventManager
eventManager: EventManager,
aa95ResubmitMultiplier: bigint
) {
this.entryPoints = entryPoints
this.reputationManager = reputationManager
Expand All @@ -89,6 +91,7 @@ export class ExecutorManager {
this.maxGasLimitPerBundle = maxGasLimitPerBundle
this.gasPriceManager = gasPriceManager
this.eventManager = eventManager
this.aa95ResubmitMultiplier = aa95ResubmitMultiplier

if (bundleMode === "auto") {
this.timer = setInterval(async () => {
Expand Down Expand Up @@ -378,6 +381,7 @@ export class ExecutorManager {
isVersion06,
transactionHash,
this.publicClient,
this.logger,
entryPoint
))
}))
Expand Down Expand Up @@ -460,7 +464,21 @@ export class ExecutorManager {
})

this.executor.markWalletProcessed(transactionInfo.executor)
} else if (bundlingStatus.status === "reverted") {
} else if (
bundlingStatus.status === "reverted" &&
bundlingStatus.isAA95
) {
// resubmit with more gas when bundler encounters AA95
const multiplier = this.aa95ResubmitMultiplier
transactionInfo.transactionRequest.gas =
(transactionInfo.transactionRequest.gas * multiplier) / 100n
transactionInfo.transactionRequest.nonce += 1

opInfos.map(({ userOperationHash }) => {
this.mempool.removeSubmitted(userOperationHash)
})
await this.replaceTransaction(transactionInfo, "AA95")
} else {
opInfos.map(({ userOperationHash }) => {
this.mempool.removeSubmitted(userOperationHash)
this.monitor.setUserOperationStatus(userOperationHash, {
Expand Down
45 changes: 39 additions & 6 deletions src/utils/userop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,12 @@ import {
toHex,
concat,
slice,
pad
pad,
decodeErrorResult,
parseAbi
} from "viem"
import { areAddressesEqual } from "./helpers"
import type { Logger } from "pino"

// Type predicate check if the UserOperation is V06.
export function isVersion06(
Expand Down Expand Up @@ -202,6 +205,8 @@ type BundlingStatus =
| {
// The tx reverted due to a op in the bundle failing EntryPoint validation
status: "reverted"
// biome-ignore lint/style/useNamingConvention: use double upper case for AA errors
isAA95: boolean
}
| {
// The tx could not be found (pending or invalid hash)
Expand All @@ -213,6 +218,7 @@ export const getBundleStatus = async (
isVersion06: boolean,
txHash: HexData32,
publicClient: PublicClient,
logger: Logger,
entryPoint: Address
): Promise<{
bundlingStatus: BundlingStatus
Expand All @@ -225,12 +231,39 @@ export const getBundleStatus = async (
const blockNumber = receipt.blockNumber

if (receipt.status === "reverted") {
return {
bundlingStatus: {
status: "reverted"
},
blockNumber
const bundlingStatus: { status: "reverted"; isAA95: boolean } = {
status: "reverted",
isAA95: false
}

if ("error" in receipt) {
try {
const match = (receipt.error as any).match(
/0x([a-fA-F0-9]+)?/
)

if (match) {
const revertReason = match[0] as Hex
const decoded = decodeErrorResult({
data: revertReason,
abi: parseAbi([
"error FailedOp(uint256 opIndex, string reason)"
])
})

if (decoded.args[1] === "AA95 out of gas") {
bundlingStatus.isAA95 = true
}
}
} catch (e) {
logger.error(
"Failed to decode userOperation revert reason due to ",
e
)
}
}

return { bundlingStatus, blockNumber }
}

const userOperationDetails = receipt.logs
Expand Down

0 comments on commit e25ee21

Please sign in to comment.