Skip to content

Commit 974c05d

Browse files
authored
Retry etherscan calls (#86)
1 parent 6d3378e commit 974c05d

File tree

5 files changed

+68
-2
lines changed

5 files changed

+68
-2
lines changed

packages/ethereum-viewer/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
},
1717
"dependencies": {
1818
"fast-json-stable-stringify": "^2.1.0",
19+
"fetch-retry": "^5.0.6",
1920
"match-sorter": "^6.3.1",
2021
"p-finally": "^2.0.1",
2122
"path-browserify": "^1.0.1",

packages/ethereum-viewer/src/explorer/fetchFiles.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,20 @@ import { join } from "path";
22
import { assert, StrictOmit } from "ts-essentials";
33

44
import { fetch as _fetch } from "../util/fetch";
5+
import { makeSolidFetch } from "../util/solidFetch";
56
import { prettyStringify } from "../util/stringify";
67
import * as types from "./api-types";
78
import { apiUrlToWebsite } from "./apiUrlToWebsite";
89
import { fileExtension } from "./fileExtension";
910
import { ApiName, explorerApiKeys, explorerApiUrls } from "./networks";
1011

12+
const fetchEtherscanResponse = makeSolidFetch({
13+
async verifyResponse(response: unknown): Promise<boolean> {
14+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
15+
return (response as any)?.message === "OK" || false;
16+
},
17+
});
18+
1119
interface FetchFilesOptions {
1220
/**
1321
* For unit testing.
@@ -27,7 +35,11 @@ interface FetchFilesOptions {
2735
export async function fetchFiles(
2836
apiName: ApiName,
2937
contractAddress: string,
30-
{ fetch = _fetch, proxyDepth = 3, skipPrefix = false }: FetchFilesOptions = {}
38+
{
39+
fetch = fetchEtherscanResponse,
40+
proxyDepth = 3,
41+
skipPrefix = false,
42+
}: FetchFilesOptions = {}
3143
): Promise<FetchFilesResult> {
3244
const apiUrl = explorerApiUrls[apiName];
3345
const url =

packages/ethereum-viewer/src/openContractSource.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ async function saveSingleContractFilesToFs(
118118
return {
119119
entries,
120120
mainFile: withPrefix(mainFile),
121+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
121122
contractName: result.info.ContractName ?? "contract",
122123
};
123124
}
@@ -133,7 +134,7 @@ function getMainContractFile(
133134

134135
if (!fileToShow) {
135136
const regexp = new RegExp(`contract\\s+${name}`);
136-
fileToShow = files.find(([path, source]) => regexp.test(source));
137+
fileToShow = files.find(([_path, source]) => regexp.test(source));
137138
}
138139

139140
if (!fileToShow) fileToShow = files.sort(byPathLength)[0];
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import fetchRetry from "fetch-retry";
2+
3+
interface MakeSolidFetchOptions {
4+
/**
5+
* return false if the response should be retried
6+
*/
7+
verifyResponse: (response: unknown) => Promise<boolean>;
8+
}
9+
10+
// @note: returns already parsed response
11+
type SolidFetch = (input: string, init?: RequestInit) => Promise<unknown>;
12+
13+
export function makeSolidFetch(opts: MakeSolidFetchOptions): SolidFetch {
14+
const solidFetch = fetchRetry(self.fetch, {
15+
retries: 3,
16+
async retryOn(_attempt, error, response) {
17+
const retry = error !== null || !response?.ok;
18+
if (retry) {
19+
// eslint-disable-next-line no-console
20+
console.log("Retrying failed fetch", {
21+
error,
22+
status: response?.status,
23+
});
24+
}
25+
// if we have a response, we verify it and decide if we should retry
26+
if (!retry) {
27+
const responseJson = await response.clone().json();
28+
const verified = await opts.verifyResponse(responseJson);
29+
if (!verified) {
30+
console.log("Response verification failed, retrying...");
31+
}
32+
33+
return !verified;
34+
}
35+
36+
return true;
37+
},
38+
retryDelay: function (attempt) {
39+
return Math.pow(2, attempt) * 1000;
40+
},
41+
});
42+
43+
return (input: string, init?: RequestInit) =>
44+
solidFetch(input, init).then((response) => response.json());
45+
}

pnpm-lock.yaml

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)