Skip to content

Commit

Permalink
fix: retryable contract retry functionality (#303)
Browse files Browse the repository at this point in the history
# What ❔

Fix for the retryable contract retry functionality.

## Why ❔

Ethers v6 returns errors differently than Ethers v5, so the function
that parses an error and determines whether the request should be
retried needs to be fixed.

## Checklist

<!-- Check your PR fulfills the following items. -->
<!-- For draft PRs check the boxes as you complete them. -->

- [X] PR title corresponds to the body of PR (we generate changelog
entries from PRs).
- [X] Tests for the changes have been added / updated.
  • Loading branch information
vasyl-ivanchuk authored Oct 31, 2024
1 parent 770c97e commit edcbd02
Show file tree
Hide file tree
Showing 4 changed files with 19 additions and 53 deletions.
11 changes: 3 additions & 8 deletions packages/data-fetcher/src/blockchain/retryableContract.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,13 +100,8 @@ describe("RetryableContract", () => {

describe("when throws a permanent call exception function error", () => {
const callExceptionError = {
code: "CALL_EXCEPTION",
method: "contractFn(address)",
transaction: {
data: "0x00",
to: "to",
},
message: "call revert exception ....",
code: 3,
shortMessage: "execution reverted...",
};

beforeEach(() => {
Expand Down Expand Up @@ -209,7 +204,7 @@ describe("RetryableContract", () => {
describe("when throws a few errors with no method or message before returning a result", () => {
const functionResult = "functionResult";
const error = new Error();
(error as any).code = "CALL_EXCEPTION";
(error as any).code = 3;

beforeEach(() => {
let countOfFailedRequests = 0;
Expand Down
26 changes: 7 additions & 19 deletions packages/data-fetcher/src/blockchain/retryableContract.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
import { Logger } from "@nestjs/common";
import { setTimeout } from "timers/promises";
import { Contract, Interface, ContractRunner, ErrorCode } from "ethers";
import { Contract, Interface, ContractRunner, ErrorCode, isError } from "ethers";
import config from "../config";

const { blockchain } = config();

interface EthersError {
code: ErrorCode;
method: string;
transaction: {
data: string;
to: string;
};
message: string;
code: ErrorCode | number;
shortMessage: string;
}

const MAX_RETRY_INTERVAL = 60000;
Expand All @@ -24,16 +19,9 @@ const PERMANENT_ERRORS: ErrorCode[] = [
"NOT_IMPLEMENTED",
];

const shouldRetry = (calledFunctionName: string, error: EthersError): boolean => {
return (
!PERMANENT_ERRORS.includes(error.code) &&
!(
error.code === "CALL_EXCEPTION" &&
error.method?.startsWith(`${calledFunctionName}(`) &&
!!error.transaction &&
error.message?.startsWith("call revert exception")
)
);
const shouldRetry = (error: EthersError): boolean => {
const isPermanentErrorCode = PERMANENT_ERRORS.find((errorCode) => isError(error, errorCode));
return !isPermanentErrorCode && !(error.code === 3 && error.shortMessage?.startsWith("execution reverted"));
};

const retryableFunctionCall = async (
Expand All @@ -48,7 +36,7 @@ const retryableFunctionCall = async (
try {
return await result;
} catch (error) {
const isRetryable = shouldRetry(functionName, error);
const isRetryable = shouldRetry(error);
if (!isRetryable) {
logger.warn({
message: `Requested contract function ${functionName} failed to execute, not retryable`,
Expand Down
9 changes: 2 additions & 7 deletions packages/worker/src/blockchain/retryableContract.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,8 @@ describe("RetryableContract", () => {

describe("when throws a permanent call exception function error", () => {
const callExceptionError = {
code: "CALL_EXCEPTION",
method: "contractFn(address)",
transaction: {
data: "0x00",
to: "to",
},
message: "call revert exception ....",
code: 3,
shortMessage: "execution reverted ....",
};

beforeEach(() => {
Expand Down
26 changes: 7 additions & 19 deletions packages/worker/src/blockchain/retryableContract.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
import { Logger } from "@nestjs/common";
import { setTimeout } from "timers/promises";
import { Contract, Interface, ContractRunner, ErrorCode } from "ethers";
import { Contract, Interface, ContractRunner, ErrorCode, isError } from "ethers";

interface EthersError {
code: ErrorCode;
method: string;
transaction: {
data: string;
to: string;
};
message: string;
code: ErrorCode | number;
shortMessage: string;
}

const MAX_RETRY_INTERVAL = 60000;
Expand All @@ -21,16 +16,9 @@ const PERMANENT_ERRORS: ErrorCode[] = [
"NOT_IMPLEMENTED",
];

const shouldRetry = (calledFunctionName: string, error: EthersError): boolean => {
return (
!PERMANENT_ERRORS.includes(error.code) &&
!(
error.code === "CALL_EXCEPTION" &&
error.method?.startsWith(`${calledFunctionName}(`) &&
!!error.transaction &&
error.message?.startsWith("call revert exception")
)
);
const shouldRetry = (error: EthersError): boolean => {
const isPermanentErrorCode = PERMANENT_ERRORS.find((errorCode) => isError(error, errorCode));
return !isPermanentErrorCode && !(error.code === 3 && error.shortMessage?.startsWith("execution reverted"));
};

const retryableFunctionCall = async (
Expand All @@ -44,7 +32,7 @@ const retryableFunctionCall = async (
try {
return await result;
} catch (error) {
const isRetryable = shouldRetry(functionName, error);
const isRetryable = shouldRetry(error);
logger.warn({
message: `Requested contract function ${functionName} failed to execute, ${
isRetryable ? "retrying..." : "not retryable"
Expand Down

0 comments on commit edcbd02

Please sign in to comment.