Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve(retryProvider): Avoid retry on eth_call reverts #757

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions src/providers/retryProvider.ts
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still todo after discussion w/ @mrice32: update _trySend() such that it can identify a revert on an eth_call and short-circuit the retry loop that is inevitably occurring. This would shave off at least 3+ seconds per call.

Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { getOriginFromURL } from "../utils/NetworkUtils";
import { CacheProvider } from "./cachedProvider";
import { compareRpcResults, createSendErrorWithMessage, formatProviderError } from "./utils";
import { PROVIDER_CACHE_TTL } from "./constants";
import { JsonRpcError } from "./types";
import { Logger } from "winston";

export class RetryProvider extends ethers.providers.StaticJsonRpcProvider {
Expand Down Expand Up @@ -88,9 +89,27 @@ export class RetryProvider extends ethers.providers.StaticJsonRpcProvider {
): Promise<[ethers.providers.StaticJsonRpcProvider, unknown]> => {
return this._trySend(provider, method, params)
.then((result): [ethers.providers.StaticJsonRpcProvider, unknown] => [provider, result])
.catch((err) => {
.catch((err: unknown) => {
if (JsonRpcError.is(err)) {
const {
error: { code, message },
} = err;
// [-32768, -32100] is reserved by the JSON-RPC spec.
// [-32099, -32000] is allocated for implementation-defined responses.
// Everything else is available for use by the application space.
// Most node implementations return 3 for an eth_call revert, but some return -32000.
// See also https://www.jsonrpc.org/specification
if (code < -32768 || code > -32100) {
if (method === "eth_call" && message.toLowerCase().includes("revert")) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could maybe also be conditional on quorumThreshold === 1.

// Reverts will probably be the same across all providers, so don't waste time rotating.
throw err;
}
}
}

// Append the provider and error to the error array.
errors.push([provider, err?.stack || err?.toString()]);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
errors.push([provider, (err as any)?.stack || err?.toString()]);

// If there are no new fallback providers to use, terminate the recursion by throwing an error.
// Otherwise, we can try to call another provider.
Expand Down
10 changes: 10 additions & 0 deletions src/providers/types.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,12 @@
import { object, number, string } from "superstruct";

export type RPCProvider = "ALCHEMY" | "DRPC" | "INFURA" | "INFURA_DIN";
export type RPCTransport = "https" | "wss";

export const JsonRpcError = object({
error: object({
code: number(),
message: string(),
// data is optional and has no reliable type, so skip it.
}),
});
Loading