fix: improve postman logging and error parsing (#622)

* fix: improve postman logging and error parsing

* fix: remove unnecessary casting
This commit is contained in:
Victorien Gauch
2025-01-29 17:12:01 +01:00
committed by GitHub
parent c819e319cc
commit 317ef4f06e
30 changed files with 548 additions and 339 deletions

View File

@@ -15,6 +15,8 @@ import {
L2ClaimMessageTransactionSizeProcessorConfig,
} from "../../core/services/processors/IL2ClaimMessageTransactionSizeProcessor";
import { IL2ClaimTransactionSizeCalculator } from "../../core/services/processors/IL2ClaimTransactionSizeCalculator";
import { ErrorParser } from "../../utils/ErrorParser";
import { Message } from "../../core/entities/Message";
export class L2ClaimMessageTransactionSizeProcessor implements IL2ClaimMessageTransactionSizeProcessor {
/**
@@ -48,6 +50,8 @@ export class L2ClaimMessageTransactionSizeProcessor implements IL2ClaimMessageTr
* @returns {Promise<void>} A promise that resolves when the processing is complete.
*/
public async process(): Promise<void> {
let message: Message | null = null;
try {
const messages = await this.databaseService.getNFirstMessagesByStatus(
MessageStatus.ANCHORED,
@@ -57,10 +61,11 @@ export class L2ClaimMessageTransactionSizeProcessor implements IL2ClaimMessageTr
);
if (messages.length === 0) {
this.logger.info("No anchored messages found to compute transaction size.");
return;
}
const message = messages[0];
message = messages[0];
const { gasLimit, maxPriorityFeePerGas, maxFeePerGas } =
await this.l2MessageServiceClient.estimateClaimGasFees(message);
@@ -86,7 +91,32 @@ export class L2ClaimMessageTransactionSizeProcessor implements IL2ClaimMessageTr
gasLimit,
);
} catch (e) {
this.logger.error(e);
await this.handleProcessingError(e, message);
}
}
/**
* Handles error that occur during the processing.
*
* @param {unknown} e - The error that occurred.
* @param {Message | null} message - The message object being processed when the error occurred.
* @returns {Promise<void>} A promise that resolves when the error has been handled.
*/
private async handleProcessingError(e: unknown, message: Message | null): Promise<void> {
const parsedError = ErrorParser.parseErrorWithMitigation(e);
if (parsedError?.mitigation && !parsedError.mitigation.shouldRetry && message) {
message.edit({ status: MessageStatus.NON_EXECUTABLE });
await this.databaseService.updateMessage(message);
this.logger.warnOrError("Error occurred while processing message transaction size.", {
...parsedError,
messageHash: message.messageHash,
});
return;
}
this.logger.warnOrError("Error occurred while processing message transaction size.", {
parsedError,
});
}
}

View File

@@ -18,6 +18,7 @@ import { MessageStatus } from "../../core/enums";
import { ILogger } from "../../core/utils/logging/ILogger";
import { IMessageServiceContract } from "../../core/services/contracts/IMessageServiceContract";
import { IMessageDBService } from "../../core/persistence/IMessageDBService";
import { ErrorParser } from "../../utils/ErrorParser";
export class MessageAnchoringProcessor implements IMessageAnchoringProcessor {
private readonly maxFetchMessagesFromDb: number;
@@ -93,7 +94,12 @@ export class MessageAnchoringProcessor implements IMessageAnchoringProcessor {
await this.databaseService.saveMessages(messages);
} catch (e) {
this.logger.error(e);
const error = ErrorParser.parseErrorWithMitigation(e);
this.logger.error("An error occurred while processing messages.", {
errorCode: error?.errorCode,
errorMessage: error?.errorMessage,
...(error?.data ? { data: error.data } : {}),
});
}
}
}

View File

@@ -20,6 +20,7 @@ import {
MessageClaimingPersisterConfig,
} from "../../core/services/processors/IMessageClaimingPersister";
import { IMessageDBService } from "../../core/persistence/IMessageDBService";
import { ErrorParser } from "../../utils/ErrorParser";
export class MessageClaimingPersister implements IMessageClaimingPersister {
private messageBeingRetry: { message: Message | null; retries: number };
@@ -79,6 +80,7 @@ export class MessageClaimingPersister implements IMessageClaimingPersister {
try {
firstPendingMessage = await this.databaseService.getFirstPendingMessage(this.config.direction);
if (!firstPendingMessage?.claimTxHash) {
this.logger.info("No pending message status to update.");
return;
}
@@ -111,7 +113,13 @@ export class MessageClaimingPersister implements IMessageClaimingPersister {
await this.updateReceiptStatus(firstPendingMessage, receipt);
} catch (e) {
this.logger.error(e);
const error = ErrorParser.parseErrorWithMitigation(e);
this.logger.error("Error processing message.", {
...(firstPendingMessage ? { messageHash: firstPendingMessage.messageHash } : {}),
...(error?.errorCode ? { errorCode: error.errorCode } : {}),
...(error?.errorMessage ? { errorMessage: error.errorMessage } : {}),
...(error?.data ? { data: error.data } : {}),
});
}
}

View File

@@ -3,7 +3,6 @@ import {
Overrides,
TransactionResponse,
ContractTransactionResponse,
EthersError,
TransactionReceipt,
Signer,
ErrorDescription,
@@ -74,6 +73,7 @@ export class MessageClaimingProcessor implements IMessageClaimingProcessor {
);
if (!nextMessageToClaim) {
this.logger.info("No message to claim found");
return;
}
@@ -265,7 +265,7 @@ export class MessageClaimingProcessor implements IMessageClaimingProcessor {
* @returns {Promise<void>} A promise that resolves when the error has been handled.
*/
private async handleProcessingError(e: unknown, message: Message | null): Promise<void> {
const parsedError = ErrorParser.parseErrorWithMitigation(e as EthersError);
const parsedError = ErrorParser.parseErrorWithMitigation(e);
if (parsedError?.mitigation && !parsedError.mitigation.shouldRetry && message) {
message.edit({ status: MessageStatus.NON_EXECUTABLE });
@@ -274,6 +274,7 @@ export class MessageClaimingProcessor implements IMessageClaimingProcessor {
this.logger.warnOrError(e, {
parsedError,
...(message ? { messageHash: message.messageHash } : {}),
});
}
}

View File

@@ -3,12 +3,13 @@ import { mock } from "jest-mock-extended";
import {
ContractTransactionResponse,
ErrorDescription,
makeError,
Overrides,
Signer,
TransactionReceipt,
TransactionResponse,
} from "ethers";
import { Direction } from "@consensys/linea-sdk";
import { Direction, makeBaseError } from "@consensys/linea-sdk";
import { TestLogger } from "../../../utils/testing/helpers";
import { MessageStatus } from "../../../core/enums";
import { testL1NetworkConfig, testMessage, DEFAULT_MAX_FEE_PER_GAS } from "../../../utils/testing/constants";
@@ -67,7 +68,7 @@ describe("L2ClaimMessageTransactionSizeProcessor", () => {
it("Should log as error when calculateTransactionSize failed", async () => {
const testGasLimit = 50_000n;
const loggerErrorSpy = jest.spyOn(logger, "error");
const loggerErrorSpy = jest.spyOn(logger, "warnOrError");
jest.spyOn(databaseService, "getNFirstMessagesByStatus").mockResolvedValue([testMessage]);
jest.spyOn(l2ContractClientMock, "estimateClaimGasFees").mockResolvedValue({
gasLimit: testGasLimit,
@@ -81,7 +82,53 @@ describe("L2ClaimMessageTransactionSizeProcessor", () => {
await transactionSizeProcessor.process();
expect(loggerErrorSpy).toHaveBeenCalledTimes(1);
expect(loggerErrorSpy).toHaveBeenCalledWith(new Error("calculation failed."));
expect(loggerErrorSpy).toHaveBeenCalledWith("Error occurred while processing message transaction size.", {
errorCode: "UNKNOWN_ERROR",
errorMessage: "calculation failed.",
messageHash: testMessage.messageHash,
mitigation: { shouldRetry: false },
});
});
it("Should log as error when estimateClaimGasFees failed", async () => {
const loggerErrorSpy = jest.spyOn(logger, "warnOrError");
jest.spyOn(databaseService, "getNFirstMessagesByStatus").mockResolvedValue([testMessage]);
jest.spyOn(l2ContractClientMock, "estimateClaimGasFees").mockRejectedValue(
makeBaseError(
makeError("could not coalesce error", "UNKNOWN_ERROR", {
error: {
code: -32000,
data: "0x5461344300000000000000000000000034be5b8c30ee4fde069dc878989686abe9884470",
message: "Execution reverted",
},
payload: {
id: 1,
jsonrpc: "2.0",
method: "linea_estimateGas",
params: [
{
data: "0x491e09360000000000000000000000004420ce157f2c39edaae6cc107a42c8e527d6e02800000000000000000000000034be5b8c30ee4fde069dc878989686abe988447000000000000000000000000000000000000000000000000000006182ba2f0b400000000000000000000000000000000000000000000000000001c6bf52634000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000052b130000000000000000000000000000000000000000000000000000000000000000",
from: "0x46eA7a855DA88FBC09cc59de93468E6bFbf0d81b",
to: "0x508Ca82Df566dCD1B0DE8296e70a96332cD644ec",
value: "0",
},
],
},
}),
testMessage,
),
);
await transactionSizeProcessor.process();
expect(loggerErrorSpy).toHaveBeenCalledTimes(1);
expect(loggerErrorSpy).toHaveBeenCalledWith("Error occurred while processing message transaction size.", {
data: "0x5461344300000000000000000000000034be5b8c30ee4fde069dc878989686abe9884470",
errorCode: "UNKNOWN_ERROR",
errorMessage: "Execution reverted",
messageHash: testMessage.messageHash,
mitigation: { shouldRetry: false },
});
});
it("Should log as info and call updateMessage if the transaction size calculation succeed", async () => {

View File

@@ -129,7 +129,10 @@ describe("TestMessageAnchoringProcessor", () => {
await anchoringProcessor.process();
expect(loggerErrorSpy).toHaveBeenCalledTimes(1);
expect(loggerErrorSpy).toHaveBeenCalledWith(error);
expect(loggerErrorSpy).toHaveBeenCalledWith("An error occurred while processing messages.", {
errorCode: "UNKNOWN_ERROR",
errorMessage: error.message,
});
expect(messageRepositoryMockSaveSpy).toHaveBeenCalledTimes(0);
});
});

View File

@@ -82,7 +82,11 @@ describe("TestMessageClaimingPersister ", () => {
await messageClaimingPersister.process();
expect(loggerErrorSpy).toHaveBeenCalledTimes(1);
expect(loggerErrorSpy).toHaveBeenCalledWith(getTxReceiptError);
expect(loggerErrorSpy).toHaveBeenCalledWith("Error processing message.", {
messageHash: testPendingMessage.messageHash,
errorCode: "UNKNOWN_ERROR",
errorMessage: getTxReceiptError.message,
});
});
it("Should log as info and update message as claimed success if successful", async () => {

View File

@@ -370,6 +370,7 @@ describe("TestMessageClaimingProcessor", () => {
expect(loggerWarnOrErrorSpy).toHaveBeenCalledTimes(1);
expect(loggerWarnOrErrorSpy).toHaveBeenCalledWith(actionRejectedError, {
parsedError: ErrorParser.parseErrorWithMitigation(actionRejectedError as EthersError),
messageHash: expectedLoggingMessage.messageHash,
});
});
});

View File

@@ -1,5 +1,5 @@
import { EthersError, ErrorCode } from "ethers";
import { GasEstimationError } from "@consensys/linea-sdk";
import { EthersError, ErrorCode, isError } from "ethers";
import { isBaseError } from "@consensys/linea-sdk";
import { DatabaseAccessError } from "../core/errors/DatabaseErrors";
import { MessageProps } from "../core/entities/Message";
@@ -12,8 +12,8 @@ export type Mitigation = {
export type ParsedErrorResult = {
errorCode: ErrorCode;
context?: string;
reason?: string;
errorMessage?: string;
data?: string;
mitigation: Mitigation;
};
@@ -26,82 +26,140 @@ export class ErrorParser {
* @param {EthersError} error - The error encountered during Ethereum operations.
* @returns {ParsedErrorResult | null} An object containing the parsed error result and mitigation strategies, or `null` if the error is `undefined`.
*/
public static parseErrorWithMitigation(error: EthersError): ParsedErrorResult | null {
public static parseErrorWithMitigation(error: unknown): ParsedErrorResult | null {
if (!error) {
return null;
}
const parsedErrResult: ParsedErrorResult = {
errorCode: "UNKNOWN_ERROR",
mitigation: { shouldRetry: false },
};
switch (error.code) {
case "NETWORK_ERROR":
case "SERVER_ERROR":
case "TIMEOUT":
case "INSUFFICIENT_FUNDS":
case "REPLACEMENT_UNDERPRICED":
case "NONCE_EXPIRED":
parsedErrResult.context = error.shortMessage;
parsedErrResult.mitigation = {
shouldRetry: true,
if (!isBaseError(error)) {
if (error instanceof DatabaseAccessError) {
return {
errorCode: "UNKNOWN_ERROR",
errorMessage: (error as DatabaseAccessError<MessageProps>).message,
mitigation: { shouldRetry: true },
};
break;
case "CALL_EXCEPTION":
if (
error.shortMessage.includes("execution reverted") ||
error.info?.error?.code === 4001 || //The user rejected the request (EIP-1193)
error.info?.error?.code === -32603 //Internal JSON-RPC error (EIP-1474)
) {
parsedErrResult.context = error.info?.error?.message ?? error.shortMessage;
break;
}
}
if (
error.info?.error?.code === -32000 && //Missing or invalid parameters (EIP-1474)
(error.info?.error?.message.includes("gas required exceeds allowance (0)") ||
error.info?.error?.message.includes("max priority fee per gas higher than max fee per gas") ||
error.info?.error?.message.includes("max fee per gas less than block base fee"))
) {
parsedErrResult.context = error.info?.error?.message;
parsedErrResult.mitigation = {
return {
errorCode: "UNKNOWN_ERROR",
errorMessage: error instanceof Error ? error.message : String(error),
mitigation: { shouldRetry: false },
};
}
if (!this.isEthersError(error.error)) {
return {
errorCode: "UNKNOWN_ERROR",
errorMessage: error instanceof Error ? error.message : String(error),
mitigation: { shouldRetry: false },
};
}
return this.parseEthersError(error.error);
}
private static isEthersError(error: unknown): error is EthersError {
return (error as EthersError).shortMessage !== undefined || (error as EthersError).code !== undefined;
}
public static parseEthersError(error: EthersError): ParsedErrorResult {
if (
isError(error, "NETWORK_ERROR") ||
isError(error, "SERVER_ERROR") ||
isError(error, "TIMEOUT") ||
isError(error, "INSUFFICIENT_FUNDS") ||
isError(error, "REPLACEMENT_UNDERPRICED") ||
isError(error, "NONCE_EXPIRED")
) {
return {
errorCode: error.code,
errorMessage: error.message,
mitigation: {
shouldRetry: true,
},
};
}
if (isError(error, "CALL_EXCEPTION")) {
if (
error.shortMessage.includes("execution reverted") ||
error.info?.error?.code === 4001 || //The user rejected the request (EIP-1193)
error.info?.error?.code === -32603 //Internal JSON-RPC error (EIP-1474)
) {
return {
errorCode: error.code,
errorMessage: error.info?.error?.message ?? error.shortMessage,
mitigation: {
shouldRetry: false,
},
};
}
if (
error.info?.error?.code === -32000 && //Missing or invalid parameters (EIP-1474)
(error.info?.error?.message.includes("gas required exceeds allowance (0)") ||
error.info?.error?.message.includes("max priority fee per gas higher than max fee per gas") ||
error.info?.error?.message.includes("max fee per gas less than block base fee"))
) {
return {
errorCode: error.code,
errorMessage: error.info?.error?.message ?? error.shortMessage,
mitigation: {
shouldRetry: true,
};
break;
}
parsedErrResult.context = error.shortMessage;
parsedErrResult.mitigation = {
shouldRetry: true,
},
};
break;
case "ACTION_REJECTED":
case "UNKNOWN_ERROR":
parsedErrResult.context = error.shortMessage;
break;
default:
if (error instanceof GasEstimationError) {
parsedErrResult.context = (error as GasEstimationError<MessageProps>).message;
break;
}
}
if (error instanceof DatabaseAccessError) {
parsedErrResult.context = (error as DatabaseAccessError<MessageProps>).message;
} else {
parsedErrResult.context = error.message;
}
parsedErrResult.context = error.shortMessage ?? error.message;
parsedErrResult.mitigation = {
return {
errorCode: error.code,
errorMessage: error.shortMessage,
mitigation: {
shouldRetry: true,
},
};
}
if (isError(error, "ACTION_REJECTED")) {
return {
errorCode: error.code,
errorMessage: error.info?.error?.message ?? error.shortMessage,
mitigation: {
shouldRetry: false,
},
};
}
if (isError(error, "UNKNOWN_ERROR")) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
if (error.error?.code === -32000 && error.error?.message?.toLowerCase().includes("execution reverted")) {
return {
errorCode: error.code,
errorMessage: error.error?.message ?? error.shortMessage,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
data: error.error?.data,
mitigation: {
shouldRetry: false,
},
};
break;
}
return {
errorCode: error.code,
errorMessage: error.shortMessage,
mitigation: {
shouldRetry: false,
},
};
}
return {
...parsedErrResult,
errorCode: error.code || parsedErrResult.errorCode,
errorCode: error.code,
errorMessage: error.shortMessage ?? error.message,
mitigation: {
shouldRetry: true,
},
};
}
}

View File

@@ -1,6 +1,6 @@
import { describe, it, expect } from "@jest/globals";
import { ErrorCode, EthersError } from "ethers";
import { GasEstimationError } from "@consensys/linea-sdk";
import { ErrorCode, makeError } from "ethers";
import { makeBaseError } from "@consensys/linea-sdk";
import { ErrorParser } from "../ErrorParser";
import { DatabaseAccessError } from "../../core/errors";
import { DatabaseErrorType, DatabaseRepoName } from "../../core/enums";
@@ -9,17 +9,36 @@ import { generateMessage } from "../testing/helpers";
describe("ErrorParser", () => {
describe("parseErrorWithMitigation", () => {
it("should return null when error is null", () => {
expect(ErrorParser.parseErrorWithMitigation(null as unknown as EthersError)).toStrictEqual(null);
expect(ErrorParser.parseErrorWithMitigation(null)).toStrictEqual(null);
});
it("should return NETWORK_ERROR and shouldRetry = true when error code = NETWORK_ERROR", () => {
const errorMessage = {
code: "NETWORK_ERROR",
shortMessage: "any reason",
};
it("should return UNKNOWN_ERROR and shouldRetry = false when error is not instance of BaseError", () => {
expect(ErrorParser.parseErrorWithMitigation(new Error("any reason"))).toStrictEqual({
errorMessage: "any reason",
errorCode: "UNKNOWN_ERROR",
mitigation: {
shouldRetry: false,
},
});
});
expect(ErrorParser.parseErrorWithMitigation(errorMessage as unknown as EthersError)).toStrictEqual({
context: "any reason",
it("should return UNKNOWN_ERROR and shouldRetry = false when error is instance of BaseError but the underlying error is not an EthersError", () => {
expect(ErrorParser.parseErrorWithMitigation(makeBaseError(new Error("any reason")))).toStrictEqual({
errorMessage: "any reason",
errorCode: "UNKNOWN_ERROR",
mitigation: {
shouldRetry: false,
},
});
});
});
describe("parseEthersError", () => {
it("should return NETWORK_ERROR and shouldRetry = true when error code = NETWORK_ERROR", () => {
const error = makeError("any reason", "NETWORK_ERROR");
expect(ErrorParser.parseEthersError(error)).toStrictEqual({
errorMessage: "any reason (code=NETWORK_ERROR, version=6.13.4)",
errorCode: "NETWORK_ERROR",
mitigation: {
shouldRetry: true,
@@ -28,13 +47,10 @@ describe("ErrorParser", () => {
});
it("should return SERVER_ERROR and shouldRetry = true when error code = SERVER_ERROR", () => {
const errorMessage = {
code: "SERVER_ERROR",
shortMessage: "any reason",
};
const error = makeError("any reason", "SERVER_ERROR");
expect(ErrorParser.parseErrorWithMitigation(errorMessage as unknown as EthersError)).toStrictEqual({
context: "any reason",
expect(ErrorParser.parseEthersError(error)).toStrictEqual({
errorMessage: "any reason (code=SERVER_ERROR, version=6.13.4)",
errorCode: "SERVER_ERROR",
mitigation: {
shouldRetry: true,
@@ -43,13 +59,10 @@ describe("ErrorParser", () => {
});
it("should return TIMEOUT and shouldRetry = true when error code = TIMEOUT", () => {
const errorMessage = {
code: "TIMEOUT",
shortMessage: "any reason",
};
const error = makeError("any reason", "TIMEOUT");
expect(ErrorParser.parseErrorWithMitigation(errorMessage as unknown as EthersError)).toStrictEqual({
context: "any reason",
expect(ErrorParser.parseEthersError(error)).toStrictEqual({
errorMessage: "any reason (code=TIMEOUT, version=6.13.4)",
errorCode: "TIMEOUT",
mitigation: {
shouldRetry: true,
@@ -58,13 +71,10 @@ describe("ErrorParser", () => {
});
it("should return INSUFFICIENT_FUNDS and shouldRetry = true when error code = INSUFFICIENT_FUNDS", () => {
const errorMessage = {
code: "INSUFFICIENT_FUNDS",
shortMessage: "any reason",
};
const error = makeError("any reason", "INSUFFICIENT_FUNDS");
expect(ErrorParser.parseErrorWithMitigation(errorMessage as unknown as EthersError)).toStrictEqual({
context: "any reason",
expect(ErrorParser.parseEthersError(error)).toStrictEqual({
errorMessage: "any reason (code=INSUFFICIENT_FUNDS, version=6.13.4)",
errorCode: "INSUFFICIENT_FUNDS",
mitigation: {
shouldRetry: true,
@@ -72,14 +82,11 @@ describe("ErrorParser", () => {
});
});
it("should return REPLACEMENT_UNDERPRICED and shouldRetry = true when error code = NETWOREPLACEMENT_UNDERPRICEDK_ERROR", () => {
const errorMessage = {
code: "REPLACEMENT_UNDERPRICED",
shortMessage: "any reason",
};
it("should return REPLACEMENT_UNDERPRICED and shouldRetry = true when error code = REPLACEMENT_UNDERPRICED", () => {
const error = makeError("any reason", "REPLACEMENT_UNDERPRICED");
expect(ErrorParser.parseErrorWithMitigation(errorMessage as unknown as EthersError)).toStrictEqual({
context: "any reason",
expect(ErrorParser.parseEthersError(error)).toStrictEqual({
errorMessage: "any reason (code=REPLACEMENT_UNDERPRICED, version=6.13.4)",
errorCode: "REPLACEMENT_UNDERPRICED",
mitigation: {
shouldRetry: true,
@@ -88,13 +95,10 @@ describe("ErrorParser", () => {
});
it("should return NONCE_EXPIRED and shouldRetry = true when error code = NONCE_EXPIRED", () => {
const errorMessage = {
code: "NONCE_EXPIRED",
shortMessage: "any reason",
};
const error = makeError("any reason", "NONCE_EXPIRED");
expect(ErrorParser.parseErrorWithMitigation(errorMessage as unknown as EthersError)).toStrictEqual({
context: "any reason",
expect(ErrorParser.parseEthersError(error)).toStrictEqual({
errorMessage: "any reason (code=NONCE_EXPIRED, version=6.13.4)",
errorCode: "NONCE_EXPIRED",
mitigation: {
shouldRetry: true,
@@ -103,13 +107,10 @@ describe("ErrorParser", () => {
});
it("should return CALL_EXCEPTION and shouldRetry = true when error code = CALL_EXCEPTION", () => {
const errorMessage = {
shortMessage: "any reason",
code: "CALL_EXCEPTION",
};
const error = makeError("any reason", "CALL_EXCEPTION");
expect(ErrorParser.parseErrorWithMitigation(errorMessage as unknown as EthersError)).toStrictEqual({
context: "any reason",
expect(ErrorParser.parseEthersError(error)).toStrictEqual({
errorMessage: "any reason",
errorCode: "CALL_EXCEPTION",
mitigation: {
shouldRetry: true,
@@ -118,18 +119,26 @@ describe("ErrorParser", () => {
});
it("should return CALL_EXCEPTION and shouldRetry = false when error code = CALL_EXCEPTION with short message as execution reverted", () => {
const errorMessage = {
shortMessage: "execution reverted",
code: "CALL_EXCEPTION",
const error = makeError("execution reverted", "CALL_EXCEPTION", {
info: {
error: {
message: "execution reverted for some reason",
},
},
};
reason: "execution reverted",
action: "call",
data: "0x0123456789abcdef",
transaction: {
to: null,
from: undefined,
data: "",
},
invocation: null,
revert: null,
});
expect(ErrorParser.parseErrorWithMitigation(errorMessage as unknown as EthersError)).toStrictEqual({
context: "execution reverted for some reason",
expect(ErrorParser.parseEthersError(error)).toStrictEqual({
errorMessage: "execution reverted for some reason",
errorCode: "CALL_EXCEPTION",
mitigation: {
shouldRetry: false,
@@ -138,19 +147,27 @@ describe("ErrorParser", () => {
});
it("should return CALL_EXCEPTION and shouldRetry = false when error code = CALL_EXCEPTION with inner error code as 4001", () => {
const errorMessage = {
shortMessage: "any reason",
code: "CALL_EXCEPTION",
const error = makeError("any reason", "CALL_EXCEPTION", {
info: {
error: {
message: "execution reverted for some reason",
code: 4001,
},
},
};
action: "call",
data: null,
reason: null,
transaction: {
to: null,
from: undefined,
data: "",
},
invocation: null,
revert: null,
});
expect(ErrorParser.parseErrorWithMitigation(errorMessage as unknown as EthersError)).toStrictEqual({
context: "execution reverted for some reason",
expect(ErrorParser.parseEthersError(error)).toStrictEqual({
errorMessage: "execution reverted for some reason",
errorCode: "CALL_EXCEPTION",
mitigation: {
shouldRetry: false,
@@ -159,19 +176,27 @@ describe("ErrorParser", () => {
});
it("should return CALL_EXCEPTION and shouldRetry = false when error code = CALL_EXCEPTION with inner error code as -32603", () => {
const errorMessage = {
shortMessage: "any reason",
code: "CALL_EXCEPTION",
const error = makeError("any reason", "CALL_EXCEPTION", {
info: {
error: {
message: "execution reverted for some reason",
code: -32603,
},
},
};
action: "call",
data: null,
reason: null,
transaction: {
to: null,
from: undefined,
data: "",
},
invocation: null,
revert: null,
});
expect(ErrorParser.parseErrorWithMitigation(errorMessage as unknown as EthersError)).toStrictEqual({
context: "execution reverted for some reason",
expect(ErrorParser.parseEthersError(error)).toStrictEqual({
errorMessage: "execution reverted for some reason",
errorCode: "CALL_EXCEPTION",
mitigation: {
shouldRetry: false,
@@ -180,19 +205,27 @@ describe("ErrorParser", () => {
});
it("should return CALL_EXCEPTION and shouldRetry = true when error code = CALL_EXCEPTION and inner error code as -32000 and message as gas required exceeds allowance (0)", () => {
const errorMessage = {
shortMessage: "any reason",
code: "CALL_EXCEPTION",
const error = makeError("any reason", "CALL_EXCEPTION", {
info: {
error: {
message: "gas required exceeds allowance (0)",
code: -32000,
},
},
};
action: "call",
data: null,
reason: null,
transaction: {
to: null,
from: undefined,
data: "",
},
invocation: null,
revert: null,
});
expect(ErrorParser.parseErrorWithMitigation(errorMessage as unknown as EthersError)).toStrictEqual({
context: "gas required exceeds allowance (0)",
expect(ErrorParser.parseEthersError(error)).toStrictEqual({
errorMessage: "gas required exceeds allowance (0)",
errorCode: "CALL_EXCEPTION",
mitigation: {
shouldRetry: true,
@@ -201,19 +234,27 @@ describe("ErrorParser", () => {
});
it("should return CALL_EXCEPTION and shouldRetry = true when error code = CALL_EXCEPTION and inner error code as -32000 and message as max priority fee per gas higher", () => {
const errorMessage = {
shortMessage: "any reason",
code: "CALL_EXCEPTION",
const error = makeError("any reason", "CALL_EXCEPTION", {
info: {
error: {
message: "max priority fee per gas higher than max fee per gas",
code: -32000,
},
},
};
action: "call",
data: null,
reason: null,
transaction: {
to: null,
from: undefined,
data: "",
},
invocation: null,
revert: null,
});
expect(ErrorParser.parseErrorWithMitigation(errorMessage as unknown as EthersError)).toStrictEqual({
context: "max priority fee per gas higher than max fee per gas",
expect(ErrorParser.parseEthersError(error)).toStrictEqual({
errorMessage: "max priority fee per gas higher than max fee per gas",
errorCode: "CALL_EXCEPTION",
mitigation: {
shouldRetry: true,
@@ -222,19 +263,27 @@ describe("ErrorParser", () => {
});
it("should return CALL_EXCEPTION and shouldRetry = true when error code = CALL_EXCEPTION and inner error code as -32000 and message as max fee per gas less than block base fee", () => {
const errorMessage = {
shortMessage: "any reason",
code: "CALL_EXCEPTION",
const error = makeError("any reason", "CALL_EXCEPTION", {
info: {
error: {
message: "max fee per gas less than block base fee",
code: -32000,
},
},
};
action: "call",
data: null,
reason: null,
transaction: {
to: null,
from: undefined,
data: "",
},
invocation: null,
revert: null,
});
expect(ErrorParser.parseErrorWithMitigation(errorMessage as unknown as EthersError)).toStrictEqual({
context: "max fee per gas less than block base fee",
expect(ErrorParser.parseEthersError(error)).toStrictEqual({
errorMessage: "max fee per gas less than block base fee",
errorCode: "CALL_EXCEPTION",
mitigation: {
shouldRetry: true,
@@ -243,19 +292,27 @@ describe("ErrorParser", () => {
});
it("should return CALL_EXCEPTION and shouldRetry = true when error code = CALL_EXCEPTION with other inner error", () => {
const errorMessage = {
shortMessage: "any reason",
code: "CALL_EXCEPTION",
const error = makeError("any reason", "CALL_EXCEPTION", {
info: {
error: {
message: "invalid method parameters",
code: -32602,
},
},
};
action: "call",
data: null,
reason: null,
transaction: {
to: null,
from: undefined,
data: "",
},
invocation: null,
revert: null,
});
expect(ErrorParser.parseErrorWithMitigation(errorMessage as unknown as EthersError)).toStrictEqual({
context: "any reason",
expect(ErrorParser.parseEthersError(error)).toStrictEqual({
errorMessage: "any reason",
errorCode: "CALL_EXCEPTION",
mitigation: {
shouldRetry: true,
@@ -264,13 +321,10 @@ describe("ErrorParser", () => {
});
it("should return ACTION_REJECTED and shouldRetry = false when error code = ACTION_REJECTED", () => {
const errorMessage = {
shortMessage: "any reason",
code: "ACTION_REJECTED",
};
const error = makeError("any reason", "ACTION_REJECTED");
expect(ErrorParser.parseErrorWithMitigation(errorMessage as unknown as EthersError)).toStrictEqual({
context: "any reason",
expect(ErrorParser.parseEthersError(error)).toStrictEqual({
errorMessage: "any reason",
errorCode: "ACTION_REJECTED",
mitigation: {
shouldRetry: false,
@@ -279,13 +333,10 @@ describe("ErrorParser", () => {
});
it("should return UNKNOWN_ERROR and shouldRetry = false when error code = UNKNOWN_ERROR", () => {
const errorMessage = {
shortMessage: "any reason",
code: "UNKNOWN_ERROR",
};
const error = makeError("any reason", "UNKNOWN_ERROR");
expect(ErrorParser.parseErrorWithMitigation(errorMessage as unknown as EthersError)).toStrictEqual({
context: "any reason",
expect(ErrorParser.parseEthersError(error)).toStrictEqual({
errorMessage: "any reason",
errorCode: "UNKNOWN_ERROR",
mitigation: {
shouldRetry: false,
@@ -294,10 +345,10 @@ describe("ErrorParser", () => {
});
it("should return UNKNOWN_ERROR and shouldRetry = false when error = GasEstimationError", () => {
const gasEstimationError = new GasEstimationError("Gas estimation failed", generateMessage());
const gasEstimationError = makeBaseError(makeError("Gas estimation failed", "UNKNOWN_ERROR"), generateMessage());
expect(ErrorParser.parseErrorWithMitigation(gasEstimationError as unknown as EthersError)).toStrictEqual({
context: "Gas estimation failed",
expect(ErrorParser.parseErrorWithMitigation(gasEstimationError)).toStrictEqual({
errorMessage: "Gas estimation failed",
errorCode: "UNKNOWN_ERROR",
mitigation: {
shouldRetry: false,
@@ -305,6 +356,25 @@ describe("ErrorParser", () => {
});
});
it("should return UNKNOWN_ERROR and shouldRetry = false when error is execution reverted", () => {
const error = makeError("Gas estimation failed", "UNKNOWN_ERROR", {
error: {
code: -32000,
message: "execution reverted",
data: "0x0123456789abcdef",
},
});
expect(ErrorParser.parseEthersError(error)).toStrictEqual({
errorMessage: "execution reverted",
errorCode: "UNKNOWN_ERROR",
data: "0x0123456789abcdef",
mitigation: {
shouldRetry: false,
},
});
});
it("should return UNKNOWN_ERROR and shouldRetry = false when error = DatabaseAccessError", () => {
const databaseAccessError = new DatabaseAccessError(
DatabaseRepoName.MessageRepository,
@@ -313,8 +383,8 @@ describe("ErrorParser", () => {
generateMessage(),
);
expect(ErrorParser.parseErrorWithMitigation(databaseAccessError as unknown as EthersError)).toStrictEqual({
context: "MessageRepository: insert - Database access failed",
expect(ErrorParser.parseErrorWithMitigation(databaseAccessError)).toStrictEqual({
errorMessage: "MessageRepository: insert - Database access failed",
errorCode: "UNKNOWN_ERROR",
mitigation: {
shouldRetry: true,
@@ -339,13 +409,10 @@ describe("ErrorParser", () => {
"OFFCHAIN_FAULT",
];
otherErrorCodes.forEach((errorCode: ErrorCode) => {
const errorMessage = {
shortMessage: "any reason",
code: errorCode,
};
const error = makeError("any reason", errorCode);
expect(ErrorParser.parseErrorWithMitigation(errorMessage as unknown as EthersError)).toStrictEqual({
context: "any reason",
expect(ErrorParser.parseEthersError(error)).toStrictEqual({
errorMessage: "any reason",
errorCode: errorCode,
mitigation: {
shouldRetry: true,