diff --git a/src/error.ts b/src/error.ts index fa286be2..628efade 100644 --- a/src/error.ts +++ b/src/error.ts @@ -1,5 +1,4 @@ /* eslint-disable @typescript-eslint/ban-ts-comment */ -import { BigNumber } from "ethers"; import { ViemClient } from "./types"; // @ts-ignore import { abi as obAbi } from "../test/abis/OrderBook.json"; @@ -12,7 +11,6 @@ import { abi as genericArbAbi } from "../test/abis/GenericPoolOrderBookV4ArbOrde import { isHex, BaseError, - isAddress, RpcRequestError, decodeErrorResult, ExecutionRevertedError, @@ -30,17 +28,26 @@ export enum ErrorSeverity { HIGH = "HIGH", } +/** + * Specifies a decoded contract error + */ export type DecodedError = { name: string; args: string[]; }; +/** + * Raw error returned from rpc call + */ export type RawError = { code: number; message: string; data?: string; }; +/** + * Represents a revert error that happened for a transaction + */ export type TxRevertError = { raw: RawError; decoded?: DecodedError; @@ -106,6 +113,9 @@ export function containsNodeError(err: BaseError): boolean { } } +/** + * Handles a reverted transaction by simulating it and returning the revert error + */ export async function handleRevert( viemClient: ViemClient, hash: `0x${string}`, @@ -130,6 +140,9 @@ export async function handleRevert( } } +/** + * Parses a revert error to TxRevertError type + */ export function parseRevertError(error: BaseError): TxRevertError { if ("cause" in error) { return parseRevertError(error.cause as any); @@ -147,29 +160,22 @@ export function parseRevertError(error: BaseError): TxRevertError { } } +/** + * Tries to decode an error data with known contract error selectors + */ export function tryDecodeError(data: `0x${string}`): DecodedError | undefined { const handleArgs = (args: readonly unknown[]): string[] => { return ( args?.map((arg) => { if (typeof arg === "string") { return arg; - } - if (typeof arg === "bigint") { - const str = BigNumber.from(arg).toHexString(); - if (isAddress(str)) { - return str; - } else { - return arg.toString(); + } else { + try { + return arg!.toString(); + } catch (error) { + return ""; } } - if (typeof arg === "number") { - return arg.toString(); - } - try { - return arg!.toString(); - } catch (error) { - return ""; - } }) ?? [] ); }; diff --git a/src/processOrders.ts b/src/processOrders.ts index bae240ee..e2654e67 100644 --- a/src/processOrders.ts +++ b/src/processOrders.ts @@ -331,8 +331,12 @@ export const processOrders = async ( message = errorSnapshot(message, e.error); span.setAttribute("errorDetails", message); } - span.setAttribute("severity", ErrorSeverity.HIGH); - span.setStatus({ code: SpanStatusCode.ERROR, message }); + if (e.spanAttributes["txNoneNodeError"]) { + span.setAttribute("severity", ErrorSeverity.HIGH); + span.setStatus({ code: SpanStatusCode.ERROR, message }); + } else { + span.setStatus({ code: SpanStatusCode.OK, message }); + } span.setAttribute("unsuccessfulClear", true); span.setAttribute("txReverted", true); } else if (e.reason === ProcessPairHaltReason.TxMineFailed) { @@ -779,7 +783,7 @@ export async function processPair(args: { return result; } else { // keep track of gas consumption of the account - const simulation = await handleRevert(config.viemClient as any, txhash); + const simulation = await handleRevert(viemClient as any, txhash); if (simulation) { result.error = simulation.err; spanAttributes["txNoneNodeError"] = !simulation.nodeError; diff --git a/test/error.test.js b/test/error.test.js new file mode 100644 index 00000000..633c0c34 --- /dev/null +++ b/test/error.test.js @@ -0,0 +1,34 @@ +const { assert } = require("chai"); +const { BaseError } = require("viem"); +const { tryDecodeError, parseRevertError } = require("../src/error"); + +describe("Test error", async function () { + const data = "0x963b34a500000000000000000000000000000000000000000000000340bda9d7e155feb0"; + + it("should decode the error data", async function () { + const result = tryDecodeError(data); + const expected = { + name: "MinimalOutputBalanceViolation", + args: ["60005303754817928880"], + }; + assert.deepEqual(result, expected); + }); + + it("should parse viem revert error", async function () { + const rawError = { + code: -3, + message: "some msg", + data, + }; + const error = new BaseError("some msg", { cause: rawError }); + const result = parseRevertError(error); + const expected = { + raw: rawError, + decoded: { + name: "MinimalOutputBalanceViolation", + args: ["60005303754817928880"], + }, + }; + assert.deepEqual(result, expected); + }); +}); diff --git a/test/processPair.test.js b/test/processPair.test.js index a65de918..9a22c100 100644 --- a/test/processPair.test.js +++ b/test/processPair.test.js @@ -583,6 +583,8 @@ describe("Test process pair", async function () { }; signer.sendTransaction = async () => txHash; viemClient.waitForTransactionReceipt = async () => errorReceipt; + viemClient.getTransaction = async () => ({}); + viemClient.call = async () => Promise.reject("out of gas"); try { await processPair({ config, @@ -610,7 +612,7 @@ describe("Test process pair", async function () { actualGasCost: formatUnits(effectiveGasPrice.mul(gasUsed)), }, reason: ProcessPairHaltReason.TxReverted, - error: undefined, + error: "out of gas", gasCost: undefined, spanAttributes: { "details.pair": pair, @@ -630,6 +632,7 @@ describe("Test process pair", async function () { "details.marketQuote.num": 0.99699, "details.marketQuote.str": "0.99699", "details.clearModePick": "rp4", + txNoneNodeError: true, "details.quote": JSON.stringify({ maxOutput: formatUnits(vaultBalance), ratio: formatUnits(ethers.constants.Zero),