Skip to content

Commit

Permalink
Merge pull request #228 from pimlicolabs/fix/false-internal-error
Browse files Browse the repository at this point in the history
capture reverts from compressed ops
  • Loading branch information
mouseless0x authored May 30, 2024
2 parents 22f6f0d + a1f66a2 commit 57219d3
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 35 deletions.
19 changes: 15 additions & 4 deletions src/executor/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
} from "@alto/types"
import type { Logger } from "@alto/utils"
import {
getRevertErrorData,
isVersion06,
parseViemError,
toPackedUserOperation,
Expand Down Expand Up @@ -207,6 +208,7 @@ export async function filterOpsAndEstimateGas(
} catch (err: unknown) {
logger.error({ err }, "error estimating gas")
const e = parseViemError(err)

if (e instanceof ContractFunctionRevertedError) {
const failedOpError = failedOpErrorSchema.safeParse(e.data)
const failedOpWithRevertError =
Expand Down Expand Up @@ -266,8 +268,11 @@ export async function filterOpsAndEstimateGas(
resubmitAllOps: false
}
}
} else if (e instanceof EstimateGasExecutionError) {
if (e.cause instanceof FeeCapTooLowError) {
} else if (
e instanceof EstimateGasExecutionError ||
err instanceof EstimateGasExecutionError
) {
if (e?.cause instanceof FeeCapTooLowError) {
logger.info(
{ error: e.shortMessage },
"error estimating gas due to max fee < basefee"
Expand All @@ -280,7 +285,13 @@ export async function filterOpsAndEstimateGas(
}

try {
const errorHexData = e.details.split("Reverted ")[1] as Hex
let errorHexData: Hex = "0x"

if (err instanceof EstimateGasExecutionError) {
errorHexData = getRevertErrorData(err) as Hex
} else {
errorHexData = e?.details.split("Reverted ")[1] as Hex
}
const errorResult = decodeErrorResult({
abi: isUserOpV06 ? EntryPointV06Abi : EntryPointV07Abi,
data: errorHexData
Expand Down Expand Up @@ -316,7 +327,7 @@ export async function filterOpsAndEstimateGas(
)[Number(errorResult.args[0])]

failingOp.reason = errorResult.args[1]
} catch (_e: unknown) {
} catch (e: unknown) {
logger.error(
{ error: JSON.stringify(err) },
"failed to parse error result"
Expand Down
14 changes: 13 additions & 1 deletion src/utils/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
import { type Address, getAddress } from "viem"
import {
type Address,
getAddress,
BaseError,
type RawContractError
} from "viem"

/// Ensure proper equality by converting both addresses into their checksum type
export const areAddressesEqual = (a: Address, b: Address) => {
return getAddress(a) === getAddress(b)
}

export function getRevertErrorData(err: unknown) {
// biome-ignore lint/style/useBlockStatements:
if (!(err instanceof BaseError)) return undefined
const error = err.walk() as RawContractError
return typeof error?.data === "object" ? error.data?.data : error.data
}
63 changes: 34 additions & 29 deletions test/kinto-e2e/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import {
type Address,
type Hex,
createPublicClient,
decodeEventLog,
decodeFunctionData,
http,
parseAbi,
parseAbiItem,
getAddress
slice,
hexToNumber
} from "viem"
import { BundleBulkerAbi, handleOpsAbi } from "./abi"
import { handleOpsAbi } from "./abi"
import { type Pool, createPool } from "@viem/anvil"
import type { UserOperation } from "permissionless"
import { createPimlicoBundlerClient } from "permissionless/clients/pimlico"
Expand All @@ -18,22 +18,13 @@ import {
KINTO_ENTRYPOINT,
kintoMainnet,
prettyPrintTxHash,
sleep
sleep,
type OpInfoType,
type CompressedOp,
isCompressed
} from "./utils"
import { startAlto } from "./setupAlto"

type CompressedOp = {
compressedBytes: Hex
inflator: Address
}

type OpInfoType = {
opHash: Hex
txHash: Hex
blockNum: bigint
opParams: UserOperation | CompressedOp
}

const canReplayUserOperation = async ({
anvilPool,
anvilId,
Expand Down Expand Up @@ -65,7 +56,7 @@ const canReplayUserOperation = async ({
})

let hash: Hex
if ("inflator" in opParams) {
if (isCompressed(opParams)) {
hash = await bundlerClient.sendCompressedUserOperation({
compressedUserOperation: opParams.compressedBytes,
inflatorAddress: opParams.inflator,
Expand Down Expand Up @@ -122,7 +113,8 @@ const main = async () => {
let userOperationEvents = await publicClient.getLogs({
address: KINTO_ENTRYPOINT,
event: parseAbiItem(userOperationEventAbi),
fromBlock: latestBlock - 10_000n
fromBlock: latestBlock - 10_000n,
toBlock: latestBlock
})
userOperationEvents = userOperationEvents.reverse()

Expand Down Expand Up @@ -163,15 +155,24 @@ const main = async () => {
data: rawTx.input
}).args[0][0]
} catch {
const compressedBytes = decodeFunctionData({
abi: BundleBulkerAbi,
data: rawTx.input
}).args[0]
// Extract first compressedUserOperation (compressedBytes)
// slice of 9 bytes:
// - 4 Bytes BundleBulker Payload (PerOpInflator Id)
// - 1 Bytes PerOpInflator Payload (number of ops)
// - 4 Bytes PerOpInflator Payload (inflator id)
const bytes = slice(rawTx.input, 9, undefined)

const compressedLength = hexToNumber(slice(bytes, 0, 2))

const compressedBytes = slice(
bytes,
2,
2 + compressedLength
)

opParams = {
compressedBytes,
inflator: getAddress(
"0x336a76a7A2a1e97CE20c420F39FC08c441234aa2"
)
inflator: "0x336a76a7A2a1e97CE20c420F39FC08c441234aa2"
}
}

Expand Down Expand Up @@ -229,7 +230,7 @@ const main = async () => {
const endTime = performance.now()
const elapsedTime = (endTime - startTime) / 1000

// biome-ignore lint/suspicious/noConsoleLog: <explanation>
// biome-ignore lint/suspicious/noConsoleLog:
console.log(
`Processed ${processed}/${totalOps} operations. (processed in ${elapsedTime.toFixed(
2
Expand All @@ -240,11 +241,15 @@ const main = async () => {
// if any ops failed, print them and exit with 1
if (failedOps.length > 0) {
for (const f of failedOps) {
let opType = "uncompressed"
if (isCompressed(f.opParams)) {
opType = "compressed"
}
// biome-ignore lint/suspicious/noConsoleLog:
console.log(
`FAILED OP: ${f.opHash} (txhash: ${prettyPrintTxHash(
f.txHash
)})`
`[${opType}] FAILED OP: ${
f.opHash
} (txhash: ${prettyPrintTxHash(f.txHash)})`
)
}
process.exit(1)
Expand Down
21 changes: 20 additions & 1 deletion test/kinto-e2e/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,28 @@
import { defineChain, type Hash } from "viem"
import type { UserOperation } from "permissionless"
import { defineChain, type Hex, type Hash, type Address } from "viem"

export const prettyPrintTxHash = (hash: Hash) => {
return `https://kintoscan.io/tx/${hash}`
}

export type CompressedOp = {
compressedBytes: Hex
inflator: Address
}

export type OpInfoType = {
opHash: Hex
txHash: Hex
blockNum: bigint
opParams: UserOperation | CompressedOp
}

export const isCompressed = (
op: UserOperation | CompressedOp
): op is CompressedOp => {
return "inflator" in op
}

export const kintoMainnet = defineChain({
id: 7887,
name: "Kinto Mainnet",
Expand Down

0 comments on commit 57219d3

Please sign in to comment.