Skip to content

Commit

Permalink
restore userOp with hash type
Browse files Browse the repository at this point in the history
  • Loading branch information
mouseless0x committed Jan 30, 2025
1 parent 008a5a0 commit a5fc67b
Show file tree
Hide file tree
Showing 11 changed files with 748 additions and 699 deletions.
253 changes: 98 additions & 155 deletions src/executor/executor.ts

Large diffs are not rendered by default.

374 changes: 196 additions & 178 deletions src/executor/executorManager.ts

Large diffs are not rendered by default.

197 changes: 115 additions & 82 deletions src/executor/filterOpsAndEStimateGas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import {
EntryPointV06Abi,
EntryPointV07Abi,
FailedOpWithRevert,
UserOperationInfo,
UserOperationV07,
RejectedUserOp,
UserOpInfo,
UserOperationBundle,
failedOpErrorSchema,
failedOpWithRevertErrorSchema
} from "@alto/types"
Expand All @@ -13,81 +14,93 @@ import {
ContractFunctionRevertedError,
EstimateGasExecutionError,
FeeCapTooLowError,
GetContractReturnType,
Hex,
PublicClient,
WalletClient,
decodeErrorResult
decodeErrorResult,
getContract
} from "viem"
import { AltoConfig } from "../createConfig"
import {
Logger,
getRevertErrorData,
parseViemError,
scaleBigIntByPercent,
toPackedUserOperation
scaleBigIntByPercent
} from "@alto/utils"
import { z } from "zod"
import { getAuthorizationList } from "./utils"
import { getAuthorizationList, packUserOps } from "./utils"
import * as sentry from "@sentry/node"

type FailedOpWithReason = {
userOperation: UserOperationInfo
reason: string
}

export type FilterOpsAndEstimateGasResult =
| {
status: "success"
opsToBundle: UserOperationInfo[]
failedOps: FailedOpWithReason[]
userOpsToBundle: UserOpInfo[]
rejectedUserOps: RejectedUserOp[]
gasLimit: bigint
}
| {
status: "unexpected_failure"
reason: string
status: "unhandled_failure"
rejectedUserOps: RejectedUserOp[]
}
| {
status: "all_ops_failed_simulation"
failedOps: FailedOpWithReason[]
rejectedUserOps: RejectedUserOp[]
}

function rejectUserOp(userOpInfo: UserOpInfo, reason: string): RejectedUserOp {
return {
...userOpInfo,
reason
}
}

function rejectUserOps(
userOpInfos: UserOpInfo[],
reason: string
): RejectedUserOp[] {
return userOpInfos.map((userOpInfo) => rejectUserOp(userOpInfo, reason))
}

// Attempt to create a handleOps bundle + estimate bundling tx gas.
export async function filterOpsAndEstimateGas({
ep,
isUserOpV06,
wallet,
ops,
executor,
userOpBundle,
nonce,
maxFeePerGas,
maxPriorityFeePerGas,
reputationManager,
config,
logger
}: {
ep: GetContractReturnType<
typeof EntryPointV06Abi | typeof EntryPointV07Abi,
{
public: PublicClient
wallet: WalletClient
}
>
isUserOpV06: boolean
wallet: Account
ops: UserOperationInfo[]
executor: Account
userOpBundle: UserOperationBundle
nonce: number
maxFeePerGas: bigint
maxPriorityFeePerGas: bigint
reputationManager: InterfaceReputationManager
config: AltoConfig
logger: Logger
}): Promise<FilterOpsAndEstimateGasResult> {
let { legacyTransactions, fixedGasLimitForEstimation, blockTagSupport } =
config
const { userOps: userOperations, version, entryPoint } = userOpBundle
let {
fixedGasLimitForEstimation,
legacyTransactions,
blockTagSupport,
publicClient,
walletClient
} = config

const isUserOpV06 = version === "0.6"
const epContract = getContract({
abi: isUserOpV06 ? EntryPointV06Abi : EntryPointV07Abi,
address: entryPoint,
client: {
public: publicClient,
wallet: walletClient
}
})

// Keep track of invalid and valid ops
const opsToBundle = [...ops]
const failedOps: FailedOpWithReason[] = []
const userOpsToBundle = [...userOperations]
const rejectedUserOps: RejectedUserOp[] = []

// Prepare bundling tx params
const gasOptions = legacyTransactions
Expand All @@ -98,28 +111,27 @@ export async function filterOpsAndEstimateGas({
let gasLimit: bigint
let retriesLeft = 5

while (opsToBundle.length > 0) {
while (userOpsToBundle.length > 0) {
if (retriesLeft === 0) {
logger.error("max retries reached")
return {
status: "unexpected_failure",
reason: "max retries reached"
status: "unhandled_failure",
rejectedUserOps: [
...rejectedUserOps,
...rejectUserOps(userOpsToBundle, "INTERNAL FAILURE")
]
}
}

try {
const encodedOps = opsToBundle.map((userOperation) => {
return isUserOpV06
? userOperation
: toPackedUserOperation(userOperation as UserOperationV07)
})
const packedUserOps = packUserOps(userOpsToBundle)
const authorizationList = getAuthorizationList(userOpsToBundle)

const authorizationList = getAuthorizationList(opsToBundle)

gasLimit = await ep.estimateGas.handleOps(
gasLimit = await epContract.estimateGas.handleOps(
// @ts-ignore - ep is set correctly for opsToSend, but typescript doesn't know that
[encodedOps, wallet.address],
[packedUserOps, executor.address],
{
account: wallet,
account: executor,
nonce: nonce,
blockTag,
...(fixedGasLimitForEstimation && {
Expand All @@ -134,8 +146,8 @@ export async function filterOpsAndEstimateGas({

return {
status: "success",
opsToBundle,
failedOps,
userOpsToBundle,
rejectedUserOps,
gasLimit
}
} catch (err: unknown) {
Expand All @@ -156,8 +168,14 @@ export async function filterOpsAndEstimateGas({
"failed to parse failedOpError"
)
return {
status: "unexpected_failure",
reason: "failed to parse failedOpError"
status: "unhandled_failure",
rejectedUserOps: [
...rejectedUserOps,
...rejectUserOps(
userOpsToBundle,
"INTERNAL FAILURE"
)
]
}
}

Expand All @@ -173,24 +191,24 @@ export async function filterOpsAndEstimateGas({
continue
}

const failingOpIndex = Number(errorData.opIndex)
const failingUserOp = userOpsToBundle[failingOpIndex]
userOpsToBundle.splice(failingOpIndex, 1)

reputationManager.crashedHandleOps(
failingUserOp.userOp,
epContract.address,
errorData.reason
)

const innerError = (errorData as FailedOpWithRevert)?.inner
const reason = innerError
const revertReason = innerError
? `${errorData.reason} - ${innerError}`
: errorData.reason

const failingOp = {
userOperation: opsToBundle[Number(errorData.opIndex)],
reason
}
opsToBundle.splice(Number(errorData.opIndex), 1)

reputationManager.crashedHandleOps(
failingOp.userOperation,
ep.address,
failingOp.reason
rejectedUserOps.push(
rejectUserOp(failingUserOp, revertReason)
)

failedOps.push(failingOp)
}
} else if (
e instanceof EstimateGasExecutionError ||
Expand Down Expand Up @@ -251,29 +269,38 @@ export async function filterOpsAndEstimateGas({
"unexpected error result"
)
return {
status: "unexpected_failure",
reason: "unexpected error result"
status: "unhandled_failure",
rejectedUserOps: [
...rejectedUserOps,
...rejectUserOps(
userOpsToBundle,
"INTERNAL FAILURE"
)
]
}
}

const failedOpIndex = Number(errorResult.args[0])
const failingOp = {
userOperation: opsToBundle[failedOpIndex],
reason: errorResult.args[1]
}
const [opIndex, reason] = errorResult.args

failedOps.push(failingOp)
opsToBundle.splice(Number(errorResult.args[0]), 1)
const failedOpIndex = Number(opIndex)
const failingUserOp = userOpsToBundle[failedOpIndex]

continue
rejectedUserOps.push(rejectUserOp(failingUserOp, reason))
userOpsToBundle.splice(failedOpIndex, 1)
} catch (e: unknown) {
logger.error(
{ error: JSON.stringify(err) },
"failed to parse error result"
)
return {
status: "unexpected_failure",
reason: "failed to parse error result"
status: "unhandled_failure",
rejectedUserOps: [
...rejectedUserOps,
...rejectUserOps(
userOpsToBundle,
"INTERNAL FAILURE"
)
]
}
}
} else {
Expand All @@ -283,12 +310,18 @@ export async function filterOpsAndEstimateGas({
"error estimating gas"
)
return {
status: "unexpected_failure",
reason: "error estimating gas"
status: "unhandled_failure",
rejectedUserOps: [
...rejectedUserOps,
...rejectUserOps(userOpsToBundle, "INTERNAL FAILURE")
]
}
}
}
}

return { status: "all_ops_failed_simulation", failedOps }
return {
status: "all_ops_failed_simulation",
rejectedUserOps
}
}
28 changes: 0 additions & 28 deletions src/executor/types.ts

This file was deleted.

Loading

0 comments on commit a5fc67b

Please sign in to comment.