Skip to content

Commit

Permalink
feat: auto fetch contract name from address (graphprotocol#1664)
Browse files Browse the repository at this point in the history
* feat: auto fetch contract name from address

* fix: lint fix

* Create shaggy-ligers-carry.md

---------

Co-authored-by: Saihajpreet Singh <[email protected]>
  • Loading branch information
shiyasmohd and saihaj authored May 20, 2024
1 parent 8f6ee24 commit c581b33
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 7 deletions.
5 changes: 5 additions & 0 deletions .changeset/shaggy-ligers-carry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@graphprotocol/graph-cli": minor
---

auto fetch contract name from address
43 changes: 43 additions & 0 deletions packages/cli/src/command-helpers/abi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,19 @@ export const loadStartBlockForContract = async (
},
);

export const loadContractNameForAddress = async (
network: string,
address: string,
): Promise<string> =>
await withSpinner(
`Fetching Contract Name`,
`Failed to fetch Contract Name`,
`Warnings while fetching contract name from Etherscan`,
async () => {
return getContractNameForAddress(network, address);
},
);

export const fetchDeployContractTransactionFromEtherscan = async (
network: string,
address: string,
Expand Down Expand Up @@ -111,6 +124,36 @@ export const fetchTransactionByHashFromRPC = async (
}
};

export const fetchSourceCodeFromEtherscan = async (
network: string,
address: string,
): Promise<any> => {
const scanApiUrl = getEtherscanLikeAPIUrl(network);
const result = await fetch(
`${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');
};

export const getContractNameForAddress = async (
network: string,
address: string,
): Promise<string> => {
try {
const contractSourceCode = await fetchSourceCodeFromEtherscan(network, address);
const contractName = contractSourceCode.result[0].ContractName;
logger('Successfully getContractNameForAddress. contractName: %s', contractName);
return contractName;
} catch (error) {
logger('Failed to fetch getContractNameForAddress: %O', error);
throw new Error(error?.message);
}
};

export const getStartBlockForContract = async (
network: string,
address: string,
Expand Down
25 changes: 24 additions & 1 deletion packages/cli/src/commands/add.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { CLIError } from '@oclif/core/lib/errors';
import {
loadAbiFromBlockScout,
loadAbiFromEtherscan,
loadContractNameForAddress,
loadStartBlockForContract,
} from '../command-helpers/abi';
import * as DataSourcesExtractor from '../command-helpers/data-sources';
Expand Down Expand Up @@ -64,7 +65,7 @@ export default class AddCommand extends Command {
args: { address, 'subgraph-manifest': manifestPath },
flags: {
abi,
'contract-name': contractName,
'contract-name': contractNameFlag,
'merge-entities': mergeEntities,
'network-file': networksFile,
'start-block': startBlockFlag,
Expand All @@ -78,6 +79,7 @@ export default class AddCommand extends Command {
const result = manifest.result.asMutable();

let startBlock = startBlockFlag;
let contractName = contractNameFlag;

const entities = getEntities(manifest);
const contractNames = getContractNames(manifest);
Expand Down Expand Up @@ -119,6 +121,27 @@ export default class AddCommand extends Command {
}
}

try {
contractName = await loadContractNameForAddress(network, address);
} catch (error) {
// not asking user to do prompt in test environment
if (process.env.NODE_ENV !== 'test') {
const { contractName: userInputContractName } = await prompt.ask<{ contractName: string }>([
{
type: 'input',
name: 'contractName',
message: 'Contract Name',
initial: 'Contract',
validate: value => value && value.length > 0,
result(value) {
return value;
},
},
]);
contractName = userInputContractName;
}
}

await writeABI(ethabi, contractName);

const { collisionEntities, onlyCollisions, abiData } = updateEventNamesOnCollision(
Expand Down
24 changes: 18 additions & 6 deletions packages/cli/src/commands/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Args, Command, Flags, ux } from '@oclif/core';
import {
loadAbiFromBlockScout,
loadAbiFromEtherscan,
loadContractNameForAddress,
loadStartBlockForContract,
} from '../command-helpers/abi';
import { initNetworksConfig } from '../command-helpers/network';
Expand Down Expand Up @@ -717,6 +718,18 @@ async function processInitForm(
initStartBlock = Number(startBlock).toString();
}
}

// If contract name is not set, try to load it.
if (!initContractName) {
// Load contract name for this contract
const contractName = await retryWithPrompt(() =>
loadContractNameForAddress(network, value),
);
if (contractName) {
initContractName = contractName;
}
}

return value;
},
},
Expand Down Expand Up @@ -813,6 +826,10 @@ async function processInitForm(
initial: initContractName || 'Contract' || isSubstreams,
skip: () => initFromExample !== undefined || !protocolInstance.hasContract(),
validate: value => value && value.length > 0,
result(value) {
if (initContractName) return initContractName;
return value;
},
},
]);

Expand Down Expand Up @@ -1314,11 +1331,6 @@ async function addAnotherContract(
this.log(`✖ ${error}`);
}

const contractName = await ux.prompt('\nContract Name', {
required: true,
default: 'Contract',
});

// Get the cwd before process.chdir in order to switch back in the end of command execution
const cwd = process.cwd();

Expand All @@ -1327,7 +1339,7 @@ async function addAnotherContract(
process.chdir(directory);
}

const commandLine = [contract, '--contract-name', contractName];
const commandLine = [contract];

await AddCommand.run(commandLine);
} catch (e) {
Expand Down

0 comments on commit c581b33

Please sign in to comment.