diff --git a/README.md b/README.md index 82352e5..c966bc2 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ This is a hardhat plugin that aims to make subgraph building easy for Ethereum d ### `init` - Expects two parameters: `contractName: 'MyContract'` and `address: '0x123..'` + - Has optional param `startBlock` - the optional number of the block that the data source starts indexing from - Workflow: - Generates a subgraph in `./subgraph` using `generateScaffold` from `graph-cli` - Generates a network.json file in `./subgraph` using `initNetworksConfig` from `graph-cli` @@ -32,6 +33,7 @@ deploy() ### `update` - Expects two parameters: `contractName: 'MyContract'` and `address: '0x123..'` + - Has optional param `startBlock` - the optional number of the block that the data source starts indexing from - Workflow: - Updates the contract ABI in `./subgraph/abis` - Updates the contract Address in `network.json` if it's deployed to the same network. If the contract has been deployed to a network that is not present in the config file, adds an entry for the new network. @@ -58,6 +60,7 @@ deploy() ### `add` - Expects one mandatory parameter: `address: '0x123..` + - Has optional param `startBlock` - the optional number of the block that the data source starts indexing from - Has four optional paramaters: - `subgraphYaml: path/to/subgraph.yaml` (default is './subgraph.yaml') - `abi: path/to/Contract.json` Loads abi from file @@ -83,7 +86,8 @@ npx hardhat add --address 0x123... --abi path/to/Contract.json --contactName MyC async function deploy(contractName: string) { .... await contract.deployed(); - return { contractName: MyContract , address: contract.address} + const deployTx = await contract.deployTransaction.wait(); + return { contractName: MyContract , address: contract.address, blockNumber: deployTx.blockNumber} } deploy() diff --git a/src/helpers/subgraph.ts b/src/helpers/subgraph.ts index 2253687..28954c5 100644 --- a/src/helpers/subgraph.ts +++ b/src/helpers/subgraph.ts @@ -57,12 +57,16 @@ export const initSubgraph = async (taskArgs: { contractName: string, address: st } ) -export const updateNetworksFile = async (toolbox: any, network: string, dataSource: string, address: string, directory: string): Promise => { +export const updateNetworksConfig = async (toolbox: any, network: string, dataSource: string, identifier: string, value: string | number, directory: string): Promise => { await toolbox.patching.update(path.join(directory, 'networks.json'), (config: any) => { - if(Object.keys(config).includes(network)) { - Object.assign(config[network], { [dataSource]: { "address": address } }) + if (Object.prototype.hasOwnProperty.call(config, network)) { + if (Object.prototype.hasOwnProperty.call(config[network], dataSource)) { + Object.assign(config[network][dataSource], { [identifier]: value }) + } else { + Object.assign(config[network], { [dataSource]: { [identifier]: value } }) + } } else { - Object.assign(config, { [network]: { [dataSource]: { "address": address } }}) + Object.assign(config, { [network]: { [dataSource]: {[identifier]: value } }}) } return config }) diff --git a/src/tasks.ts b/src/tasks.ts index 331fb07..d361097 100644 --- a/src/tasks.ts +++ b/src/tasks.ts @@ -1,12 +1,12 @@ import path from 'path' import * as YAML from 'yaml' import * as toolbox from 'gluegun' -import { subtask, task } from 'hardhat/config' +import { task, types } from 'hardhat/config' import { compareAbiEvents } from './helpers/events' import { parseName } from 'hardhat/utils/contract-names' import { generateDockerCompose, generatePackageScripts } from './helpers/generator' import { checkForRepo, initRepository, initGitignore } from './helpers/git' -import { initSubgraph, runCodegen, updateNetworksFile, runGraphAdd } from './helpers/subgraph' +import { initSubgraph, runCodegen, updateNetworksConfig, runGraphAdd } from './helpers/subgraph' const Protocol = require('@graphprotocol/graph-cli/src/protocols') const Subgraph = require('@graphprotocol/graph-cli/src/subgraph') @@ -17,6 +17,7 @@ task("graph", "Wrapper task that will conditionally execute init, update or add. .addOptionalPositionalParam("subtask", "Specify which subtask to execute") .addParam("contractName", "The name of the contract") .addParam("address", "The address of the contract") + .addOptionalParam("startBlock", 'The subgraph startBlock', undefined, types.int) .addFlag("mergeEntities", "Whether the entities should be merged") .setAction(async (taskArgs, hre) => { const directory = hre.config.paths.subgraph @@ -41,19 +42,14 @@ task("graph", "Wrapper task that will conditionally execute init, update or add. await hre.run(subtask || command, args) }); - const getArtifactPath = async (hre: any, contractName: string): Promise => { - const artifact = await hre.artifacts.readArtifact(contractName) - return path.join(hre.config.paths.artifacts, artifact.sourceName, `${artifact.contractName}.json`) - } - - -/// MAYBE INIT AND UPDATE SHOULD NOT BE SUBTASKS BUT JUST FUNCTIONS? -subtask("init", "Initialize a subgraph") +task("init", "Initialize a subgraph") .addParam("contractName", "The name of the contract") .addParam("address", "The address of the contract") + .addOptionalParam("startBlock", 'The subgraph startBlock', undefined, types.int) .setAction(async (taskArgs, hre) => { const directory = hre.config.paths.subgraph const subgraphName = hre.config.subgraph.name + const network = hre.network.name || hre.config.defaultNetwork if (toolbox.filesystem.exists(directory) == "dir" && toolbox.filesystem.exists(path.join(directory, 'subgraph.yaml')) == "file") { toolbox.print.error("Subgraph already exists! Please use the update subtask to update an existing subgraph!") @@ -70,6 +66,8 @@ subtask("init", "Initialize a subgraph") process.exit(1) } + await updateNetworksConfig(toolbox, network, taskArgs.contractName, 'startBlock', taskArgs.startBlock, directory) + const isGitRepo = await checkForRepo(toolbox) if (!isGitRepo) { const repo = await initRepository(toolbox) @@ -102,9 +100,10 @@ subtask("init", "Initialize a subgraph") } }) -subtask("update", "Updates an existing subgraph from artifact or contract address") +task("update", "Updates an existing subgraph from artifact or contract address") .addParam("contractName", "The name of the contract") .addParam("address", "The address of the contract") + .addOptionalParam("startBlock", 'The subgraph startBlock', undefined, types.int) .setAction(async (taskArgs, hre) => { const directory = hre.config.paths.subgraph const network = hre.network.name || hre.config.defaultNetwork @@ -142,7 +141,8 @@ subtask("update", "Updates an existing subgraph from artifact or contract addres toolbox.filesystem.write(path.join(directory, subgraphAbi.file), artifact.abi) step(spinner, `Updating contract's ${network} address in networks.json`) - await updateNetworksFile(toolbox, network, dataSource.name, taskArgs.address, directory) + await updateNetworksConfig(toolbox, network, dataSource.name, 'address', taskArgs.address, directory) + await updateNetworksConfig(toolbox, network, dataSource.name, 'startBlock', taskArgs.startBlock, directory) step(spinner, `Checking events for changes`) const eventsChanged = await compareAbiEvents(spinner, toolbox, dataSource, artifact.abi) @@ -160,14 +160,16 @@ subtask("update", "Updates an existing subgraph from artifact or contract addres task("add", "Add a dataSource to the project") .addParam("address", "The address of the contract") + .addFlag("mergeEntities", "Whether the entities should be merged") + .addOptionalParam("startBlock", 'The subgraph startBlock', undefined, types.int) .addOptionalParam("subgraphYaml", "The location of the subgraph.yaml file", "subgraph.yaml") .addOptionalParam("contractName", "The name of the contract", "Contract") - .addFlag("mergeEntities", "Whether the entities should be merged") .addOptionalParam("abi", "Path to local abi file") .setAction(async (taskArgs, hre) => { const directory = hre.config.paths.subgraph const subgraph = toolbox.filesystem.read(path.join(directory, taskArgs.subgraphYaml), 'utf8') const { contractName } = parseName(taskArgs.contractName) + const network = hre.network.name || hre.config.defaultNetwork if (!toolbox.filesystem.exists(directory) || !subgraph) { toolbox.print.error("No subgraph found! Please first initialize a new subgraph!") @@ -181,8 +183,14 @@ task("add", "Add a dataSource to the project") async (spinner: any) => { step(spinner, `Initiating graph add command`) await runGraphAdd(hre, taskArgs, directory) + await updateNetworksConfig(toolbox, network, contractName, 'startBlock', taskArgs.startBlock, directory) return true } ) }) + + const getArtifactPath = async (hre: any, contractName: string): Promise => { + const artifact = await hre.artifacts.readArtifact(contractName) + return path.join(hre.config.paths.artifacts, artifact.sourceName, `${artifact.contractName}.json`) + }