Skip to content

Commit 1d4217a

Browse files
authored
Refactor debug log for failed Etherscan ABI lookups (#1759)
Shows status, url and output of failed Etherscan request.
1 parent 58dbd28 commit 1d4217a

File tree

2 files changed

+46
-37
lines changed

2 files changed

+46
-37
lines changed

.changeset/nervous-grapes-mix.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@graphprotocol/graph-cli': patch
3+
---
4+
5+
Refactor debug log for failed Etherscan ABI lookups

packages/cli/src/command-helpers/abi.ts

Lines changed: 41 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,31 @@ import { withSpinner } from './spinner';
77

88
const logger = debugFactory('graph-cli:abi-helpers');
99

10+
const fetchFromEtherscan = async (url: string): Promise<any | null> => {
11+
const result = await fetch(url);
12+
let json: any = {};
13+
14+
if (result.ok) {
15+
json = await result.json().catch(error => {
16+
throw new Error(`Failed to read JSON response from Etherscan: ${error}`);
17+
});
18+
19+
// Etherscan returns a JSON object that has a `status`, a `message` and
20+
// a `result` field. The `status` is '0' in case of errors and '1' in
21+
// case of success
22+
if (json.status === '1') return json;
23+
}
24+
25+
logger(
26+
'Failed to fetchFromEtherscan: [%s] %s (%s)\n%O',
27+
result.status,
28+
result.statusText,
29+
result.url,
30+
json,
31+
);
32+
return null;
33+
};
34+
1035
export const loadAbiFromEtherscan = async (
1136
ABICtor: typeof ABI,
1237
network: string,
@@ -18,16 +43,14 @@ export const loadAbiFromEtherscan = async (
1843
`Warnings while fetching ABI from Etherscan`,
1944
async () => {
2045
const scanApiUrl = getEtherscanLikeAPIUrl(network);
21-
const result = await fetch(`${scanApiUrl}?module=contract&action=getabi&address=${address}`);
22-
const json = await result.json();
46+
const json = await fetchFromEtherscan(
47+
`${scanApiUrl}?module=contract&action=getabi&address=${address}`,
48+
);
2349

24-
// Etherscan returns a JSON object that has a `status`, a `message` and
25-
// a `result` field. The `status` is '0' in case of errors and '1' in
26-
// case of success
27-
if (json.status === '1') {
50+
if (json)
2851
return new ABICtor('Contract', undefined, immutable.fromJS(JSON.parse(json.result)));
29-
}
30-
throw new Error('ABI not found, try loading it from a local file');
52+
53+
throw new Error('Try loading it from a local file');
3154
},
3255
);
3356

@@ -62,11 +85,11 @@ export const fetchDeployContractTransactionFromEtherscan = async (
6285
address: string,
6386
): Promise<string> => {
6487
const scanApiUrl = getEtherscanLikeAPIUrl(network);
65-
const json = await fetchContractCreationHashWithRetry(
88+
const json = await fetchFromEtherscan(
6689
`${scanApiUrl}?module=contract&action=getcontractcreation&contractaddresses=${address}`,
67-
5,
6890
);
69-
if (json.status === '1') {
91+
92+
if (json) {
7093
const hash = json.result[0].txHash;
7194
logger('Successfully fetchDeployContractTransactionFromEtherscan. txHash: %s', hash);
7295
return hash;
@@ -75,26 +98,6 @@ export const fetchDeployContractTransactionFromEtherscan = async (
7598
throw new Error(`Failed to fetch deploy contract transaction`);
7699
};
77100

78-
export const fetchContractCreationHashWithRetry = async (
79-
url: string,
80-
retryCount: number,
81-
): Promise<any> => {
82-
let json;
83-
for (let i = 0; i < retryCount; i++) {
84-
try {
85-
const result = await fetch(url);
86-
json = await result.json();
87-
if (json.status !== '0') {
88-
return json;
89-
}
90-
} catch (error) {
91-
logger('Failed to fetchContractCreationHashWithRetry: %O', error);
92-
/* empty */
93-
}
94-
}
95-
throw new Error(`Failed to fetch contract creation transaction hash`);
96-
};
97-
98101
export const fetchTransactionByHashFromRPC = async (
99102
network: string,
100103
transactionHash: string,
@@ -131,14 +134,15 @@ export const fetchSourceCodeFromEtherscan = async (
131134
address: string,
132135
): Promise<any> => {
133136
const scanApiUrl = getEtherscanLikeAPIUrl(network);
134-
const result = await fetch(
137+
const json = await fetchFromEtherscan(
135138
`${scanApiUrl}?module=contract&action=getsourcecode&address=${address}`,
136139
);
137-
const json = await result.json();
138-
if (json.status === '1') {
139-
return json;
140-
}
141-
throw new Error('Failed to fetch contract source code');
140+
141+
// Have to check that the SourceCode response is not empty due to Etherscan API bug responding with
142+
// 1 - OK on non-valid contracts.
143+
if (json.result[0].SourceCode) return json;
144+
145+
throw new Error(`Failed to fetch contract source code: ${json.result[0].ABI}`);
142146
};
143147

144148
export const getContractNameForAddress = async (

0 commit comments

Comments
 (0)