Skip to content
Merged
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
5 changes: 5 additions & 0 deletions .changeset/nervous-grapes-mix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@graphprotocol/graph-cli': patch
---

Refactor debug log for failed Etherscan ABI lookups
78 changes: 41 additions & 37 deletions packages/cli/src/command-helpers/abi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,31 @@ import { withSpinner } from './spinner';

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

const fetchFromEtherscan = async (url: string): Promise<any | null> => {
const result = await fetch(url);
let json: any = {};

if (result.ok) {
json = await result.json().catch(error => {
throw new Error(`Failed to read JSON response from Etherscan: ${error}`);
});

// Etherscan returns a JSON object that has a `status`, a `message` and
// a `result` field. The `status` is '0' in case of errors and '1' in
// case of success
if (json.status === '1') return json;
}

logger(
'Failed to fetchFromEtherscan: [%s] %s (%s)\n%O',
result.status,
result.statusText,
result.url,
json,
);
return null;
};

export const loadAbiFromEtherscan = async (
ABICtor: typeof ABI,
network: string,
Expand All @@ -18,16 +43,14 @@ export const loadAbiFromEtherscan = async (
`Warnings while fetching ABI from Etherscan`,
async () => {
const scanApiUrl = getEtherscanLikeAPIUrl(network);
const result = await fetch(`${scanApiUrl}?module=contract&action=getabi&address=${address}`);
const json = await result.json();
const json = await fetchFromEtherscan(
`${scanApiUrl}?module=contract&action=getabi&address=${address}`,
);

// Etherscan returns a JSON object that has a `status`, a `message` and
// a `result` field. The `status` is '0' in case of errors and '1' in
// case of success
if (json.status === '1') {
if (json)
return new ABICtor('Contract', undefined, immutable.fromJS(JSON.parse(json.result)));
}
throw new Error('ABI not found, try loading it from a local file');

throw new Error('Try loading it from a local file');
},
);

Expand Down Expand Up @@ -62,11 +85,11 @@ export const fetchDeployContractTransactionFromEtherscan = async (
address: string,
): Promise<string> => {
const scanApiUrl = getEtherscanLikeAPIUrl(network);
const json = await fetchContractCreationHashWithRetry(
const json = await fetchFromEtherscan(
`${scanApiUrl}?module=contract&action=getcontractcreation&contractaddresses=${address}`,
5,
);
if (json.status === '1') {

if (json) {
const hash = json.result[0].txHash;
logger('Successfully fetchDeployContractTransactionFromEtherscan. txHash: %s', hash);
return hash;
Expand All @@ -75,26 +98,6 @@ export const fetchDeployContractTransactionFromEtherscan = async (
throw new Error(`Failed to fetch deploy contract transaction`);
};

export const fetchContractCreationHashWithRetry = async (
url: string,
retryCount: number,
): Promise<any> => {
let json;
for (let i = 0; i < retryCount; i++) {
try {
const result = await fetch(url);
json = await result.json();
if (json.status !== '0') {
return json;
}
} catch (error) {
logger('Failed to fetchContractCreationHashWithRetry: %O', error);
/* empty */
}
}
throw new Error(`Failed to fetch contract creation transaction hash`);
};

export const fetchTransactionByHashFromRPC = async (
network: string,
transactionHash: string,
Expand Down Expand Up @@ -131,14 +134,15 @@ export const fetchSourceCodeFromEtherscan = async (
address: string,
): Promise<any> => {
const scanApiUrl = getEtherscanLikeAPIUrl(network);
const result = await fetch(
const json = await fetchFromEtherscan(
`${scanApiUrl}?module=contract&action=getsourcecode&address=${address}`,
);
const json = await result.json();
if (json.status === '1') {
return json;
}
throw new Error('Failed to fetch contract source code');

// Have to check that the SourceCode response is not empty due to Etherscan API bug responding with
// 1 - OK on non-valid contracts.
if (json.result[0].SourceCode) return json;

throw new Error(`Failed to fetch contract source code: ${json.result[0].ABI}`);
};

export const getContractNameForAddress = async (
Expand Down
Loading