diff --git a/.mocharc.json b/.mocharc.json new file mode 100644 index 000000000..79ba35ddf --- /dev/null +++ b/.mocharc.json @@ -0,0 +1,5 @@ +{ + "require": "ts-node/register/files", + "ignore": ["test/fixture-projects/**/*"], + "timeout": 6000 +} diff --git a/gre/config.ts b/gre/config.ts index 008b65568..49e7651ab 100644 --- a/gre/config.ts +++ b/gre/config.ts @@ -1,4 +1,5 @@ import fs from 'fs' +import path from 'path' import { providers } from 'ethers' import { NetworkConfig, NetworksConfig } from 'hardhat/types/config' @@ -19,8 +20,8 @@ interface GREChains { } interface GREProviders { - l1Provider: providers.JsonRpcProvider - l2Provider: providers.JsonRpcProvider + l1Provider: providers.JsonRpcProvider | undefined + l2Provider: providers.JsonRpcProvider | undefined } interface GREGraphConfigs { @@ -33,15 +34,18 @@ export function getAddressBookPath( opts: GraphRuntimeEnvironmentOptions, ): string { logDebug('== Getting address book path') + logDebug(`Graph base dir: ${hre.config.paths.graph}`) logDebug(`1) opts.addressBookPath: ${opts.addressBook}`) - logDebug(`2) hre.config.graph.addressBook: ${hre.config.graph.addressBook}`) + logDebug(`2) hre.config.graph.addressBook: ${hre.config.graph?.addressBook}`) - const addressBookPath = opts.addressBook ?? hre.config.graph.addressBook + let addressBookPath = opts.addressBook ?? hre.config.graph?.addressBook if (addressBookPath === undefined) { - throw new GREPluginError(`Must set a an addressBook path!`) + throw new GREPluginError('Must set a an addressBook path!') } + addressBookPath = normalizePath(addressBookPath, hre.config.paths.graph) + if (!fs.existsSync(addressBookPath)) { throw new GREPluginError(`Address book not found: ${addressBookPath}`) } @@ -55,10 +59,7 @@ export function getChains(mainChainId: number | undefined): GREChains { logDebug(`Hardhat chain id: ${mainChainId}`) if (!GraphNetwork.isSupported(mainChainId)) { - const supportedChains = GraphNetwork.chains.join(',') - throw new GREPluginError( - `Chain ${mainChainId} is not supported! Supported chainIds: ${supportedChains}.`, - ) + throw new GREPluginError(`Chain ${mainChainId} is not supported!`) } // If mainChainId is supported there is a supported counterpart chainId so both chains are not undefined @@ -89,25 +90,34 @@ export function getProviders( hre: HardhatRuntimeEnvironment, l1ChainId: number, l2ChainId: number, + isHHL1: boolean, ): GREProviders { logDebug('== Getting providers') - const l1Network = getNetworkConfig(hre.config.networks, l1ChainId) as HttpNetworkConfig - const l2Network = getNetworkConfig(hre.config.networks, l2ChainId) as HttpNetworkConfig + const getProvider = ( + networks: NetworksConfig, + chainId: number, + isMainProvider: boolean, + chainLabel: string, + ): providers.JsonRpcProvider | undefined => { + const network = getNetworkConfig(networks, chainId) as HttpNetworkConfig + + // Ensure at least main provider is configured + if (isMainProvider && network === undefined) { + throw new GREPluginError(`Must set a provider url for chain: ${chainId}!`) + } + + logDebug(`Provider url for ${chainLabel}: ${network?.url}`) - for (const network of [l1Network, l2Network]) { if (network === undefined) { - throw new GREPluginError(`L1 or L2 network not found in hardhat config!`) - } else if (network.url === undefined) { - throw new GREPluginError(`Must set a provider url for chain ${network.chainId}!`) + return undefined } - } - const l1Provider = new providers.JsonRpcProvider(l1Network.url) - const l2Provider = new providers.JsonRpcProvider(l2Network.url) + return new providers.JsonRpcProvider(network.url) + } - logDebug(`L1 provider url: ${l1Network.url}`) - logDebug(`L2 provider url: ${l2Network.url}`) + const l1Provider = getProvider(hre.config.networks, l1ChainId, isHHL1, 'L1') + const l2Provider = getProvider(hre.config.networks, l2ChainId, !isHHL1, 'L2') return { l1Provider, @@ -123,6 +133,7 @@ export function getGraphConfigPaths( isHHL1: boolean, ): GREGraphConfigs { logDebug('== Getting graph config paths') + logDebug(`Graph base dir: ${hre.config.paths.graph}`) const l1Network = getNetworkConfig(hre.config.networks, l1ChainId) const l2Network = getNetworkConfig(hre.config.networks, l2ChainId) @@ -132,7 +143,7 @@ export function getGraphConfigPaths( // - hre.graph() init parameter graphConfigPath (only for layer corresponding to hh network) // - hh network config // - hh graph config (layer specific: l1GraphConfig, l2GraphConfig) - const l1GraphConfigPath = + let l1GraphConfigPath = opts.l1GraphConfig ?? (isHHL1 ? opts.graphConfig : undefined) ?? l1Network?.graphConfig ?? @@ -144,7 +155,15 @@ export function getGraphConfigPaths( logDebug(`3) l1Network.graphConfig: ${l1Network?.graphConfig}`) logDebug(`4) hre.config.graph.l1GraphConfig: ${hre.config.graph.l1GraphConfig}`) - const l2GraphConfigPath = + if (isHHL1 && l1GraphConfigPath === undefined) { + throw new GREPluginError('Must specify a graph config file for L1!') + } + + if (l1GraphConfigPath !== undefined) { + l1GraphConfigPath = normalizePath(l1GraphConfigPath, hre.config.paths.graph) + } + + let l2GraphConfigPath = opts.l2GraphConfig ?? (!isHHL1 ? opts.graphConfig : undefined) ?? l2Network?.graphConfig ?? @@ -156,6 +175,14 @@ export function getGraphConfigPaths( logDebug(`3) l2Network.graphConfig: ${l2Network?.graphConfig}`) logDebug(`4) hre.config.graph.l2GraphConfig: ${hre.config.graph.l2GraphConfig}`) + if (!isHHL1 && l2GraphConfigPath === undefined) { + throw new GREPluginError('Must specify a graph config file for L2!') + } + + if (l2GraphConfigPath !== undefined) { + l2GraphConfigPath = normalizePath(l2GraphConfigPath, hre.config.paths.graph) + } + for (const configPath of [l1GraphConfigPath, l2GraphConfigPath]) { if (configPath !== undefined && !fs.existsSync(configPath)) { throw new GREPluginError(`Graph config file not found: ${configPath}`) @@ -176,3 +203,10 @@ function getNetworkConfig(networks: NetworksConfig, chainId: number): NetworkCon .map((n) => networks[n]) .find((n) => n.chainId === chainId) } + +function normalizePath(_path: string, graphPath: string) { + if (!path.isAbsolute(_path)) { + _path = path.join(graphPath, _path) + } + return _path +} diff --git a/gre/gre.ts b/gre/gre.ts index 2475513fd..99d081f7c 100644 --- a/gre/gre.ts +++ b/gre/gre.ts @@ -1,5 +1,5 @@ -import { HardhatRuntimeEnvironment } from 'hardhat/types' -import { extendEnvironment } from 'hardhat/config' +import { HardhatConfig, HardhatRuntimeEnvironment, HardhatUserConfig } from 'hardhat/types' +import { extendConfig, extendEnvironment } from 'hardhat/config' import { lazyFunction, lazyObject } from 'hardhat/plugins' import { getAddressBook } from '../cli/address-book' @@ -14,12 +14,33 @@ import { providers } from 'ethers' import { getChains, getProviders, getAddressBookPath, getGraphConfigPaths } from './config' import { getDeployer, getNamedAccounts, getTestAccounts } from './accounts' import { logDebug, logWarn } from './logger' +import path from 'path' // Graph Runtime Environment (GRE) extensions for the HRE + +extendConfig((config: HardhatConfig, userConfig: Readonly) => { + // Source for the path convention: + // https://github.com/NomicFoundation/hardhat-ts-plugin-boilerplate/blob/d450d89f4b6ed5d26a8ae32b136b9c55d2aadab5/src/index.ts + const userPath = userConfig.paths?.graph + + let newPath: string + if (userPath === undefined) { + newPath = config.paths.root + } else { + if (path.isAbsolute(userPath)) { + newPath = userPath + } else { + newPath = path.normalize(path.join(config.paths.root, userPath)) + } + } + + config.paths.graph = newPath +}) + extendEnvironment((hre: HardhatRuntimeEnvironment) => { hre.graph = (opts: GraphRuntimeEnvironmentOptions = {}) => { const { l1ChainId, l2ChainId, isHHL1 } = getChains(hre.network.config.chainId) - const { l1Provider, l2Provider } = getProviders(hre, l1ChainId, l2ChainId) + const { l1Provider, l2Provider } = getProviders(hre, l1ChainId, l2ChainId, isHHL1) const addressBookPath = getAddressBookPath(hre, opts) const { l1GraphConfigPath, l2GraphConfigPath } = getGraphConfigPaths( hre, @@ -60,7 +81,7 @@ extendEnvironment((hre: HardhatRuntimeEnvironment) => { function buildGraphNetworkEnvironment( chainId: number, - provider: providers.JsonRpcProvider, + provider: providers.JsonRpcProvider | undefined, graphConfigPath: string | undefined, addressBookPath: string, isHHL1: boolean, @@ -74,7 +95,15 @@ function buildGraphNetworkEnvironment( return null } + if (provider === undefined) { + logWarn( + `No provider URL found for: ${chainId}. ${isHHL1 ? 'L2' : 'L1'} will not be initialized.`, + ) + return null + } + return { + chainId: chainId, addressBook: lazyObject(() => getAddressBook(addressBookPath, chainId.toString())), graphConfig: lazyObject(() => readConfig(graphConfigPath, true)), contracts: lazyObject(() => diff --git a/gre/helpers/network.ts b/gre/helpers/network.ts index 85ee07313..5fc2829ff 100644 --- a/gre/helpers/network.ts +++ b/gre/helpers/network.ts @@ -1,7 +1,7 @@ class MapWithGetKey extends Map { getKey(value: K): K | undefined { - for (const [k] of this.entries()) { - if (k === value) { + for (const [k, v] of this.entries()) { + if (v === value) { return k } } diff --git a/gre/test/config.test.ts b/gre/test/config.test.ts new file mode 100644 index 000000000..d206220e6 --- /dev/null +++ b/gre/test/config.test.ts @@ -0,0 +1,224 @@ +import { expect } from 'chai' +import { useEnvironment } from './helpers' + +import { getAddressBookPath, getChains, getGraphConfigPaths, getProviders } from '../config' +import path from 'path' + +describe('GRE init functions', function () { + describe('getAddressBookPath with graph-config project', function () { + useEnvironment('graph-config') + + it('should use opts parameter if available', function () { + const addressBook = getAddressBookPath(this.hre, { + addressBook: 'addresses-opts.json', + }) + expect(path.basename(addressBook)).to.equal('addresses-opts.json') + }) + + it('should use HH graph config if opts parameter not available ', function () { + const addressBook = getAddressBookPath(this.hre, {}) + expect(path.basename(addressBook)).to.equal('addresses-hre.json') + }) + }) + + describe('getAddressBookPath with default-config project', function () { + useEnvironment('default-config') + + it('should throw if no address book is specified', function () { + expect(() => getAddressBookPath(this.hre, {})).to.throw('Must set a an addressBook path!') + }) + }) + + describe('getAddressBookPath with graph-config-bad project', function () { + useEnvironment('graph-config-bad') + + it("should throw if address book doesn't exist", function () { + expect(() => getAddressBookPath(this.hre, {})).to.throw(/Address book not found: /) + }) + }) + + describe('getChains', function () { + it('should return L1 and L2 chain ids for a supported L1 chain', function () { + const { l1ChainId, l2ChainId, isHHL1, isHHL2 } = getChains(5) // Goerli + + expect(l1ChainId).to.equal(5) + expect(l2ChainId).to.equal(421613) + expect(isHHL1).to.equal(true) + expect(isHHL2).to.equal(false) + }) + it('should return L1 and L2 chain ids for a supported L2 chain', function () { + const { l1ChainId, l2ChainId, isHHL1, isHHL2 } = getChains(42161) // Arbitrum One + + expect(l1ChainId).to.equal(1) + expect(l2ChainId).to.equal(42161) + expect(isHHL1).to.equal(false) + expect(isHHL2).to.equal(true) + }) + it('should throw if provided chain is not supported', function () { + const badChainId = 999 + expect(() => getChains(badChainId)).to.throw(`Chain ${badChainId} is not supported!`) + }) + }) + + describe('getProviders with graph-config project', function () { + useEnvironment('graph-config') + + it('should return L1 and L2 providers for supported networks (HH L1)', function () { + const { l1Provider, l2Provider } = getProviders(this.hre, 5, 421613, true) + expect(l1Provider).to.be.an('object') + expect(l2Provider).to.be.an('object') + }) + + it('should return L1 and L2 providers for supported networks (HH L2)', function () { + const { l1Provider, l2Provider } = getProviders(this.hre, 5, 421613, false) + expect(l1Provider).to.be.an('object') + expect(l2Provider).to.be.an('object') + }) + + it('should return only L1 provider if L2 is not supported (HH L1)', function () { + const { l1Provider, l2Provider } = getProviders(this.hre, 5, 123456, true) + expect(l1Provider).to.be.an('object') + expect(l2Provider).to.be.undefined + }) + + it('should return only L2 provider if L1 is not supported (HH L2)', function () { + const { l1Provider, l2Provider } = getProviders(this.hre, 123456, 421613, false) + expect(l1Provider).to.be.undefined + expect(l2Provider).to.be.an('object') + }) + }) + + describe('getProviders with graph-config-bad project', function () { + useEnvironment('graph-config-bad') + + it('should throw if main network is not defined in hardhat config (HH L1)', function () { + expect(() => getProviders(this.hre, 4, 421611, true)).to.throw( + /Must set a provider url for chain: /, + ) + }) + + it('should throw if main network is not defined in hardhat config (HH L2)', function () { + expect(() => getProviders(this.hre, 5, 421613, false)).to.throw( + /Must set a provider url for chain: /, + ) + }) + }) + + describe('getGraphConfigPaths with graph-config-full project', function () { + useEnvironment('graph-config-full') + + it('should use opts parameters if available', function () { + const { l1GraphConfigPath, l2GraphConfigPath } = getGraphConfigPaths( + this.hre, + { l1GraphConfig: 'config/graph.opts.yml', l2GraphConfig: 'config/graph.arbitrum-opts.yml' }, + 5, + 421613, + true, + ) + expect(path.basename(l1GraphConfigPath)).to.equal('graph.opts.yml') + expect(path.basename(l2GraphConfigPath)).to.equal('graph.arbitrum-opts.yml') + }) + + it('should use opts graphConfig parameter only for main network if available (HH L1)', function () { + const { l1GraphConfigPath, l2GraphConfigPath } = getGraphConfigPaths( + this.hre, + { graphConfig: 'config/graph.opts.yml' }, + 4, + 421611, + true, + ) + expect(path.basename(l1GraphConfigPath)).to.equal('graph.opts.yml') + expect(path.basename(l2GraphConfigPath)).to.equal('graph.arbitrum-hre.yml') + }) + + it('should use opts graphConfig parameter only for main network if available (HH L2)', function () { + const { l1GraphConfigPath, l2GraphConfigPath } = getGraphConfigPaths( + this.hre, + { graphConfig: 'config/graph.arbitrum-opts.yml' }, + 4, + 421611, + false, + ) + expect(path.basename(l1GraphConfigPath)).to.equal('graph.hre.yml') + expect(path.basename(l2GraphConfigPath)).to.equal('graph.arbitrum-opts.yml') + }) + + it('should ignore graphConfig parameter if both config paths are provided (HH L1)', function () { + const { l1GraphConfigPath, l2GraphConfigPath } = getGraphConfigPaths( + this.hre, + { + graphConfig: 'config/graph.opts2.yml', + l1GraphConfig: 'config/graph.opts.yml', + l2GraphConfig: 'config/graph.arbitrum-opts.yml', + }, + 5, + 421613, + true, + ) + expect(path.basename(l1GraphConfigPath)).to.equal('graph.opts.yml') + expect(path.basename(l2GraphConfigPath)).to.equal('graph.arbitrum-opts.yml') + }) + + it('should ignore graphConfig parameter if both config paths are provided (HH L2)', function () { + const { l1GraphConfigPath, l2GraphConfigPath } = getGraphConfigPaths( + this.hre, + { + graphConfig: 'config/graph.opts2.yml', + l1GraphConfig: 'config/graph.opts.yml', + l2GraphConfig: 'config/graph.arbitrum-opts.yml', + }, + 5, + 421613, + false, + ) + expect(path.basename(l1GraphConfigPath)).to.equal('graph.opts.yml') + expect(path.basename(l2GraphConfigPath)).to.equal('graph.arbitrum-opts.yml') + }) + + it('should use network specific config if no opts given', function () { + const { l1GraphConfigPath, l2GraphConfigPath } = getGraphConfigPaths( + this.hre, + {}, + 1, + 42161, + false, + ) + expect(path.basename(l1GraphConfigPath)).to.equal('graph.mainnet.yml') + expect(path.basename(l2GraphConfigPath)).to.equal('graph.arbitrum-goerli.yml') + }) + + it('should use graph generic config if nothing else given', function () { + const { l1GraphConfigPath, l2GraphConfigPath } = getGraphConfigPaths( + this.hre, + {}, + 4, + 421611, + false, + ) + expect(path.basename(l1GraphConfigPath)).to.equal('graph.hre.yml') + expect(path.basename(l2GraphConfigPath)).to.equal('graph.arbitrum-hre.yml') + }) + }) + + describe('getGraphConfigPaths with graph-config-bad project', function () { + useEnvironment('graph-config-bad') + + it('should throw if no config file for main network (HH L1)', function () { + expect(() => getGraphConfigPaths(this.hre, {}, 5, 421611, true)).to.throw( + 'Must specify a graph config file for L1!', + ) + }) + + it('should throw if no config file for main network (HH L2)', function () { + expect(() => getGraphConfigPaths(this.hre, {}, 5, 421611, false)).to.throw( + 'Must specify a graph config file for L2!', + ) + }) + + it('should throw if config file does not exist', function () { + expect(() => getGraphConfigPaths(this.hre, {}, 1, 421611, true)).to.throw( + /Graph config file not found: /, + ) + }) + }) +}) diff --git a/gre/test/files/addresses-hre.json b/gre/test/files/addresses-hre.json new file mode 100644 index 000000000..e69de29bb diff --git a/gre/test/files/addresses-opts.json b/gre/test/files/addresses-opts.json new file mode 100644 index 000000000..e69de29bb diff --git a/gre/test/files/config/graph.arbitrum-goerli.yml b/gre/test/files/config/graph.arbitrum-goerli.yml new file mode 100644 index 000000000..e69de29bb diff --git a/gre/test/files/config/graph.arbitrum-hre.yml b/gre/test/files/config/graph.arbitrum-hre.yml new file mode 100644 index 000000000..e69de29bb diff --git a/gre/test/files/config/graph.arbitrum-opts.yml b/gre/test/files/config/graph.arbitrum-opts.yml new file mode 100644 index 000000000..e69de29bb diff --git a/gre/test/files/config/graph.goerli.yml b/gre/test/files/config/graph.goerli.yml new file mode 100644 index 000000000..e69de29bb diff --git a/gre/test/files/config/graph.hre.yml b/gre/test/files/config/graph.hre.yml new file mode 100644 index 000000000..e69de29bb diff --git a/gre/test/files/config/graph.mainnet.yml b/gre/test/files/config/graph.mainnet.yml new file mode 100644 index 000000000..e69de29bb diff --git a/gre/test/files/config/graph.opts.yml b/gre/test/files/config/graph.opts.yml new file mode 100644 index 000000000..e69de29bb diff --git a/gre/test/files/config/graph.opts2.yml b/gre/test/files/config/graph.opts2.yml new file mode 100644 index 000000000..e69de29bb diff --git a/gre/test/fixture-projects/default-config/hardhat.config.js b/gre/test/fixture-projects/default-config/hardhat.config.js new file mode 100644 index 000000000..4ba52ba2c --- /dev/null +++ b/gre/test/fixture-projects/default-config/hardhat.config.js @@ -0,0 +1 @@ +module.exports = {} diff --git a/gre/test/fixture-projects/graph-config-bad/hardhat.config.ts b/gre/test/fixture-projects/graph-config-bad/hardhat.config.ts new file mode 100644 index 000000000..405d5d86c --- /dev/null +++ b/gre/test/fixture-projects/graph-config-bad/hardhat.config.ts @@ -0,0 +1,43 @@ +import '../../../gre' + +module.exports = { + paths: { + graph: '../../files', + }, + solidity: '0.8.9', + defaultNetwork: 'hardhat', + networks: { + hardhat: { + chainId: 1337, + }, + mainnet: { + chainId: 1, + graphConfig: 'config/graph.mainnet-does-not-exist.yml', + url: `https://mainnet.infura.io/v3/123456`, + }, + 'arbitrum-one': { + chainId: 42161, + url: 'https://arb1.arbitrum.io/rpc', + graphConfig: 'config/graph.arbitrum-does-not-exist.yml', + }, + goerli: { + chainId: 5, + url: `https://goerli.infura.io/v3/123456`, + }, + // 'arbitrum-goerli': { + // chainId: 421613, + // url: 'https://goerli-rollup.arbitrum.io/rpc', + // }, + // rinkeby: { + // chainId: 4, + // url: `https://goerli.infura.io/v3/123456`, + // }, + 'arbitrum-rinkeby': { + chainId: 421611, + url: `https://goerli.infura.io/v3/123456`, + }, + }, + graph: { + addressBook: 'addresses-does-not-exist.json', + }, +} diff --git a/gre/test/fixture-projects/graph-config-full/hardhat.config.ts b/gre/test/fixture-projects/graph-config-full/hardhat.config.ts new file mode 100644 index 000000000..c0d52dda1 --- /dev/null +++ b/gre/test/fixture-projects/graph-config-full/hardhat.config.ts @@ -0,0 +1,47 @@ +import '../../../gre' + +module.exports = { + paths: { + graph: '../../files', + }, + solidity: '0.8.9', + defaultNetwork: 'hardhat', + networks: { + hardhat: { + chainId: 1337, + }, + mainnet: { + chainId: 1, + graphConfig: 'config/graph.mainnet.yml', + url: `https://mainnet.infura.io/v3/123456`, + }, + 'arbitrum-one': { + chainId: 42161, + url: 'https://arb1.arbitrum.io/rpc', + graphConfig: 'config/graph.arbitrum-goerli.yml', + }, + goerli: { + chainId: 5, + url: `https://goerli.infura.io/v3/123456`, + graphConfig: 'config/graph.goerli.yml', + }, + 'arbitrum-goerli': { + chainId: 421613, + url: 'https://goerli-rollup.arbitrum.io/rpc', + graphConfig: 'config/graph.arbitrum-goerli.yml', + }, + rinkeby: { + chainId: 4, + url: `https://goerli.infura.io/v3/123456`, + }, + 'arbitrum-rinkeby': { + chainId: 421611, + url: `https://goerli.infura.io/v3/123456`, + }, + }, + graph: { + addressBook: 'addresses-hre.json', + l1GraphConfig: 'config/graph.hre.yml', + l2GraphConfig: 'config/graph.arbitrum-hre.yml', + }, +} diff --git a/gre/test/fixture-projects/graph-config/hardhat.config.ts b/gre/test/fixture-projects/graph-config/hardhat.config.ts new file mode 100644 index 000000000..5a4e5cdfc --- /dev/null +++ b/gre/test/fixture-projects/graph-config/hardhat.config.ts @@ -0,0 +1,44 @@ +import '../../../gre' + +module.exports = { + paths: { + graph: '../../files', + }, + solidity: '0.8.9', + defaultNetwork: 'hardhat', + networks: { + hardhat: { + chainId: 1337, + }, + mainnet: { + chainId: 1, + graphConfig: 'config/graph.mainnet.yml', + url: `https://mainnet.infura.io/v3/123456`, + }, + 'arbitrum-one': { + chainId: 42161, + url: 'https://arb1.arbitrum.io/rpc', + }, + goerli: { + chainId: 5, + url: `https://goerli.infura.io/v3/123456`, + }, + 'arbitrum-goerli': { + chainId: 421613, + url: 'https://goerli-rollup.arbitrum.io/rpc', + }, + localhost: { + chainId: 1337, + url: 'http://localhost:8545', + }, + 'arbitrum-rinkeby': { + chainId: 421611, + url: 'http://localhost:8545', + }, + }, + graph: { + addressBook: 'addresses-hre.json', + l1GraphConfig: 'config/graph.goerli.yml', + l2GraphConfig: 'config/graph.arbitrum-goerli.yml', + }, +} diff --git a/gre/test/gre.test.ts b/gre/test/gre.test.ts new file mode 100644 index 000000000..486e01d79 --- /dev/null +++ b/gre/test/gre.test.ts @@ -0,0 +1,70 @@ +import { expect } from 'chai' +import { useEnvironment } from './helpers' + +describe('GRE usage', function () { + describe('graph-config project setting --network to an L1', function () { + useEnvironment('graph-config', 'mainnet') + + it('should return L1 and L2 configured objects', function () { + const g = this.hre.graph() + + expect(g).to.be.an('object') + expect(g.l1).to.be.an('object') + expect(g.l2).to.be.an('object') + expect(g.l1.chainId).to.equal(1) + expect(g.l2.chainId).to.equal(42161) + expect(g.chainId).to.equal(1) + }) + }) + + describe('graph-config project setting --network to an L2', function () { + useEnvironment('graph-config', 'arbitrum-goerli') + + it('should return L1 and L2 configured objects ', function () { + const g = this.hre.graph() + + expect(g).to.be.an('object') + expect(g.l1).to.be.an('object') + expect(g.l2).to.be.an('object') + expect(g.l1.chainId).to.equal(5) + expect(g.l2.chainId).to.equal(421613) + expect(g.chainId).to.equal(421613) + }) + }) + + describe('graph-config project setting --network to an L1 with no configured counterpart', function () { + useEnvironment('graph-config', 'localhost') + + it('should return L1 configured object and L2 unconfigured', function () { + const g = this.hre.graph() + + expect(g).to.be.an('object') + expect(g.l1).to.be.an('object') + expect(g.l2).to.be.null + expect(g.l1.chainId).to.equal(1337) + expect(g.chainId).to.equal(1337) + }) + }) + + describe('graph-config project setting --network to an L2 with no configured counterpart', function () { + useEnvironment('graph-config', 'arbitrum-rinkeby') + + it('should return L2 configured object and L1 unconfigured', function () { + const g = this.hre.graph() + + expect(g).to.be.an('object') + expect(g.l1).to.be.null + expect(g.l2).to.be.an('object') + expect(g.l2.chainId).to.equal(421611) + expect(g.chainId).to.equal(421611) + }) + }) + + describe('default-config project', function () { + useEnvironment('default-config', 'mainnet') + + it('should throw', function () { + expect(() => this.hre.graph()).to.throw() + }) + }) +}) diff --git a/gre/test/helpers.ts b/gre/test/helpers.ts new file mode 100644 index 000000000..f0875a002 --- /dev/null +++ b/gre/test/helpers.ts @@ -0,0 +1,24 @@ +import { resetHardhatContext } from 'hardhat/plugins-testing' +import { HardhatRuntimeEnvironment } from 'hardhat/types' +import path from 'path' + +declare module 'mocha' { + interface Context { + hre: HardhatRuntimeEnvironment + } +} + +export function useEnvironment(fixtureProjectName: string, network?: string): void { + beforeEach('Loading hardhat environment', function () { + process.chdir(path.join(__dirname, 'fixture-projects', fixtureProjectName)) + + if (network !== undefined) { + process.env.HARDHAT_NETWORK = network + } + this.hre = require('hardhat') + }) + + afterEach('Resetting hardhat', function () { + resetHardhatContext() + }) +} diff --git a/gre/type-extensions.d.ts b/gre/type-extensions.d.ts index 34000bb92..9a1832a78 100644 --- a/gre/type-extensions.d.ts +++ b/gre/type-extensions.d.ts @@ -22,6 +22,7 @@ export type NamedAccounts = { } export interface GraphNetworkEnvironment { + chainId: number contracts: NetworkContracts graphConfig: any addressBook: AddressBook @@ -67,10 +68,10 @@ declare module 'hardhat/types/config' { } export interface ProjectPathsConfig { - graphConfigs?: string + graph?: string } export interface ProjectPathsUserConfig { - graphConfigs?: string + graph?: string } } diff --git a/hardhat.config.ts b/hardhat.config.ts index e85ff718d..782f4958e 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -99,7 +99,6 @@ const config: HardhatUserConfig = { sources: './contracts', tests: './test', artifacts: './build/contracts', - graphConfigs: './config', }, solidity: { compilers: [ diff --git a/package.json b/package.json index 852fd78f7..2ca6cd1c3 100644 --- a/package.json +++ b/package.json @@ -91,6 +91,7 @@ "test:e2e": "scripts/e2e", "test:gas": "RUN_EVM=true REPORT_GAS=true scripts/test", "test:coverage": "scripts/coverage", + "test:gre": "cd gre && mocha --exit --recursive 'test/**/*.test.ts' && cd ..", "lint": "yarn lint:ts && yarn lint:sol", "lint:fix": "yarn lint:ts:fix && yarn lint:sol:fix", "lint:ts": "eslint '**/*.{js,ts}'",