diff --git a/.gitignore b/.gitignore index 5861633e5..de3ae9b9d 100644 --- a/.gitignore +++ b/.gitignore @@ -51,6 +51,7 @@ addresses-fork.json # Forge artifacts cache_forge + # Graph client .graphclient @@ -62,4 +63,6 @@ tx-builder-*.json **/chain-1377/ **/horizon-localhost/ **/horizon-hardhat/ +**/subgraph-service-localhost/ +**/subgraph-service-hardhat/ !**/ignition/**/artifacts/ diff --git a/packages/hardhat-graph-protocol/package.json b/packages/hardhat-graph-protocol/package.json index e484a0643..7140122db 100644 --- a/packages/hardhat-graph-protocol/package.json +++ b/packages/hardhat-graph-protocol/package.json @@ -16,8 +16,8 @@ "main": "./dist/src/index.js", "exports": { ".": { - "types": "./dist/src/index.d.ts", - "default": "./dist/src/index.js" + "types": "./src/types.ts", + "default": "./src/index.ts" }, "./sdk": { "types": "./src/sdk/index.ts", diff --git a/packages/hardhat-graph-protocol/src/config.ts b/packages/hardhat-graph-protocol/src/config.ts index acd966db0..9f2e2af82 100644 --- a/packages/hardhat-graph-protocol/src/config.ts +++ b/packages/hardhat-graph-protocol/src/config.ts @@ -28,11 +28,7 @@ export function getAddressBookPath( } const normalizedAddressBookPath = normalizePath(addressBookPath, hre.config.paths.graph) - if (!fs.existsSync(normalizedAddressBookPath)) { - throw new GraphPluginError(`Address book not found: ${normalizedAddressBookPath}`) - } - - logDebug(`Address book path found: ${normalizedAddressBookPath}`) + logDebug(`Address book path: ${normalizedAddressBookPath}`) return normalizedAddressBookPath } diff --git a/packages/hardhat-graph-protocol/src/gre.ts b/packages/hardhat-graph-protocol/src/gre.ts index 028df6229..7f6e0566a 100644 --- a/packages/hardhat-graph-protocol/src/gre.ts +++ b/packages/hardhat-graph-protocol/src/gre.ts @@ -2,6 +2,7 @@ import path from 'path' import { getAddressBookPath } from './config' import { HardhatEthersProvider } from '@nomicfoundation/hardhat-ethers/internal/hardhat-ethers-provider' +import { lazyFunction } from 'hardhat/plugins' import { logDebug } from './logger' import { GraphHorizonAddressBook } from './sdk/deployments/horizon' @@ -28,7 +29,7 @@ export const greExtendConfig = (config: HardhatConfig, userConfig: Readonly { - hre.graph = (opts: GraphRuntimeEnvironmentOptions = { deployments: {} }) => { + hre.graph = lazyFunction(() => (opts: GraphRuntimeEnvironmentOptions = { deployments: {} }) => { logDebug('*** Initializing Graph Runtime Environment (GRE) ***') logDebug(`Main network: ${hre.network.name}`) const chainId = hre.network.config.chainId @@ -80,5 +81,5 @@ export const greExtendEnvironment = (hre: HardhatRuntimeEnvironment) => { assertGraphRuntimeEnvironment(gre) logDebug('GRE initialized successfully!') return gre - } + }) } diff --git a/packages/hardhat-graph-protocol/src/sdk/address-book.ts b/packages/hardhat-graph-protocol/src/sdk/address-book.ts index dda2f8490..3c9634bec 100644 --- a/packages/hardhat-graph-protocol/src/sdk/address-book.ts +++ b/packages/hardhat-graph-protocol/src/sdk/address-book.ts @@ -13,8 +13,9 @@ export type AddressBookJson< export type AddressBookEntry = { address: string - proxy?: boolean - implementation?: AddressBookEntry + proxy?: 'graph' | 'transparent' + proxyAdmin?: string + implementation?: string } /** @@ -24,8 +25,9 @@ export type AddressBookEntry = { * "": { * "": { * "address": "
", - * "proxy": true, // optional - * "implementation": { ... } // optional, nested contract structure + * "proxy": "", // optional + * "proxyAdmin": "
", // optional + * "implementation": "
", // optional * ... * } * } @@ -74,13 +76,18 @@ export abstract class AddressBook< this.chainId = _chainId logDebug(`Loading address book from ${this.file}.`) - if (!fs.existsSync(this.file)) throw new Error(`Address book path provided does not exist!`) + + // Create empty address book if file doesn't exist + if (!fs.existsSync(this.file)) { + const emptyAddressBook = { [this.chainId]: {} } + fs.writeFileSync(this.file, JSON.stringify(emptyAddressBook, null, 2)) + logDebug(`Created new address book at ${this.file}`) + } // Load address book and validate its shape - // If it's empty, initialize it with an empty object - const fileContents = JSON.parse(fs.readFileSync(this.file, 'utf8') || '{}') + const fileContents = JSON.parse(fs.readFileSync(this.file, 'utf8')) if (!fileContents[this.chainId]) { - fileContents[this.chainId] = {} as Record + fileContents[this.chainId] = {} } this.assertAddressBookJson(fileContents) this.addressBook = fileContents @@ -96,29 +103,40 @@ export abstract class AddressBook< return this.validContracts } + entryExists(name: string): boolean { + if (!this.isContractName(name)) { + throw new Error(`Contract name ${name} is not a valid contract name`) + } + return this.addressBook[this.chainId][name] !== undefined + } + /** * Get an entry from the address book * * @param name the name of the contract to get + * @param strict if true it will throw an error if the contract is not found * @returns the address book entry for the contract * Returns an empty address book entry if the contract is not found */ - getEntry(name: ContractName): { address: string } { - const entry = this.addressBook[this.chainId][name] - // Handle both object and string formats - if (typeof entry === 'string') { - return { address: entry } + getEntry(name: string): AddressBookEntry { + if (!this.isContractName(name)) { + throw new Error(`Contract name ${name} is not a valid contract name`) } + const entry = this.addressBook[this.chainId][name] + this._assertAddressBookEntry(entry) return entry } /** * Save an entry to the address book - * + * Allows partial address book entries to be saved * @param name the name of the contract to save * @param entry the address book entry for the contract */ - setEntry(name: ContractName, entry: AddressBookEntry): void { + setEntry(name: ContractName, entry: Partial): void { + if (entry.address === undefined) { + entry.address = '0x0000000000000000000000000000000000000000' + } this._assertAddressBookEntry(entry) this.addressBook[this.chainId][name] = entry try { @@ -162,7 +180,8 @@ export abstract class AddressBook< ): ContractList { const contracts = {} as ContractList if (this.listEntries().length == 0) { - throw Error('No valid contracts found in address book') + logError('No valid contracts found in address book') + return contracts } for (const contractName of this.listEntries()) { const artifactPath = typeof artifactsPath === 'object' && !Array.isArray(artifactsPath) @@ -214,18 +233,17 @@ export abstract class AddressBook< // Asserts the provided object is a valid address book entry _assertAddressBookEntry( entry: unknown, - ): asserts entry is { address: string } { - if (typeof entry === 'string') { - // If it's a string, treat it as an address - return - } - + ): asserts entry is AddressBookEntry { assertObject(entry) if (!('address' in entry)) { throw new Error('Address book entry must have an address field') } - if (typeof entry.address !== 'string') { - throw new Error('Address book entry address must be a string') + + const allowedFields = ['address', 'implementation', 'proxyAdmin', 'proxy'] + const entryFields = Object.keys(entry) + const invalidFields = entryFields.filter(field => !allowedFields.includes(field)) + if (invalidFields.length > 0) { + throw new Error(`Address book entry contains invalid fields: ${invalidFields.join(', ')}`) } } } diff --git a/packages/hardhat-graph-protocol/src/sdk/deployments/horizon/contracts.ts b/packages/hardhat-graph-protocol/src/sdk/deployments/horizon/contracts.ts index 86dd00be4..2797f2c83 100644 --- a/packages/hardhat-graph-protocol/src/sdk/deployments/horizon/contracts.ts +++ b/packages/hardhat-graph-protocol/src/sdk/deployments/horizon/contracts.ts @@ -24,6 +24,7 @@ export const GraphHorizonContractNameList = [ 'RewardsManager', 'L2GraphToken', 'L2GraphTokenGateway', + 'L2Curation', // @graphprotocol/horizon 'HorizonStaking', @@ -32,7 +33,7 @@ export const GraphHorizonContractNameList = [ 'GraphTallyCollector', ] as const -const root = path.resolve(__dirname, '../../../../..') // hardhat-graph-protocol root +const root = path.resolve(__dirname, '../../../..') // hardhat-graph-protocol root export const CONTRACTS_ARTIFACTS_PATH = path.resolve(root, 'node_modules', '@graphprotocol/contracts/build/contracts') export const HORIZON_ARTIFACTS_PATH = path.resolve(root, 'node_modules', '@graphprotocol/horizon/build/contracts') @@ -44,6 +45,7 @@ export const GraphHorizonArtifactsMap = { RewardsManager: CONTRACTS_ARTIFACTS_PATH, L2GraphToken: CONTRACTS_ARTIFACTS_PATH, L2GraphTokenGateway: CONTRACTS_ARTIFACTS_PATH, + L2Curation: CONTRACTS_ARTIFACTS_PATH, // @graphprotocol/horizon HorizonStaking: HORIZON_ARTIFACTS_PATH, diff --git a/packages/hardhat-graph-protocol/src/sdk/deployments/subgraph-service/contracts.ts b/packages/hardhat-graph-protocol/src/sdk/deployments/subgraph-service/contracts.ts index 7d0ef1ebc..1e770e1ff 100644 --- a/packages/hardhat-graph-protocol/src/sdk/deployments/subgraph-service/contracts.ts +++ b/packages/hardhat-graph-protocol/src/sdk/deployments/subgraph-service/contracts.ts @@ -25,7 +25,7 @@ export const SubgraphServiceContractNameList = [ 'DisputeManager', ] as const -const root = path.resolve(__dirname, '../../../../..') // hardhat-graph-protocol root +const root = path.resolve(__dirname, '../../../..') // hardhat-graph-protocol root export const CONTRACTS_ARTIFACTS_PATH = path.resolve(root, 'node_modules', '@graphprotocol/contracts/build/contracts') export const SUBGRAPH_SERVICE_ARTIFACTS_PATH = path.resolve(root, 'node_modules', '@graphprotocol/subgraph-service/build/contracts') diff --git a/packages/hardhat-graph-protocol/src/sdk/hardhat.base.config.ts b/packages/hardhat-graph-protocol/src/sdk/hardhat.base.config.ts index ea1ab8d1d..b97e07273 100644 --- a/packages/hardhat-graph-protocol/src/sdk/hardhat.base.config.ts +++ b/packages/hardhat-graph-protocol/src/sdk/hardhat.base.config.ts @@ -9,22 +9,11 @@ interface SecureAccountsOptions { enabled?: boolean } -// RPCs +// Hardhat variables const ARBITRUM_ONE_RPC = vars.get('ARBITRUM_ONE_RPC', 'https://arb1.arbitrum.io/rpc') const ARBITRUM_SEPOLIA_RPC = vars.get('ARBITRUM_SEPOLIA_RPC', 'https://sepolia-rollup.arbitrum.io/rpc') - -// Accounts -const getTestnetAccounts = () => { - const accounts: string[] = [] - if (vars.has('DEPLOYER_PRIVATE_KEY')) accounts.push(vars.get('DEPLOYER_PRIVATE_KEY')) - if (vars.has('GOVERNOR_PRIVATE_KEY')) accounts.push(vars.get('GOVERNOR_PRIVATE_KEY')) - return accounts -} -const getHardhatAccounts = () => { - return { - mnemonic: 'myth like bonus scare over problem client lizard pioneer submit female collect', - } -} +const LOCALHOST_RPC = vars.get('LOCALHOST_RPC', 'http://localhost:8545') +const LOCALHOST_CHAIN_ID = vars.get('LOCALHOST_CHAIN_ID', '31337') export const solidityUserConfig: SolidityUserConfig = { version: '0.8.27', @@ -57,17 +46,23 @@ type BaseNetworksUserConfig = NetworksUserConfig & export const networksUserConfig: BaseNetworksUserConfig = { hardhat: { chainId: 31337, - accounts: getHardhatAccounts(), + accounts: { + mnemonic: 'myth like bonus scare over problem client lizard pioneer submit female collect', + }, deployments: { - horizon: require.resolve('@graphprotocol/horizon/addresses-local.json'), + horizon: resolveLocalAddressBook('@graphprotocol/horizon/addresses.json'), + subgraphService: resolveLocalAddressBook('@graphprotocol/subgraph-service/addresses.json'), }, }, localhost: { - chainId: 31337, - url: 'http://localhost:8545', - accounts: getTestnetAccounts(), + chainId: parseInt(LOCALHOST_CHAIN_ID), + url: LOCALHOST_RPC, + secureAccounts: { + enabled: true, + }, deployments: { - horizon: require.resolve('@graphprotocol/horizon/addresses-local.json'), + horizon: resolveLocalAddressBook('@graphprotocol/horizon/addresses.json'), + subgraphService: resolveLocalAddressBook('@graphprotocol/subgraph-service/addresses.json'), }, }, arbitrumOne: { @@ -86,6 +81,13 @@ export const networksUserConfig: BaseNetworksUserConfig = { }, } +// Local address books are not commited to GitHub so they might not exist +// require.resolve will throw an error if the file does not exist, so we hack it a bit +function resolveLocalAddressBook(path: string) { + const resolvedPath = require.resolve(path) + return resolvedPath.replace('addresses.json', 'addresses-local.json') +} + type BaseHardhatConfig = HardhatUserConfig & { etherscan: Partial } & { graph: GraphRuntimeEnvironmentOptions } & @@ -100,6 +102,7 @@ export const hardhatBaseConfig: BaseHardhatConfig = { graph: { deployments: { horizon: require.resolve('@graphprotocol/horizon/addresses.json'), + subgraphService: require.resolve('@graphprotocol/subgraph-service/addresses.json'), }, }, etherscan: etherscanUserConfig, diff --git a/packages/hardhat-graph-protocol/src/sdk/ignition/ignition.ts b/packages/hardhat-graph-protocol/src/sdk/ignition/ignition.ts index 55e11a772..fdf73fcde 100644 --- a/packages/hardhat-graph-protocol/src/sdk/ignition/ignition.ts +++ b/packages/hardhat-graph-protocol/src/sdk/ignition/ignition.ts @@ -6,6 +6,8 @@ require('json5/lib/register') import fs from 'fs' import path from 'path' +import type { AddressBook } from '../address-book' + export function loadConfig(configPath: string, prefix: string, networkName: string): any { const configFileCandidates = [ path.resolve(process.cwd(), configPath, `${prefix}.${networkName}.json5`), @@ -19,76 +21,84 @@ export function loadConfig(configPath: string, prefix: string, networkName: stri ) } - return removeNFromBigInts(require(configFile)) + return { config: removeNFromBigInts(require(configFile)), file: configFile } } export function patchConfig(jsonData: any, patches: Record) { - function recursivePatch(obj: any) { - if (typeof obj === 'object' && obj !== null) { - for (const key in obj) { - if (key in patches) { - obj[key] = patches[key] + function recursivePatch(obj: any, patchObj: any) { + if (typeof obj === 'object' && obj !== null && typeof patchObj === 'object' && patchObj !== null) { + for (const key in patchObj) { + if (obj.hasOwnProperty(key) && typeof obj[key] === 'object' && typeof patchObj[key] === 'object') { + // Both are objects, recursively merge + recursivePatch(obj[key], patchObj[key]) } else { - recursivePatch(obj[key]) + // Either not an object or new key, directly assign + obj[key] = patchObj[key] } } } + return obj } - recursivePatch(jsonData) - return jsonData + return recursivePatch(jsonData, patches) } -export function mergeConfigs(obj1: any, obj2: any) { - const merged = { ...obj1 } - - for (const key in obj2) { - if (obj2.hasOwnProperty(key)) { - if (typeof obj2[key] === 'object' && obj2[key] !== null && obj1[key]) { - merged[key] = mergeConfigs(obj1[key], obj2[key]) - } else { - merged[key] = obj2[key] - } - } - } - - return merged -} - -export function saveAddressBook( +export function saveToAddressBook( contracts: any, chainId: number | undefined, - addressBook = 'addresses.json', -): Record> { + addressBook: AddressBook, +): AddressBook { if (!chainId) { throw new Error('Chain ID is required') } - // Use different address book for local networks - this one can be gitignored - if ([1377, 31337].includes(chainId)) { - addressBook = 'addresses-local.json' - } - - const output = fs.existsSync(addressBook) - ? JSON.parse(fs.readFileSync(addressBook, 'utf8')) - : {} + // Extract contract names and addresses + for (const [ignitionContractName, contract] of Object.entries(contracts)) { + // Proxy contracts + if (ignitionContractName.includes('_Proxy_')) { + const contractName = ignitionContractName.replace(/(Transparent_Proxy_|Graph_Proxy_)/, '') as ContractName + const proxy = ignitionContractName.includes('Transparent_Proxy_') ? 'transparent' : 'graph' + const entry = addressBook.entryExists(contractName) ? addressBook.getEntry(contractName) : {} + addressBook.setEntry(contractName, { + ...entry, + address: (contract as any).target, + proxy, + }) + } - output[chainId] = output[chainId] || {} + // Proxy admin contracts + if (ignitionContractName.includes('_ProxyAdmin_')) { + const contractName = ignitionContractName.replace(/(Transparent_ProxyAdmin_|Graph_ProxyAdmin_)/, '') as ContractName + const proxy = ignitionContractName.includes('Transparent_ProxyAdmin_') ? 'transparent' : 'graph' + const entry = addressBook.entryExists(contractName) ? addressBook.getEntry(contractName) : {} + addressBook.setEntry(contractName, { + ...entry, + proxy, + proxyAdmin: (contract as any).target, + }) + } - // Extract contract names and addresses - Object.entries(contracts).forEach(([contractName, contract]: [string, any]) => { - output[chainId][contractName] = contract.target - }) + // Implementation contracts + if (ignitionContractName.startsWith('Implementation_')) { + const contractName = ignitionContractName.replace('Implementation_', '') as ContractName + const entry = addressBook.entryExists(contractName) ? addressBook.getEntry(contractName) : {} + addressBook.setEntry(contractName, { + ...entry, + implementation: (contract as any).target, + }) + } - // Write to output file - const outputDir = path.dirname(addressBook) - if (!fs.existsSync(outputDir)) { - fs.mkdirSync(outputDir, { recursive: true }) + // Non proxied contracts + if (addressBook.isContractName(ignitionContractName)) { + const entry = addressBook.entryExists(ignitionContractName) ? addressBook.getEntry(ignitionContractName) : {} + addressBook.setEntry(ignitionContractName, { + ...entry, + address: (contract as any).target, + }) + } } - fs.writeFileSync(addressBook, JSON.stringify(output, null, 2)) - - return output as Record> + return addressBook } // Ignition requires "n" suffix for bigints, but not here diff --git a/packages/hardhat-graph-protocol/src/sdk/index.ts b/packages/hardhat-graph-protocol/src/sdk/index.ts index 5919c81f3..4bdc82681 100644 --- a/packages/hardhat-graph-protocol/src/sdk/index.ts +++ b/packages/hardhat-graph-protocol/src/sdk/index.ts @@ -1,5 +1,5 @@ -import { loadConfig, mergeConfigs, patchConfig, saveAddressBook } from './ignition/ignition' +import { loadConfig, patchConfig, saveToAddressBook } from './ignition/ignition' import { hardhatBaseConfig } from './hardhat.base.config' -const IgnitionHelper = { saveAddressBook, loadConfig, patchConfig, mergeConfigs } +const IgnitionHelper = { saveToAddressBook, loadConfig, patchConfig } export { hardhatBaseConfig, IgnitionHelper } diff --git a/packages/horizon/README.md b/packages/horizon/README.md index d541684af..748286c2e 100644 --- a/packages/horizon/README.md +++ b/packages/horizon/README.md @@ -8,11 +8,11 @@ The following environment variables might be required: | Variable | Description | |----------|-------------| -| `ARBISCAN_API_KEY` | Arbiscan API key | -| `DEPLOYER_PRIVATE_KEY` | Deployer private key - for testnet deployments | -| `GOVERNOR_PRIVATE_KEY` | Governor private key - for testnet deployments | -| `ARBITRUM_SEPOLIA_RPC` | Arbitrum Sepolia RPC URL | -| `VIRTUAL_ARBITRUM_SEPOLIA_RPC` | Virtual Arbitrum Sepolia RPC URL | +| `ARBISCAN_API_KEY` | Arbiscan API key - for contract verification| +| `ARBITRUM_ONE_RPC` | Arbitrum One RPC URL - defaults to `https://arb1.arbitrum.io/rpc` | +| `ARBITRUM_SEPOLIA_RPC` | Arbitrum Sepolia RPC URL - defaults to `https://sepolia-rollup.arbitrum.io/rpc` | +| `LOCALHOST_RPC` | Localhost RPC URL - defaults to `http://localhost:8545` | +| `LOCALHOST_CHAIN_ID` | Localhost chain ID - defaults to `31337` | You can set them using Hardhat: @@ -27,7 +27,7 @@ yarn install yarn build ``` -## Deploy +## Deployment Note that this instructions will help you deploy Graph Horizon contracts, but no data service will be deployed. If you want to deploy the Subgraph Service please refer to the [Subgraph Service README](../subgraph-service/README.md) for deploy instructions. @@ -35,14 +35,17 @@ Note that this instructions will help you deploy Graph Horizon contracts, but no To deploy Graph Horizon from scratch run the following command: ```bash -npx hardhat run scripts/deploy.ts --network hardhat +npx hardhat deploy:protocol --network hardhat ``` ### Upgrade deployment -To upgrade an existing deployment of the original Graph Protocol to Graph Horizon, run the following command: +Usually you would run this against a network (or a fork) where the original Graph Protocol was previously deployed. To upgrade an existing deployment of the original Graph Protocol to Graph Horizon, run the following commands. Note that some steps might need to be run by different accounts (deployer vs governor): ```bash -npx hardhat run scripts/migrate.ts --network localhost +npx hardhat deploy:migrate --network hardhat --step 1 +npx hardhat deploy:migrate --network hardhat --step 2 # Run with governor. Optionally add --patch-config +npx hardhat deploy:migrate --network hardhat --step 3 # Optionally add --patch-config +npx hardhat deploy:migrate --network hardhat --step 4 # Run with governor. Optionally add --patch-config ``` -Usually you would run this against a network (or a fork) where the original Graph Protocol was previously deployed. \ No newline at end of file +Steps 2, 3 and 4 require patching the configuration file with addresses from previous steps. The files are located in the `ignition/configs` directory and need to be manually edited. You can also pass `--patch-config` flag to the deploy command to automatically patch the configuration reading values from the address book. Note that this will NOT update the configuration file. \ No newline at end of file diff --git a/packages/horizon/hardhat.config.ts b/packages/horizon/hardhat.config.ts index 0c9262f8c..6efe24059 100644 --- a/packages/horizon/hardhat.config.ts +++ b/packages/horizon/hardhat.config.ts @@ -11,6 +11,7 @@ import 'hardhat-secure-accounts' // Skip importing hardhat-graph-protocol when building the project, it has circular dependency if (process.env.BUILD_RUN !== 'true') { require('hardhat-graph-protocol') + require('./tasks/deploy') } export default hardhatBaseConfig diff --git a/packages/horizon/ignition/configs/horizon-migrate.default.json5 b/packages/horizon/ignition/configs/horizon-migrate.default.json5 index e968be8f2..6061e1216 100644 --- a/packages/horizon/ignition/configs/horizon-migrate.default.json5 +++ b/packages/horizon/ignition/configs/horizon-migrate.default.json5 @@ -13,13 +13,13 @@ "rewardsManagerAddress": "0x1F49caE7669086c8ba53CC35d1E9f80176d67E79", "curationAddress": "0xDe761f075200E75485F4358978FB4d1dC8644FD5", - // Must be set for step 4 of the migration - "subgraphServiceAddress": "0x0000000000000000000000000000000000000000", + // Must be set for step 3 and 4 of the migration + "subgraphServiceAddress": "", // Parameters "maxThawingPeriod": 2419200 }, - "GraphPayments": { + "GraphPayments": { "protocolPaymentCut": 10000 }, "PaymentsEscrow": { @@ -31,20 +31,20 @@ "revokeSignerThawingPeriod": 10000 }, "HorizonProxiesGovernor": { - // These addresses must be set for step 2 of the migration - "graphPaymentsAddress": "0x0000000000000000000000000000000000000000", - "paymentsEscrowAddress": "0x0000000000000000000000000000000000000000" + // Must be set for step 2 of the migration + "graphPaymentsAddress": "", + "paymentsEscrowAddress": "" }, "HorizonStakingGovernor": { // Must be set for step 4 of the migration - "horizonStakingImplementationAddress": "0x0000000000000000000000000000000000000000" + "horizonStakingImplementationAddress": "" }, "L2CurationGovernor": { // Must be set for step 4 of the migration - "curationImplementationAddress": "0x0000000000000000000000000000000000000000" + "curationImplementationAddress": "" }, "RewardsManagerGovernor": { // Must be set for step 4 of the migration - "rewardsManagerImplementationAddress": "0x0000000000000000000000000000000000000000" + "rewardsManagerImplementationAddress": "" } } diff --git a/packages/horizon/ignition/configs/horizon-migrate.horizon-virtualArbitrumOne.json5 b/packages/horizon/ignition/configs/horizon-migrate.horizon-virtualArbitrumOne.json5 deleted file mode 100644 index 1633153d9..000000000 --- a/packages/horizon/ignition/configs/horizon-migrate.horizon-virtualArbitrumOne.json5 +++ /dev/null @@ -1,50 +0,0 @@ -{ - "$global": { - // Accounts - "governor": "0x72ee30d43Fb5A90B3FE983156C5d2fBE6F6d07B3", - - // Addresses for contracts deployed in the original Graph Protocol - "graphProxyAdminAddress": "0x2983936aC20202a6555993448E0d5654AC8Ca5fd", - "controllerAddress": "0x0a8491544221dd212964fbb96487467291b2C97e", - "horizonStakingAddress": "0x00669A4CF01450B64E8A2A20E9b1FCB71E61eF03", - "epochManagerAddress": "0x5A843145c43d328B9bB7a4401d94918f131bB281", - "graphTokenAddress": "0x9623063377AD1B27544C965cCd7342f7EA7e88C7", - "graphTokenGatewayAddress": "0x65E1a5e8946e7E87d9774f5288f41c30a99fD302", - "rewardsManagerAddress": "0x971B9d3d0Ae3ECa029CAB5eA1fB0F72c85e6a525", - "curationAddress": "0x22d78fb4bc72e191C765807f8891B5e1785C8014", - - // Must be set for step 4 of the migration - "subgraphServiceAddress": "0x0000000000000000000000000000000000000000", - - // Parameters - "maxThawingPeriod": 2419200 - }, - "GraphPayments": { - "protocolPaymentCut": 10000 - }, - "PaymentsEscrow": { - "withdrawEscrowThawingPeriod": 10000 - }, - "GraphTallyCollector": { - "eip712Name": "GraphTallyCollector", - "eip712Version": "1", - "revokeSignerThawingPeriod": 10000 - }, - "HorizonProxiesGovernor": { - // These addresses must be set for step 2 of the migration - "graphPaymentsAddress": "0x0000000000000000000000000000000000000000", - "paymentsEscrowAddress": "0x0000000000000000000000000000000000000000" - }, - "HorizonStakingGovernor": { - // Must be set for step 4 of the migration - "horizonStakingImplementationAddress": "0x0000000000000000000000000000000000000000" - }, - "L2CurationGovernor": { - // Must be set for step 4 of the migration - "curationImplementationAddress": "0x0000000000000000000000000000000000000000" - }, - "RewardsManagerGovernor": { - // Must be set for step 4 of the migration - "rewardsManagerImplementationAddress": "0x0000000000000000000000000000000000000000" - } -} diff --git a/packages/horizon/ignition/modules/core/GraphPayments.ts b/packages/horizon/ignition/modules/core/GraphPayments.ts index 414d3a8fa..56aa73f60 100644 --- a/packages/horizon/ignition/modules/core/GraphPayments.ts +++ b/packages/horizon/ignition/modules/core/GraphPayments.ts @@ -33,7 +33,7 @@ export default buildModule('GraphPayments', (m) => { m.call(GraphPaymentsProxyAdmin, 'transferOwnership', [governor], { after: [GraphPayments] }) - return { GraphPayments, GraphPaymentsProxyAdmin } + return { GraphPayments, GraphPaymentsProxyAdmin, GraphPaymentsImplementation } }) // Note that this module requires MigrateHorizonProxiesGovernorModule to be executed first @@ -66,5 +66,5 @@ export const MigrateGraphPaymentsModule = buildModule('GraphPayments', (m) => { m.call(GraphPaymentsProxyAdmin, 'transferOwnership', [governor], { after: [GraphPayments] }) - return { GraphPayments, GraphPaymentsProxyAdmin } + return { GraphPayments, GraphPaymentsProxyAdmin, GraphPaymentsImplementation } }) diff --git a/packages/horizon/ignition/modules/core/HorizonStaking.ts b/packages/horizon/ignition/modules/core/HorizonStaking.ts index e0e2d9b16..c87208be2 100644 --- a/packages/horizon/ignition/modules/core/HorizonStaking.ts +++ b/packages/horizon/ignition/modules/core/HorizonStaking.ts @@ -43,7 +43,7 @@ export default buildModule('HorizonStaking', (m) => { }) m.call(HorizonStaking, 'setMaxThawingPeriod', [maxThawingPeriod]) - return { HorizonStaking } + return { HorizonStaking, HorizonStakingImplementation } }) // Note that this module requires MigrateHorizonProxiesGovernorModule to be executed first @@ -95,5 +95,5 @@ export const MigrateHorizonStakingGovernorModule = buildModule('HorizonStakingGo }) m.call(HorizonStaking, 'setMaxThawingPeriod', [maxThawingPeriod]) - return { HorizonStaking } + return { HorizonStaking, HorizonStakingImplementation } }) diff --git a/packages/horizon/ignition/modules/core/PaymentsEscrow.ts b/packages/horizon/ignition/modules/core/PaymentsEscrow.ts index 2734dd205..139d06b06 100644 --- a/packages/horizon/ignition/modules/core/PaymentsEscrow.ts +++ b/packages/horizon/ignition/modules/core/PaymentsEscrow.ts @@ -33,7 +33,7 @@ export default buildModule('PaymentsEscrow', (m) => { m.call(PaymentsEscrowProxyAdmin, 'transferOwnership', [governor], { after: [PaymentsEscrow] }) - return { PaymentsEscrow, PaymentsEscrowProxyAdmin } + return { PaymentsEscrow, PaymentsEscrowProxyAdmin, PaymentsEscrowImplementation } }) // Note that this module requires MigrateHorizonProxiesGovernorModule to be executed first @@ -66,5 +66,5 @@ export const MigratePaymentsEscrowModule = buildModule('PaymentsEscrow', (m) => m.call(PaymentsEscrowProxyAdmin, 'transferOwnership', [governor], { after: [PaymentsEscrow] }) - return { PaymentsEscrow, PaymentsEscrowProxyAdmin } + return { PaymentsEscrow, PaymentsEscrowProxyAdmin, PaymentsEscrowImplementation } }) diff --git a/packages/horizon/ignition/modules/core/core.ts b/packages/horizon/ignition/modules/core/core.ts index e634e5e65..f719e6605 100644 --- a/packages/horizon/ignition/modules/core/core.ts +++ b/packages/horizon/ignition/modules/core/core.ts @@ -1,24 +1,40 @@ import { buildModule } from '@nomicfoundation/hardhat-ignition/modules' import GraphPaymentsModule, { MigrateGraphPaymentsModule } from './GraphPayments' +import GraphTallyCollectorModule, { MigrateGraphTallyCollectorModule } from './GraphTallyCollector' import HorizonStakingModule, { MigrateHorizonStakingDeployerModule } from './HorizonStaking' import PaymentsEscrowModule, { MigratePaymentsEscrowModule } from './PaymentsEscrow' -import GraphTallyCollectorModule, { MigrateGraphTallyCollectorModule } from './GraphTallyCollector' export default buildModule('GraphHorizon_Core', (m) => { - const { HorizonStaking } = m.useModule(HorizonStakingModule) - const { GraphPayments } = m.useModule(GraphPaymentsModule) - const { PaymentsEscrow } = m.useModule(PaymentsEscrowModule) + const { HorizonStaking, HorizonStakingImplementation } = m.useModule(HorizonStakingModule) + const { GraphPayments, GraphPaymentsImplementation } = m.useModule(GraphPaymentsModule) + const { PaymentsEscrow, PaymentsEscrowImplementation } = m.useModule(PaymentsEscrowModule) const { GraphTallyCollector } = m.useModule(GraphTallyCollectorModule) - return { HorizonStaking, GraphPayments, PaymentsEscrow, GraphTallyCollector } + return { + HorizonStaking, + HorizonStakingImplementation, + GraphPayments, + GraphPaymentsImplementation, + PaymentsEscrow, + PaymentsEscrowImplementation, + GraphTallyCollector, + } }) export const MigrateHorizonCoreModule = buildModule('GraphHorizon_Core', (m) => { const { HorizonStakingProxy: HorizonStaking, HorizonStakingImplementation } = m.useModule(MigrateHorizonStakingDeployerModule) - const { GraphPayments } = m.useModule(MigrateGraphPaymentsModule) - const { PaymentsEscrow } = m.useModule(MigratePaymentsEscrowModule) + const { GraphPayments, GraphPaymentsImplementation } = m.useModule(MigrateGraphPaymentsModule) + const { PaymentsEscrow, PaymentsEscrowImplementation } = m.useModule(MigratePaymentsEscrowModule) const { GraphTallyCollector } = m.useModule(MigrateGraphTallyCollectorModule) - return { HorizonStaking, HorizonStakingImplementation, GraphPayments, PaymentsEscrow, GraphTallyCollector } + return { + HorizonStaking, + HorizonStakingImplementation, + GraphPayments, + GraphPaymentsImplementation, + PaymentsEscrow, + PaymentsEscrowImplementation, + GraphTallyCollector, + } }) diff --git a/packages/horizon/ignition/modules/deploy.ts b/packages/horizon/ignition/modules/deploy.ts index 4f904c05a..aed98f69f 100644 --- a/packages/horizon/ignition/modules/deploy.ts +++ b/packages/horizon/ignition/modules/deploy.ts @@ -7,16 +7,24 @@ export default buildModule('GraphHorizon_Deploy', (m) => { const { Controller, EpochManager, + EpochManagerImplementation, GraphProxyAdmin, L2GraphTokenGateway, + L2GraphTokenGatewayImplementation, L2GraphToken, + L2GraphTokenImplementation, RewardsManager, + RewardsManagerImplementation, L2Curation, + L2CurationImplementation, } = m.useModule(GraphPeripheryModule) const { HorizonStaking, + HorizonStakingImplementation, GraphPayments, + GraphPaymentsImplementation, PaymentsEscrow, + PaymentsEscrowImplementation, GraphTallyCollector, } = m.useModule(GraphHorizonCoreModule) @@ -32,15 +40,23 @@ export default buildModule('GraphHorizon_Deploy', (m) => { return { Controller, - L2Curation, - EpochManager, + Graph_Proxy_EpochManager: EpochManager, + Implementation_EpochManager: EpochManagerImplementation, + Graph_Proxy_L2Curation: L2Curation, + Implementation_L2Curation: L2CurationImplementation, + Graph_Proxy_RewardsManager: RewardsManager, + Implementation_RewardsManager: RewardsManagerImplementation, + Graph_Proxy_L2GraphTokenGateway: L2GraphTokenGateway, + Implementation_L2GraphTokenGateway: L2GraphTokenGatewayImplementation, + Graph_Proxy_L2GraphToken: L2GraphToken, + Implementation_L2GraphToken: L2GraphTokenImplementation, GraphProxyAdmin, - L2GraphTokenGateway, - L2GraphToken, - RewardsManager, - HorizonStaking, - GraphPayments, - PaymentsEscrow, + Graph_Proxy_HorizonStaking: HorizonStaking, + Implementation_HorizonStaking: HorizonStakingImplementation, + Transparent_Proxy_GraphPayments: GraphPayments, + Implementation_GraphPayments: GraphPaymentsImplementation, + Transparent_Proxy_PaymentsEscrow: PaymentsEscrow, + Implementation_PaymentsEscrow: PaymentsEscrowImplementation, GraphTallyCollector, } }) diff --git a/packages/horizon/ignition/modules/migrate/migrate-1.ts b/packages/horizon/ignition/modules/migrate/migrate-1.ts index f1fffc103..9b7cab512 100644 --- a/packages/horizon/ignition/modules/migrate/migrate-1.ts +++ b/packages/horizon/ignition/modules/migrate/migrate-1.ts @@ -11,9 +11,9 @@ export default buildModule('GraphHorizon_Migrate_1', (m) => { } = m.useModule(MigrateHorizonProxiesDeployerModule) return { - GraphPaymentsProxy, - PaymentsEscrowProxy, - GraphPaymentsProxyAdmin, - PaymentsEscrowProxyAdmin, + Transparent_Proxy_GraphPayments: GraphPaymentsProxy, + Transparent_Proxy_PaymentsEscrow: PaymentsEscrowProxy, + Transparent_ProxyAdmin_GraphPayments: GraphPaymentsProxyAdmin, + Transparent_ProxyAdmin_PaymentsEscrow: PaymentsEscrowProxyAdmin, } }) diff --git a/packages/horizon/ignition/modules/migrate/migrate-3.ts b/packages/horizon/ignition/modules/migrate/migrate-3.ts index 73def82c0..999cc25cc 100644 --- a/packages/horizon/ignition/modules/migrate/migrate-3.ts +++ b/packages/horizon/ignition/modules/migrate/migrate-3.ts @@ -20,24 +20,28 @@ export default buildModule('GraphHorizon_Migrate_3', (m) => { HorizonStaking, HorizonStakingImplementation, GraphPayments, + GraphPaymentsImplementation, PaymentsEscrow, + PaymentsEscrowImplementation, GraphTallyCollector, } = m.useModule(MigrateHorizonCoreModule) return { - L2Curation, - L2CurationImplementation, - RewardsManager, - RewardsManagerImplementation, - HorizonStaking, - HorizonStakingImplementation, - GraphPayments, - PaymentsEscrow, + Graph_Proxy_L2Curation: L2Curation, + Implementation_L2Curation: L2CurationImplementation, + Graph_Proxy_RewardsManager: RewardsManager, + Implementation_RewardsManager: RewardsManagerImplementation, + Graph_Proxy_HorizonStaking: HorizonStaking, + Implementation_HorizonStaking: HorizonStakingImplementation, + Transparent_Proxy_GraphPayments: GraphPayments, + Implementation_GraphPayments: GraphPaymentsImplementation, + Transparent_Proxy_PaymentsEscrow: PaymentsEscrow, + Implementation_PaymentsEscrow: PaymentsEscrowImplementation, GraphTallyCollector, - Controller, + Controller: Controller, GraphProxyAdmin, - EpochManager, - L2GraphToken, - L2GraphTokenGateway, + Graph_Proxy_EpochManager: EpochManager, + Graph_Proxy_L2GraphToken: L2GraphToken, + Graph_Proxy_L2GraphTokenGateway: L2GraphTokenGateway, } }) diff --git a/packages/horizon/ignition/modules/migrate/migrate-4.ts b/packages/horizon/ignition/modules/migrate/migrate-4.ts index f0c14ce32..9290cc753 100644 --- a/packages/horizon/ignition/modules/migrate/migrate-4.ts +++ b/packages/horizon/ignition/modules/migrate/migrate-4.ts @@ -7,19 +7,25 @@ import { MigrateRewardsManagerGovernorModule } from '../periphery/RewardsManager export default buildModule('GraphHorizon_Migrate_4', (m) => { const { L2Curation, + L2CurationImplementation, } = m.useModule(MigrateCurationGovernorModule) const { RewardsManager, + RewardsManagerImplementation, } = m.useModule(MigrateRewardsManagerGovernorModule) const { HorizonStaking, + HorizonStakingImplementation, } = m.useModule(MigrateHorizonStakingGovernorModule) return { - L2Curation, - RewardsManager, - HorizonStaking, + Graph_Proxy_L2Curation: L2Curation, + Implementation_L2Curation: L2CurationImplementation, + Graph_Proxy_RewardsManager: RewardsManager, + Implementation_RewardsManager: RewardsManagerImplementation, + Graph_Proxy_HorizonStaking: HorizonStaking, + Implementation_HorizonStaking: HorizonStakingImplementation, } }) diff --git a/packages/horizon/ignition/modules/periphery/Curation.ts b/packages/horizon/ignition/modules/periphery/Curation.ts index 102512e4d..d93ffc678 100644 --- a/packages/horizon/ignition/modules/periphery/Curation.ts +++ b/packages/horizon/ignition/modules/periphery/Curation.ts @@ -20,14 +20,14 @@ export default buildModule('L2Curation', (m) => { const GraphCurationToken = m.contract('GraphCurationToken', GraphCurationTokenArtifact, []) - const L2Curation = deployWithGraphProxy(m, GraphProxyAdmin, { + const { proxy: L2Curation, implementation: L2CurationImplementation } = deployWithGraphProxy(m, GraphProxyAdmin, { name: 'L2Curation', artifact: CurationArtifact, initArgs: [Controller, GraphCurationToken, curationTaxPercentage, minimumCurationDeposit], }) m.call(L2Curation, 'setSubgraphService', [subgraphServiceAddress]) - return { L2Curation } + return { L2Curation, L2CurationImplementation } }) export const MigrateCurationDeployerModule = buildModule('L2CurationDeployer', (m: IgnitionModuleBuilder) => { @@ -62,5 +62,5 @@ export const MigrateCurationGovernorModule = buildModule('L2CurationGovernor', ( const L2Curation = upgradeGraphProxy(m, GraphProxyAdmin, L2CurationProxy, L2CurationImplementation, implementationMetadata) m.call(L2Curation, 'setSubgraphService', [subgraphServiceAddress]) - return { L2Curation } + return { L2Curation, L2CurationImplementation } }) diff --git a/packages/horizon/ignition/modules/periphery/EpochManager.ts b/packages/horizon/ignition/modules/periphery/EpochManager.ts index 993cf7fdc..a7e08bf22 100644 --- a/packages/horizon/ignition/modules/periphery/EpochManager.ts +++ b/packages/horizon/ignition/modules/periphery/EpochManager.ts @@ -12,13 +12,13 @@ export default buildModule('EpochManager', (m) => { const epochLength = m.getParameter('epochLength') - const EpochManager = deployWithGraphProxy(m, GraphProxyAdmin, { + const { proxy: EpochManager, implementation: EpochManagerImplementation } = deployWithGraphProxy(m, GraphProxyAdmin, { name: 'EpochManager', artifact: EpochManagerArtifact, initArgs: [Controller, epochLength], }) - return { EpochManager } + return { EpochManager, EpochManagerImplementation } }) export const MigrateEpochManagerModule = buildModule('EpochManager', (m) => { diff --git a/packages/horizon/ignition/modules/periphery/GraphToken.ts b/packages/horizon/ignition/modules/periphery/GraphToken.ts index 9b55755cb..dd8c1a129 100644 --- a/packages/horizon/ignition/modules/periphery/GraphToken.ts +++ b/packages/horizon/ignition/modules/periphery/GraphToken.ts @@ -16,7 +16,7 @@ export default buildModule('L2GraphToken', (m) => { const governor = m.getAccount(1) const initialSupply = m.getParameter('initialSupply') - const L2GraphToken = deployWithGraphProxy(m, GraphProxyAdmin, { + const { proxy: L2GraphToken, implementation: L2GraphTokenImplementation } = deployWithGraphProxy(m, GraphProxyAdmin, { name: 'L2GraphToken', artifact: GraphTokenArtifact, initArgs: [deployer], @@ -31,7 +31,7 @@ export default buildModule('L2GraphToken', (m) => { const transferOwnershipCall = m.call(L2GraphToken, 'transferOwnership', [governor], { after: [mintCall, renounceMinterCall, addMinterRewardsManagerCall, addMinterGatewayCall] }) m.call(L2GraphToken, 'acceptOwnership', [], { from: governor, after: [transferOwnershipCall] }) - return { L2GraphToken } + return { L2GraphToken, L2GraphTokenImplementation } }) export const MigrateGraphTokenModule = buildModule('L2GraphToken', (m) => { diff --git a/packages/horizon/ignition/modules/periphery/GraphTokenGateway.ts b/packages/horizon/ignition/modules/periphery/GraphTokenGateway.ts index 5ddaf7860..fc679e8ad 100644 --- a/packages/horizon/ignition/modules/periphery/GraphTokenGateway.ts +++ b/packages/horizon/ignition/modules/periphery/GraphTokenGateway.ts @@ -13,14 +13,14 @@ export default buildModule('L2GraphTokenGateway', (m) => { const pauseGuardian = m.getParameter('pauseGuardian') - const L2GraphTokenGateway = deployWithGraphProxy(m, GraphProxyAdmin, { + const { proxy: L2GraphTokenGateway, implementation: L2GraphTokenGatewayImplementation } = deployWithGraphProxy(m, GraphProxyAdmin, { name: 'L2GraphTokenGateway', artifact: GraphTokenGatewayArtifact, initArgs: [Controller], }) m.call(L2GraphTokenGateway, 'setPauseGuardian', [pauseGuardian]) - return { L2GraphTokenGateway } + return { L2GraphTokenGateway, L2GraphTokenGatewayImplementation } }) export const MigrateGraphTokenGatewayModule = buildModule('L2GraphTokenGateway', (m) => { diff --git a/packages/horizon/ignition/modules/periphery/RewardsManager.ts b/packages/horizon/ignition/modules/periphery/RewardsManager.ts index 9fa043ee2..c1dc8e285 100644 --- a/packages/horizon/ignition/modules/periphery/RewardsManager.ts +++ b/packages/horizon/ignition/modules/periphery/RewardsManager.ts @@ -17,7 +17,7 @@ export default buildModule('RewardsManager', (m) => { const subgraphAvailabilityOracle = m.getParameter('subgraphAvailabilityOracle') const subgraphServiceAddress = m.getParameter('subgraphServiceAddress') - const RewardsManager = deployWithGraphProxy(m, GraphProxyAdmin, { + const { proxy: RewardsManager, implementation: RewardsManagerImplementation } = deployWithGraphProxy(m, GraphProxyAdmin, { name: 'RewardsManager', artifact: RewardsManagerArtifact, initArgs: [Controller], @@ -26,7 +26,7 @@ export default buildModule('RewardsManager', (m) => { m.call(RewardsManager, 'setIssuancePerBlock', [issuancePerBlock]) m.call(RewardsManager, 'setSubgraphService', [subgraphServiceAddress]) - return { RewardsManager } + return { RewardsManager, RewardsManagerImplementation } }) export const MigrateRewardsManagerDeployerModule = buildModule('RewardsManagerDeployer', (m: IgnitionModuleBuilder) => { @@ -62,5 +62,5 @@ export const MigrateRewardsManagerGovernorModule = buildModule('RewardsManagerGo const RewardsManager = upgradeGraphProxy(m, GraphProxyAdmin, RewardsManagerProxy, RewardsManagerImplementation, implementationMetadata) m.call(RewardsManager, 'setSubgraphService', [subgraphServiceAddress]) - return { RewardsManager } + return { RewardsManager, RewardsManagerImplementation } }) diff --git a/packages/horizon/ignition/modules/periphery/periphery.ts b/packages/horizon/ignition/modules/periphery/periphery.ts index 276455154..f02f9b2cc 100644 --- a/packages/horizon/ignition/modules/periphery/periphery.ts +++ b/packages/horizon/ignition/modules/periphery/periphery.ts @@ -14,11 +14,11 @@ export default buildModule('GraphHorizon_Periphery', (m) => { const { Controller } = m.useModule(ControllerModule) const { GraphProxyAdmin } = m.useModule(GraphProxyAdminModule) - const { EpochManager } = m.useModule(EpochManagerModule) - const { L2Curation } = m.useModule(CurationModule) - const { RewardsManager } = m.useModule(RewardsManagerModule) - const { L2GraphTokenGateway } = m.useModule(GraphTokenGatewayModule) - const { L2GraphToken } = m.useModule(GraphTokenModule) + const { EpochManager, EpochManagerImplementation } = m.useModule(EpochManagerModule) + const { L2Curation, L2CurationImplementation } = m.useModule(CurationModule) + const { RewardsManager, RewardsManagerImplementation } = m.useModule(RewardsManagerModule) + const { L2GraphTokenGateway, L2GraphTokenGatewayImplementation } = m.useModule(GraphTokenGatewayModule) + const { L2GraphToken, L2GraphTokenImplementation } = m.useModule(GraphTokenModule) m.call(Controller, 'setContractProxy', [ethers.keccak256(ethers.toUtf8Bytes('EpochManager')), EpochManager], { id: 'setContractProxy_EpochManager' }) m.call(Controller, 'setContractProxy', [ethers.keccak256(ethers.toUtf8Bytes('RewardsManager')), RewardsManager], { id: 'setContractProxy_RewardsManager' }) @@ -30,11 +30,16 @@ export default buildModule('GraphHorizon_Periphery', (m) => { return { Controller, EpochManager, + EpochManagerImplementation, L2Curation, + L2CurationImplementation, GraphProxyAdmin, L2GraphToken, + L2GraphTokenImplementation, L2GraphTokenGateway, + L2GraphTokenGatewayImplementation, RewardsManager, + RewardsManagerImplementation, } }) diff --git a/packages/horizon/ignition/modules/proxy/GraphProxy.ts b/packages/horizon/ignition/modules/proxy/GraphProxy.ts index 997ad65c1..efc6fe4ef 100644 --- a/packages/horizon/ignition/modules/proxy/GraphProxy.ts +++ b/packages/horizon/ignition/modules/proxy/GraphProxy.ts @@ -73,5 +73,5 @@ export function deployWithGraphProxy( m.call(proxyAdmin, 'acceptProxyAndCall', [implementation, proxy, m.encodeFunctionCall(implementation, 'initialize', metadata.initArgs)], options) } - return proxy + return { proxy, implementation } } diff --git a/packages/horizon/package.json b/packages/horizon/package.json index 89fda2213..85d6bda27 100644 --- a/packages/horizon/package.json +++ b/packages/horizon/package.json @@ -15,7 +15,7 @@ "lint:sol": "prettier --write contracts/**/*.sol test/**/*.sol && solhint --noPrompt --fix contracts/**/*.sol --config node_modules/solhint-graph-config/index.js", "lint": "yarn lint:ts && yarn lint:sol", "clean": "rm -rf build dist cache cache_forge typechain-types", - "build": "forge build --skip test && BUILD_RUN=true hardhat compile", + "build": "BUILD_RUN=true hardhat compile", "test": "forge test && hardhat test" }, "devDependencies": { @@ -44,7 +44,7 @@ "hardhat-contract-sizer": "^2.10.0", "hardhat-gas-reporter": "^1.0.8", "hardhat-graph-protocol": "workspace:^0.0.1", - "hardhat-secure-accounts": "^1.0.4", + "hardhat-secure-accounts": "^1.0.5", "hardhat-storage-layout": "^0.1.7", "lint-staged": "^15.2.2", "prettier": "^3.2.5", diff --git a/packages/horizon/scripts/deploy.ts b/packages/horizon/scripts/deploy.ts deleted file mode 100644 index 1b6eeac37..000000000 --- a/packages/horizon/scripts/deploy.ts +++ /dev/null @@ -1,21 +0,0 @@ -import hre, { ignition } from 'hardhat' -import { IgnitionHelper } from 'hardhat-graph-protocol/sdk' - -import DeployModule from '../ignition/modules/deploy' - -async function main() { - const HorizonConfig = IgnitionHelper.loadConfig('./ignition/configs/', 'horizon', hre.network.name) - - // Deploy Horizon - const deployment = await ignition.deploy(DeployModule, { - displayUi: true, - parameters: HorizonConfig, - }) - - IgnitionHelper.saveAddressBook(deployment, hre.network.config.chainId) -} - -main().catch((error) => { - console.error(error) - process.exit(1) -}) diff --git a/packages/horizon/scripts/migrate.ts b/packages/horizon/scripts/migrate.ts deleted file mode 100644 index c08f363da..000000000 --- a/packages/horizon/scripts/migrate.ts +++ /dev/null @@ -1,98 +0,0 @@ -import hre, { ignition } from 'hardhat' -import { IgnitionHelper } from 'hardhat-graph-protocol/sdk' - -import MigrateModuleStep1 from '../ignition/modules/migrate/migrate-1' -import MigrateModuleStep2 from '../ignition/modules/migrate/migrate-2' -import MigrateModuleStep3 from '../ignition/modules/migrate/migrate-3' -import MigrateModuleStep4 from '../ignition/modules/migrate/migrate-4' - -async function main() { - console.log(getHorizonBanner()) - const HorizonMigrateConfig = IgnitionHelper.loadConfig('./ignition/configs/', 'horizon-migrate', `horizon-${hre.network.name}`) - - const signers = await hre.ethers.getSigners() - const deployer = signers[0] - const governor = signers[1] - - console.log('Using deployer account:', deployer.address) - console.log('Using governor account:', governor.address) - - console.log('========== Running migration: step 1 ==========') - const { - GraphPaymentsProxy, - PaymentsEscrowProxy, - } = await ignition.deploy(MigrateModuleStep1, { - displayUi: true, - parameters: HorizonMigrateConfig, - deploymentId: `horizon-${hre.network.name}`, - }) - - let patchedHorizonMigrateConfig = IgnitionHelper.patchConfig(HorizonMigrateConfig, { - HorizonProxiesGovernor: { - graphPaymentsAddress: GraphPaymentsProxy.target, - paymentsEscrowAddress: PaymentsEscrowProxy.target, - }, - }) - - console.log('========== Running migration: step 2 ==========') - await ignition.deploy(MigrateModuleStep2, { - displayUi: true, - parameters: patchedHorizonMigrateConfig, - deploymentId: `horizon-${hre.network.name}`, - defaultSender: governor.address, - }) - - console.log('========== Running migration: step 3 ==========') - const deployment = await ignition.deploy(MigrateModuleStep3, { - displayUi: true, - parameters: HorizonMigrateConfig, - deploymentId: `horizon-${hre.network.name}`, - }) - - IgnitionHelper.saveAddressBook(deployment, hre.network.config.chainId) - - patchedHorizonMigrateConfig = IgnitionHelper.patchConfig(patchedHorizonMigrateConfig, { - HorizonStakingGovernor: { - horizonStakingImplementationAddress: deployment.HorizonStakingImplementation.target, - }, - L2CurationGovernor: { - curationImplementationAddress: deployment.L2CurationImplementation.target, - }, - RewardsManagerGovernor: { - rewardsManagerImplementationAddress: deployment.RewardsManagerImplementation.target, - }, - }) - - console.log('========== Running migration: step 4 ==========') - await ignition.deploy(MigrateModuleStep4, { - displayUi: true, - parameters: patchedHorizonMigrateConfig, - deploymentId: `horizon-${hre.network.name}`, - defaultSender: governor.address, - }) - - console.log('Migration successful! šŸŽ‰') -} - -main().catch((error) => { - console.error(error) - process.exit(1) -}) - -function getHorizonBanner(): string { - return ` -ā–ˆā–ˆā•— ā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā•— -ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•”ā•ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•‘ā•šā•ā•ā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā•”ā•ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā•‘ -ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā•‘ ā–ˆā–ˆā–ˆā•”ā• ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•”ā–ˆā–ˆā•— ā–ˆā–ˆā•‘ -ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•‘ ā–ˆā–ˆā–ˆā•”ā• ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ā•šā–ˆā–ˆā•—ā–ˆā–ˆā•‘ -ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā•šā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā•šā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā•‘ ā•šā–ˆā–ˆā–ˆā–ˆā•‘ -ā•šā•ā• ā•šā•ā• ā•šā•ā•ā•ā•ā•ā• ā•šā•ā• ā•šā•ā•ā•šā•ā•ā•šā•ā•ā•ā•ā•ā•ā• ā•šā•ā•ā•ā•ā•ā• ā•šā•ā• ā•šā•ā•ā•ā• - -ā–ˆā–ˆā•— ā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— -ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•”ā•ā•ā•ā•ā• ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•”ā•ā•ā•ā•ā• -ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā•‘ ā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā–ˆā–ˆā–ˆā•— -ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•”ā•ā•ā•ā• ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•”ā•ā•ā• -ā•šā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā•‘ ā•šā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— - ā•šā•ā•ā•ā•ā•ā• ā•šā•ā• ā•šā•ā•ā•ā•ā•ā• ā•šā•ā• ā•šā•ā•ā•šā•ā• ā•šā•ā•ā•šā•ā•ā•ā•ā•ā• ā•šā•ā•ā•ā•ā•ā•ā• -` -} diff --git a/packages/horizon/tasks/deploy.ts b/packages/horizon/tasks/deploy.ts new file mode 100644 index 000000000..d9052745e --- /dev/null +++ b/packages/horizon/tasks/deploy.ts @@ -0,0 +1,177 @@ +/* eslint-disable no-case-declarations */ +import { task, types } from 'hardhat/config' +import { IgnitionHelper } from 'hardhat-graph-protocol/sdk' + +import type { AddressBook } from '../../hardhat-graph-protocol/src/sdk/address-book' +import type { HardhatRuntimeEnvironment } from 'hardhat/types' + +import DeployModule from '../ignition/modules/deploy' + +task('deploy:protocol', 'Deploy a new version of the Graph Protocol Horizon contracts - no data services deployed') + .setAction(async (_, hre: HardhatRuntimeEnvironment) => { + const graph = hre.graph() + + // Load configuration for the deployment + console.log('\n========== āš™ļø Deployment configuration ==========') + const { config: HorizonConfig, file } = IgnitionHelper.loadConfig('./ignition/configs/', 'horizon', hre.network.name) + console.log(`Loaded migration configuration from ${file}`) + + // Display the deployer -- this also triggers the secure accounts prompt if being used + console.log('\n========== šŸ”‘ Deployer account ==========') + const signers = await hre.ethers.getSigners() + const deployer = signers[0] + console.log('Using deployer account:', deployer.address) + const balance = await hre.ethers.provider.getBalance(deployer.address) + console.log('Deployer balance:', hre.ethers.formatEther(balance), 'ETH') + if (balance === 0n) { + console.error('Error: Deployer account has no ETH balance') + process.exit(1) + } + + // Deploy the contracts + console.log(`\n========== šŸš§ Deploy protocol ==========`) + const deployment = await hre.ignition.deploy(DeployModule, { + displayUi: true, + parameters: HorizonConfig, + }) + + // Save the addresses to the address book + console.log('\n========== šŸ“– Updating address book ==========') + IgnitionHelper.saveToAddressBook(deployment, hre.network.config.chainId, graph.horizon!.addressBook) + console.log(`Address book at ${graph.horizon!.addressBook.file} updated!`) + + console.log('\n\nšŸŽ‰ āœØ šŸš€ āœ… Deployment complete! šŸŽ‰ āœØ šŸš€ āœ…') + }) + +task('deploy:migrate', 'Upgrade an existing version of the Graph Protocol v1 to Horizon - no data services deployed') + .addOptionalParam('step', 'Migration step to run (1, 2, 3 or 4)', undefined, types.int) + .addFlag('patchConfig', 'Patch configuration file using address book values - does not save changes') + .setAction(async (args, hre: HardhatRuntimeEnvironment) => { + // Task parameters + const step: number = args.step ?? 0 + const patchConfig: boolean = args.patchConfig ?? false + + const graph = hre.graph() + console.log(getHorizonBanner()) + + // Migration step to run + console.log('\n========== šŸ—ļø Migration steps ==========') + const validSteps = [1, 2, 3, 4] + if (!validSteps.includes(step)) { + console.error(`Error: Invalid migration step provided: ${step}`) + console.error(`Valid steps are: ${validSteps.join(', ')}`) + process.exit(1) + } + console.log(`Running migration step: ${step}`) + + // Load configuration for the migration + console.log('\n========== āš™ļø Deployment configuration ==========') + const { config: HorizonMigrateConfig, file } = IgnitionHelper.loadConfig('./ignition/configs/', 'horizon-migrate', `horizon-${hre.network.name}`) + console.log(`Loaded migration configuration from ${file}`) + + // Display the deployer -- this also triggers the secure accounts prompt if being used + console.log('\n========== šŸ”‘ Deployer account ==========') + const signers = await hre.ethers.getSigners() + const deployer = signers[0] + console.log('Using deployer account:', deployer.address) + const balance = await hre.ethers.provider.getBalance(deployer.address) + console.log('Deployer balance:', hre.ethers.formatEther(balance), 'ETH') + if (balance === 0n) { + console.error('Error: Deployer account has no ETH balance') + process.exit(1) + } + + // Run migration step + console.log(`\n========== šŸš§ Running migration: step ${step} ==========`) + const MigrationModule = require(`../ignition/modules/migrate/migrate-${step}`).default + const deployment = await hre.ignition.deploy( + MigrationModule, + { + displayUi: true, + parameters: patchConfig ? _patchStepConfig(step, HorizonMigrateConfig, graph.horizon!.addressBook, graph.subgraphService!.addressBook) : HorizonMigrateConfig, + deploymentId: `horizon-${hre.network.name}`, + }) + + // Update address book + console.log('\n========== šŸ“– Updating address book ==========') + IgnitionHelper.saveToAddressBook(deployment, hre.network.config.chainId, graph.horizon!.addressBook) + console.log(`Address book at ${graph.horizon!.addressBook.file} updated!`) + + console.log('\n\nšŸŽ‰ āœØ šŸš€ āœ… Migration complete! šŸŽ‰ āœØ šŸš€ āœ…') + }) + +// This function patches the Ignition configuration object using an address book to fill in the gaps +// The resulting configuration is not saved back to the configuration file +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function _patchStepConfig( + step: number, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + config: any, + horizonAddressBook: AddressBook, + subgraphServiceAddressBook: AddressBook, + // eslint-disable-next-line @typescript-eslint/no-explicit-any +): any { + let patchedConfig = config + + switch (step) { + case 2: + const GraphPayments = horizonAddressBook.getEntry('GraphPayments') + const PaymentsEscrow = horizonAddressBook.getEntry('PaymentsEscrow') + patchedConfig = IgnitionHelper.patchConfig(config, { + HorizonProxiesGovernor: { + graphPaymentsAddress: GraphPayments.address, + paymentsEscrowAddress: PaymentsEscrow.address, + }, + }) + break + case 3: + const SubgraphService3 = subgraphServiceAddressBook.getEntry('SubgraphService') + patchedConfig = IgnitionHelper.patchConfig(patchedConfig, { + $global: { + subgraphServiceAddress: SubgraphService3.address, + }, + }) + console.log(patchedConfig) + break + case 4: + const HorizonStaking = horizonAddressBook.getEntry('HorizonStaking') + const L2Curation = horizonAddressBook.getEntry('L2Curation') + const RewardsManager = horizonAddressBook.getEntry('RewardsManager') + const SubgraphService4 = subgraphServiceAddressBook.getEntry('SubgraphService') + patchedConfig = IgnitionHelper.patchConfig(patchedConfig, { + $global: { + subgraphServiceAddress: SubgraphService4.address, + }, + HorizonStakingGovernor: { + horizonStakingImplementationAddress: HorizonStaking.implementation, + }, + L2CurationGovernor: { + curationImplementationAddress: L2Curation.implementation, + }, + RewardsManagerGovernor: { + rewardsManagerImplementationAddress: RewardsManager.implementation, + }, + }) + break + } + + return patchedConfig +} + +function getHorizonBanner(): string { + return ` + ā–ˆā–ˆā•— ā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā•— + ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•”ā•ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•‘ā•šā•ā•ā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā•”ā•ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā•‘ + ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā•‘ ā–ˆā–ˆā–ˆā•”ā• ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•”ā–ˆā–ˆā•— ā–ˆā–ˆā•‘ + ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•‘ ā–ˆā–ˆā–ˆā•”ā• ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ā•šā–ˆā–ˆā•—ā–ˆā–ˆā•‘ + ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā•šā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā•šā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā•‘ ā•šā–ˆā–ˆā–ˆā–ˆā•‘ + ā•šā•ā• ā•šā•ā• ā•šā•ā•ā•ā•ā•ā• ā•šā•ā• ā•šā•ā•ā•šā•ā•ā•šā•ā•ā•ā•ā•ā•ā• ā•šā•ā•ā•ā•ā•ā• ā•šā•ā• ā•šā•ā•ā•ā• + + ā–ˆā–ˆā•— ā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— + ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•”ā•ā•ā•ā•ā• ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•”ā•ā•ā•ā•ā• + ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā•‘ ā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā–ˆā–ˆā–ˆā•— + ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•”ā•ā•ā•ā• ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•”ā•ā•ā• + ā•šā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā•‘ ā•šā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— + ā•šā•ā•ā•ā•ā•ā• ā•šā•ā• ā•šā•ā•ā•ā•ā•ā• ā•šā•ā• ā•šā•ā•ā•šā•ā• ā•šā•ā•ā•šā•ā•ā•ā•ā•ā• ā•šā•ā•ā•ā•ā•ā•ā• + ` +} diff --git a/packages/horizon/types/hardhat-graph-protocol.d.ts b/packages/horizon/types/hardhat-graph-protocol.d.ts new file mode 100644 index 000000000..8b5985269 --- /dev/null +++ b/packages/horizon/types/hardhat-graph-protocol.d.ts @@ -0,0 +1,45 @@ +// TypeScript does not resolve correctly the type extensions when they are symlinked from the same monorepo. +// So we need to re-type it... this file should be a copy of hardhat-graph-protocol/src/type-extensions.ts +import 'hardhat/types/config' +import 'hardhat/types/runtime' +import type { GraphDeployments, GraphRuntimeEnvironment, GraphRuntimeEnvironmentOptions } from 'hardhat-graph-protocol' + +declare module 'hardhat/types/runtime' { + interface HardhatRuntimeEnvironment { + graph: (opts?: GraphRuntimeEnvironmentOptions) => GraphRuntimeEnvironment + } +} + +declare module 'hardhat/types/config' { + interface HardhatConfig { + graph: GraphRuntimeEnvironmentOptions + } + + interface HardhatUserConfig { + graph: GraphRuntimeEnvironmentOptions + } + + interface HardhatNetworkConfig { + deployments?: GraphDeployments + } + + interface HardhatNetworkUserConfig { + deployments?: GraphDeployments + } + + interface HttpNetworkConfig { + deployments?: GraphDeployments + } + + interface HttpNetworkUserConfig { + deployments?: GraphDeployments + } + + interface ProjectPathsConfig { + graph?: string + } + + interface ProjectPathsUserConfig { + graph?: string + } +} diff --git a/packages/subgraph-service/README.md b/packages/subgraph-service/README.md index dbd580740..19522dfcd 100644 --- a/packages/subgraph-service/README.md +++ b/packages/subgraph-service/README.md @@ -2,16 +2,52 @@ The Subgraph Service is a data service designed to work with Graph Horizon that supports indexing subgraphs and serving queries to consumers. -## Deployment +## Configuration -We use Hardhat Ignition to deploy the contracts. To build and deploy the Subgraph Service run the following commands: +The following environment variables might be required: + +| Variable | Description | +|----------|-------------| +| `ARBISCAN_API_KEY` | Arbiscan API key - for contract verification| +| `ARBITRUM_ONE_RPC` | Arbitrum One RPC URL - defaults to `https://arb1.arbitrum.io/rpc` | +| `ARBITRUM_SEPOLIA_RPC` | Arbitrum Sepolia RPC URL - defaults to `https://sepolia-rollup.arbitrum.io/rpc` | +| `LOCALHOST_RPC` | Localhost RPC URL - defaults to `http://localhost:8545` | +| `LOCALHOST_CHAIN_ID` | Localhost chain ID - defaults to `31337` | +You can set them using Hardhat: + +```bash +npx hardhat vars set +``` + +## Build ```bash yarn install yarn build -npx hardhat run scripts/deploy.ts --network hardhat ``` -You can use any network defined in `hardhat.config.ts` by replacing `hardhat` with the network name. +## Deployment + +Note that this instructions will help you deploy Graph Horizon contracts alongside the Subgraph Service. If you want to deploy just the core Horizon contracts please refer to the [Horizon README](../horizon/README.md) for deploy instructions. + +### New deployment +To deploy Graph Horizon from scratch including the Subgraph Service run the following command: + +```bash +npx hardhat deploy:protocol --network hardhat +``` + +### Upgrade deployment +Usually you would run this against a network (or a fork) where the original Graph Protocol was previously deployed. To upgrade an existing deployment of the original Graph Protocol to Graph Horizon including the Subgraph Service, run the following commands. Note that some steps might need to be run by different accounts (deployer vs governor): + +```bash +cd ../ +cd horizon && npx hardhat deploy:migrate --network hardhat --step 1 && cd .. +cd subgraph-service && npx hardhat deploy:migrate --network hardhat --step 1 && cd .. +cd horizon && npx hardhat deploy:migrate --network hardhat --step 2 && cd .. # Run with governor. Optionally add --patch-config +cd horizon && npx hardhat deploy:migrate --network hardhat --step 3 && cd .. # Optionally add --patch-config +cd subgraph-service && npx hardhat deploy:migrate --network hardhat --step 2 && cd .. # Optionally add --patch-config +cd horizon && npx hardhat deploy:migrate --network hardhat --step 4 && cd .. # Run with governor. Optionally add --patch-config +``` -Note that this will deploy and configure Graph Horizon contracts as well as the Subgraph Service. \ No newline at end of file +Horizon Steps 2, 3 and 4, and Subgraph Service Step 2 require patching the configuration file with addresses from previous steps. The files are located in the `ignition/configs` directory and need to be manually edited. You can also pass `--patch-config` flag to the deploy command to automatically patch the configuration reading values from the address book. Note that this will NOT update the configuration file. \ No newline at end of file diff --git a/packages/subgraph-service/addresses.json b/packages/subgraph-service/addresses.json index 0fb2088ec..7a73a41bf 100644 --- a/packages/subgraph-service/addresses.json +++ b/packages/subgraph-service/addresses.json @@ -1,34 +1,2 @@ { - "421614": { - "SubgraphService": { - "address": "0x2268247782f4b7AdB2DA810EFD6e43B27a37af54" - }, - "DisputeManager": { - "address": "0x8e436E815226C8Bd5e775C7FF693DAe6a94bE7d1" - }, - "ServiceRegistry": { - "address": "0x888541878CbDDEd880Cd58c728f1Af5C47343F86", - "proxy": true, - "implementation": { - "address": "0x05E732280bf9F37054346Cb83f5Fd58C5B44F6A8" - } - }, - "L2Curation": { - "address": "0xDe761f075200E75485F4358978FB4d1dC8644FD5", - "proxy": true, - "implementation": { - "address": "0xd90022aB67920212D0F902F5c427DE82732DE136" - } - }, - "SubgraphNFT": { - "address": "0xF21Df5BbA7EB9b54D8F60C560aFb9bA63e6aED1A" - }, - "L2GNS": { - "address": "0x3133948342F35b8699d8F94aeE064AbB76eDe965", - "proxy": true, - "implementation": { - "address": "0x00CBF5024d454255577Bf2b0fB6A43328a6828c9" - } - } - } -} +} \ No newline at end of file diff --git a/packages/subgraph-service/hardhat.config.ts b/packages/subgraph-service/hardhat.config.ts index 8239cb494..9660e89b1 100644 --- a/packages/subgraph-service/hardhat.config.ts +++ b/packages/subgraph-service/hardhat.config.ts @@ -13,6 +13,7 @@ import 'solidity-docgen' // Skip importing hardhat-graph-protocol when building the project, it has circular dependency if (process.env.BUILD_RUN !== 'true') { require('hardhat-graph-protocol') + require('./tasks/deploy') } const config: HardhatUserConfig = { @@ -26,12 +27,6 @@ const config: HardhatUserConfig = { }, }, }, - graph: { - deployments: { - ...hardhatBaseConfig.graph?.deployments, - subgraphService: './addresses.json', - }, - }, } export default config diff --git a/packages/subgraph-service/ignition/configs/subgraph-service-migrate.default.json5 b/packages/subgraph-service/ignition/configs/subgraph-service-migrate.default.json5 index d8ce79391..8f3c78b29 100644 --- a/packages/subgraph-service/ignition/configs/subgraph-service-migrate.default.json5 +++ b/packages/subgraph-service/ignition/configs/subgraph-service-migrate.default.json5 @@ -1,29 +1,34 @@ { "$global": { - // Accounts "governor": "0x72ee30d43Fb5A90B3FE983156C5d2fBE6F6d07B3", "arbitrator": "0xFFcf8FDEE72ac11b5c542428B35EEF5769C409f0", // Addresses for contracts deployed in the original Graph Protocol "controllerAddress": "0x9DB3ee191681f092607035d9BDA6e59FbEaCa695", - "curationAddress": "0xDe761f075200E75485F4358978FB4d1dC8644FD5", - "graphTallyCollectorAddress": "0x0000000000000000000000000000000000000000", - // Must be set for step 2 of the migration - "disputeManagerAddress": "0x0000000000000000000000000000000000000000", - "disputeManagerProxyAdminAddress": "0x0000000000000000000000000000000000000000", - "subgraphServiceAddress": "0x0000000000000000000000000000000000000000", - "subgraphServiceProxyAdminAddress": "0x0000000000000000000000000000000000000000" + // Must be set for step 2 of the deployment + "disputeManagerProxyAddress": "" }, "DisputeManager": { "disputePeriod": 2419200, "disputeDeposit": "10000000000000000000000n", "fishermanRewardCut": 500000, - "maxSlashingCut": 1000000 + "maxSlashingCut": 1000000, + + // Must be set for step 2 of the deployment + "disputeManagerProxyAdminAddress": "" }, "SubgraphService": { "minimumProvisionTokens": "100000000000000000000000n", "maximumDelegationRatio": 16, - "stakeToFeesRatio": 2 + "stakeToFeesRatio": 2, + + // Addresses for contracts deployed in the original Graph Protocol + "curationAddress": "0xDe761f075200E75485F4358978FB4d1dC8644FD5", + + // Must be set for step 2 of the deployment + "subgraphServiceProxyAddress": "", + "subgraphServiceProxyAdminAddress": "", + "graphTallyCollectorAddress": "" } } diff --git a/packages/subgraph-service/ignition/configs/subgraph-service.default.json5 b/packages/subgraph-service/ignition/configs/subgraph-service.default.json5 index 12db4cf12..2fd75fcbc 100644 --- a/packages/subgraph-service/ignition/configs/subgraph-service.default.json5 +++ b/packages/subgraph-service/ignition/configs/subgraph-service.default.json5 @@ -1,16 +1,31 @@ { "$global": { - "arbitrator": "0xFFcf8FDEE72ac11b5c542428B35EEF5769C409f0" + "governor": "0x72ee30d43Fb5A90B3FE983156C5d2fBE6F6d07B3", + "arbitrator": "0xFFcf8FDEE72ac11b5c542428B35EEF5769C409f0", + + // Must be set for step 2 of the deployment + "controllerAddress": "", + "disputeManagerProxyAddress": "", + "curationAddress": "", + "curationImplementationAddress": "" }, "DisputeManager": { "disputePeriod": 2419200, "disputeDeposit": "10000000000000000000000n", "fishermanRewardCut": 500000, - "maxSlashingCut": 1000000 + "maxSlashingCut": 1000000, + + // Must be set for step 2 of the deployment + "disputeManagerProxyAdminAddress": "", }, "SubgraphService": { "minimumProvisionTokens": "100000000000000000000000n", "maximumDelegationRatio": 16, - "stakeToFeesRatio": 2 + "stakeToFeesRatio": 2, + + // Must be set for step 2 of the deployment + "subgraphServiceProxyAddress": "", + "subgraphServiceProxyAdminAddress": "", + "graphTallyCollectorAddress": "" } } diff --git a/packages/subgraph-service/ignition/modules/Curation.ts b/packages/subgraph-service/ignition/modules/Curation.ts new file mode 100644 index 000000000..9851a86ed --- /dev/null +++ b/packages/subgraph-service/ignition/modules/Curation.ts @@ -0,0 +1,17 @@ +import { buildModule } from '@nomicfoundation/ignition-core' + +import CurationArtifact from '@graphprotocol/contracts/build/contracts/contracts/l2/curation/L2Curation.sol/L2Curation.json' + +// Note that this module is a no-op, we only run it to get curation addresses into the address book. +// Curation deployment should be managed by ignition scripts in subgraph-service package however +// due to tight coupling with HorizonStakingExtension contract it's easier to do it on the horizon package. +// Once the transition period is over we can migrate it. +export default buildModule('L2Curation', (m) => { + const curationAddress = m.getParameter('curationAddress') + const curationImplementationAddress = m.getParameter('curationImplementationAddress') + + const L2Curation = m.contractAt('L2CurationAddressBook', CurationArtifact, curationAddress) + const L2CurationImplementation = m.contractAt('L2CurationImplementationAddressBook', CurationArtifact, curationImplementationAddress) + + return { L2Curation, L2CurationImplementation } +}) diff --git a/packages/subgraph-service/ignition/modules/DisputeManager.ts b/packages/subgraph-service/ignition/modules/DisputeManager.ts index 0da9b7dad..e1a456059 100644 --- a/packages/subgraph-service/ignition/modules/DisputeManager.ts +++ b/packages/subgraph-service/ignition/modules/DisputeManager.ts @@ -2,13 +2,14 @@ import { buildModule } from '@nomicfoundation/hardhat-ignition/modules' import { deployImplementation } from '@graphprotocol/horizon/ignition/modules/proxy/implementation' import { upgradeTransparentUpgradeableProxy } from '@graphprotocol/horizon/ignition/modules/proxy/TransparentUpgradeableProxy' +import DisputeManagerArtifact from '../../build/contracts/contracts/DisputeManager.sol/DisputeManager.json' import ProxyAdminArtifact from '@openzeppelin/contracts/build/contracts/ProxyAdmin.json' import TransparentUpgradeableProxyArtifact from '@openzeppelin/contracts/build/contracts/TransparentUpgradeableProxy.json' export default buildModule('DisputeManager', (m) => { const governor = m.getParameter('governor') const controllerAddress = m.getParameter('controllerAddress') - const disputeManagerAddress = m.getParameter('disputeManagerAddress') + const disputeManagerProxyAddress = m.getParameter('disputeManagerProxyAddress') const disputeManagerProxyAdminAddress = m.getParameter('disputeManagerProxyAdminAddress') const arbitrator = m.getParameter('arbitrator') const disputePeriod = m.getParameter('disputePeriod') @@ -17,7 +18,7 @@ export default buildModule('DisputeManager', (m) => { const maxSlashingCut = m.getParameter('maxSlashingCut') const DisputeManagerProxyAdmin = m.contractAt('ProxyAdmin', ProxyAdminArtifact, disputeManagerProxyAdminAddress) - const DisputeManagerProxy = m.contractAt('DisputeManager', TransparentUpgradeableProxyArtifact, disputeManagerAddress) + const DisputeManagerProxy = m.contractAt('DisputeManagerProxy', TransparentUpgradeableProxyArtifact, disputeManagerProxyAddress) // Deploy implementation const DisputeManagerImplementation = deployImplementation(m, { @@ -31,6 +32,7 @@ export default buildModule('DisputeManager', (m) => { DisputeManagerProxy, DisputeManagerImplementation, { name: 'DisputeManager', + artifact: DisputeManagerArtifact, initArgs: [ arbitrator, disputePeriod, @@ -42,5 +44,8 @@ export default buildModule('DisputeManager', (m) => { m.call(DisputeManagerProxyAdmin, 'transferOwnership', [governor], { after: [DisputeManager] }) - return { DisputeManager, DisputeManagerImplementation } + return { + DisputeManager, + DisputeManagerImplementation, + } }) diff --git a/packages/subgraph-service/ignition/modules/Proxies.ts b/packages/subgraph-service/ignition/modules/Proxies.ts index 92971ca90..7fe2c8960 100644 --- a/packages/subgraph-service/ignition/modules/Proxies.ts +++ b/packages/subgraph-service/ignition/modules/Proxies.ts @@ -22,9 +22,9 @@ export default buildModule('SubgraphServiceProxies', (m) => { }) return { - DisputeManagerProxy, - DisputeManagerProxyAdmin, SubgraphServiceProxy, SubgraphServiceProxyAdmin, + DisputeManagerProxy, + DisputeManagerProxyAdmin, } }) diff --git a/packages/subgraph-service/ignition/modules/SubgraphService.ts b/packages/subgraph-service/ignition/modules/SubgraphService.ts index af90e8bf7..a4883a1b0 100644 --- a/packages/subgraph-service/ignition/modules/SubgraphService.ts +++ b/packages/subgraph-service/ignition/modules/SubgraphService.ts @@ -3,6 +3,7 @@ import { deployImplementation } from '@graphprotocol/horizon/ignition/modules/pr import { upgradeTransparentUpgradeableProxy } from '@graphprotocol/horizon/ignition/modules/proxy/TransparentUpgradeableProxy' import ProxyAdminArtifact from '@openzeppelin/contracts/build/contracts/ProxyAdmin.json' +import SubgraphServiceArtifact from '../../build/contracts/contracts/SubgraphService.sol/SubgraphService.json' import TransparentUpgradeableProxyArtifact from '@openzeppelin/contracts/build/contracts/TransparentUpgradeableProxy.json' export default buildModule('SubgraphService', (m) => { @@ -10,7 +11,7 @@ export default buildModule('SubgraphService', (m) => { const controllerAddress = m.getParameter('controllerAddress') const subgraphServiceProxyAddress = m.getParameter('subgraphServiceProxyAddress') const subgraphServiceProxyAdminAddress = m.getParameter('subgraphServiceProxyAdminAddress') - const disputeManagerAddress = m.getParameter('disputeManagerAddress') + const disputeManagerProxyAddress = m.getParameter('disputeManagerProxyAddress') const graphTallyCollectorAddress = m.getParameter('graphTallyCollectorAddress') const curationAddress = m.getParameter('curationAddress') const minimumProvisionTokens = m.getParameter('minimumProvisionTokens') @@ -18,12 +19,12 @@ export default buildModule('SubgraphService', (m) => { const stakeToFeesRatio = m.getParameter('stakeToFeesRatio') const SubgraphServiceProxyAdmin = m.contractAt('ProxyAdmin', ProxyAdminArtifact, subgraphServiceProxyAdminAddress) - const SubgraphServiceProxy = m.contractAt('SubgraphService', TransparentUpgradeableProxyArtifact, subgraphServiceProxyAddress) + const SubgraphServiceProxy = m.contractAt('SubgraphServiceProxy', TransparentUpgradeableProxyArtifact, subgraphServiceProxyAddress) // Deploy implementation const SubgraphServiceImplementation = deployImplementation(m, { name: 'SubgraphService', - constructorArgs: [controllerAddress, disputeManagerAddress, graphTallyCollectorAddress, curationAddress], + constructorArgs: [controllerAddress, disputeManagerProxyAddress, graphTallyCollectorAddress, curationAddress], }) // Upgrade implementation @@ -32,6 +33,7 @@ export default buildModule('SubgraphService', (m) => { SubgraphServiceProxy, SubgraphServiceImplementation, { name: 'SubgraphService', + artifact: SubgraphServiceArtifact, initArgs: [ minimumProvisionTokens, maximumDelegationRatio, @@ -41,5 +43,8 @@ export default buildModule('SubgraphService', (m) => { m.call(SubgraphServiceProxyAdmin, 'transferOwnership', [governor], { after: [SubgraphService] }) - return { SubgraphService, SubgraphServiceImplementation } + return { + SubgraphService, + SubgraphServiceImplementation, + } }) diff --git a/packages/subgraph-service/ignition/modules/deploy/deploy-1.ts b/packages/subgraph-service/ignition/modules/deploy/deploy-1.ts new file mode 100644 index 000000000..027c4563f --- /dev/null +++ b/packages/subgraph-service/ignition/modules/deploy/deploy-1.ts @@ -0,0 +1,19 @@ +import { buildModule } from '@nomicfoundation/hardhat-ignition/modules' + +import ProxiesModule from '../Proxies' + +export default buildModule('SubgraphService_Deploy_1', (m) => { + const { + SubgraphServiceProxy, + SubgraphServiceProxyAdmin, + DisputeManagerProxy, + DisputeManagerProxyAdmin, + } = m.useModule(ProxiesModule) + + return { + Transparent_Proxy_SubgraphService: SubgraphServiceProxy, + Transparent_ProxyAdmin_SubgraphService: SubgraphServiceProxyAdmin, + Transparent_Proxy_DisputeManager: DisputeManagerProxy, + Transparent_ProxyAdmin_DisputeManager: DisputeManagerProxyAdmin, + } +}) diff --git a/packages/subgraph-service/ignition/modules/deploy/deploy-2.ts b/packages/subgraph-service/ignition/modules/deploy/deploy-2.ts new file mode 100644 index 000000000..6f9ca65c5 --- /dev/null +++ b/packages/subgraph-service/ignition/modules/deploy/deploy-2.ts @@ -0,0 +1,19 @@ +import { buildModule } from '@nomicfoundation/hardhat-ignition/modules' + +import CurationModule from '../Curation' +import DisputeManagerModule from '../DisputeManager' +import SubgraphServiceModule from '../SubgraphService' + +export default buildModule('SubgraphService_Deploy_2', (m) => { + const { DisputeManager, DisputeManagerImplementation } = m.useModule(DisputeManagerModule) + const { SubgraphService, SubgraphServiceImplementation } = m.useModule(SubgraphServiceModule) + const { L2Curation, L2CurationImplementation } = m.useModule(CurationModule) + return { + Transparent_Proxy_DisputeManager: DisputeManager, + Implementation_DisputeManager: DisputeManagerImplementation, + Transparent_Proxy_SubgraphService: SubgraphService, + Implementation_SubgraphService: SubgraphServiceImplementation, + Graph_Proxy_L2Curation: L2Curation, + Implementation_L2Curation: L2CurationImplementation, + } +}) diff --git a/packages/subgraph-service/ignition/modules/migrate/migrate-1.ts b/packages/subgraph-service/ignition/modules/migrate/migrate-1.ts index 48a189d81..66b4bf74c 100644 --- a/packages/subgraph-service/ignition/modules/migrate/migrate-1.ts +++ b/packages/subgraph-service/ignition/modules/migrate/migrate-1.ts @@ -11,9 +11,9 @@ export default buildModule('SubgraphService_Migrate_1', (m) => { } = m.useModule(ProxiesModule) return { - SubgraphServiceProxy, - SubgraphServiceProxyAdmin, - DisputeManagerProxy, - DisputeManagerProxyAdmin, + Transparent_Proxy_SubgraphService: SubgraphServiceProxy, + Transparent_ProxyAdmin_SubgraphService: SubgraphServiceProxyAdmin, + Transparent_Proxy_DisputeManager: DisputeManagerProxy, + Transparent_ProxyAdmin_DisputeManager: DisputeManagerProxyAdmin, } }) diff --git a/packages/subgraph-service/ignition/modules/migrate/migrate-2.ts b/packages/subgraph-service/ignition/modules/migrate/migrate-2.ts index 772c46973..e50115193 100644 --- a/packages/subgraph-service/ignition/modules/migrate/migrate-2.ts +++ b/packages/subgraph-service/ignition/modules/migrate/migrate-2.ts @@ -4,8 +4,13 @@ import DisputeManagerModule from '../DisputeManager' import SubgraphServiceModule from '../SubgraphService' export default buildModule('SubgraphService_Migrate_2', (m) => { - const { DisputeManager } = m.useModule(DisputeManagerModule) - const { SubgraphService } = m.useModule(SubgraphServiceModule) + const { DisputeManager, DisputeManagerImplementation } = m.useModule(DisputeManagerModule) + const { SubgraphService, SubgraphServiceImplementation } = m.useModule(SubgraphServiceModule) - return { DisputeManager, SubgraphService } + return { + Transparent_Proxy_DisputeManager: DisputeManager, + Transparent_ProxyAdmin_DisputeManager: DisputeManagerImplementation, + Transparent_Proxy_SubgraphService: SubgraphService, + Transparent_ProxyAdmin_SubgraphService: SubgraphServiceImplementation, + } }) diff --git a/packages/subgraph-service/package.json b/packages/subgraph-service/package.json index ade35a093..8ef39142d 100644 --- a/packages/subgraph-service/package.json +++ b/packages/subgraph-service/package.json @@ -15,7 +15,7 @@ "lint:sol": "prettier --write contracts/**/*.sol test/**/*.sol && solhint --noPrompt --fix contracts/**/*.sol --config node_modules/solhint-graph-config/index.js", "lint": "yarn lint:ts && yarn lint:sol", "clean": "rm -rf build dist cache cache_forge typechain-types", - "build": "forge build --skip test && BUILD_RUN=true hardhat compile", + "build": "BUILD_RUN=true hardhat compile", "test": "forge test && hardhat test" }, "devDependencies": { @@ -45,7 +45,7 @@ "hardhat-contract-sizer": "^2.10.0", "hardhat-gas-reporter": "^1.0.8", "hardhat-graph-protocol": "workspace:^0.0.1", - "hardhat-secure-accounts": "^1.0.4", + "hardhat-secure-accounts": "^1.0.5", "hardhat-storage-layout": "^0.1.7", "json5": "^2.2.3", "lint-staged": "^15.2.2", diff --git a/packages/subgraph-service/scripts/deploy.ts b/packages/subgraph-service/scripts/deploy.ts deleted file mode 100644 index 00867588f..000000000 --- a/packages/subgraph-service/scripts/deploy.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* eslint-disable no-prototype-builtins */ -/* eslint-disable @typescript-eslint/no-unsafe-return */ -/* eslint-disable @typescript-eslint/no-unused-vars */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -require('json5/lib/register') - -import hre, { ignition } from 'hardhat' -import { IgnitionHelper } from 'hardhat-graph-protocol/sdk' - -import DisputeManagerModule from '../ignition/modules/DisputeManager' -import HorizonModule from '@graphprotocol/horizon/ignition/modules/deploy' -import SubgraphServiceModule from '../ignition/modules/SubgraphService' -import SubgraphServiceProxiesModule from '../ignition/modules/Proxies' - -// Horizon needs the SubgraphService proxy address before it can be deployed -// But SubgraphService and DisputeManager implementations need Horizon... -// So the deployment order is: -// - Deploy SubgraphService and DisputeManager proxies -// - Deploy Horizon -// - Deploy SubgraphService and DisputeManager implementations -async function main() { - const SubgraphServiceConfig = IgnitionHelper.loadConfig('./ignition/configs/', 'subgraph-service', hre.network.name) - const HorizonConfig = IgnitionHelper.loadConfig('./node_modules/@graphprotocol/horizon/ignition/configs', 'horizon', hre.network.name) - - // Deploy proxies - const { - DisputeManagerProxy, - DisputeManagerProxyAdmin, - SubgraphServiceProxy, - SubgraphServiceProxyAdmin, - } = await ignition.deploy(SubgraphServiceProxiesModule, { - displayUi: true, - }) - - // Deploy Horizon - const { Controller, GraphTallyCollector, L2Curation } = await ignition.deploy(HorizonModule, { - displayUi: true, - parameters: IgnitionHelper.patchConfig(HorizonConfig, { - SubgraphService: { - subgraphServiceProxyAddress: SubgraphServiceProxy.target as string, - }, - }), - }) - - // Deploy DisputeManager implementation - await ignition.deploy(DisputeManagerModule, { - displayUi: true, - parameters: IgnitionHelper.mergeConfigs(SubgraphServiceConfig, { - DisputeManager: { - controllerAddress: Controller.target as string, - disputeManagerProxyAddress: DisputeManagerProxy.target as string, - disputeManagerProxyAdminAddress: DisputeManagerProxyAdmin.target as string, - }, - }), - }) - - // Deploy SubgraphService implementation - await ignition.deploy(SubgraphServiceModule, { - displayUi: true, - parameters: IgnitionHelper.mergeConfigs(SubgraphServiceConfig, { - SubgraphService: { - controllerAddress: Controller.target as string, - subgraphServiceProxyAddress: SubgraphServiceProxy.target as string, - subgraphServiceProxyAdminAddress: SubgraphServiceProxyAdmin.target as string, - disputeManagerAddress: DisputeManagerProxy.target as string, - graphTallyCollectorAddress: GraphTallyCollector.target as string, - curationAddress: L2Curation.target as string, - }, - }), - }) -} - -main().catch((error) => { - console.error(error) - process.exit(1) -}) diff --git a/packages/subgraph-service/scripts/migrate.ts b/packages/subgraph-service/scripts/migrate.ts deleted file mode 100644 index fd5dddcbc..000000000 --- a/packages/subgraph-service/scripts/migrate.ts +++ /dev/null @@ -1,50 +0,0 @@ -/* eslint-disable no-prototype-builtins */ -/* eslint-disable @typescript-eslint/no-unsafe-return */ -/* eslint-disable @typescript-eslint/no-unused-vars */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -require('json5/lib/register') - -import hre, { ignition } from 'hardhat' -import { IgnitionHelper } from 'hardhat-graph-protocol/sdk' - -import MigrateStep1 from '../ignition/modules/migrate/migrate-1' -import MigrateStep2 from '../ignition/modules/migrate/migrate-2' - -// Horizon needs the SubgraphService proxy address before it can be deployed -// But SubgraphService and DisputeManager implementations need Horizon... -// So the deployment order is: -// - Deploy SubgraphService and DisputeManager proxies -// - Deploy Horizon -// - Deploy SubgraphService and DisputeManager implementations -async function main() { - const SubgraphServiceConfig = IgnitionHelper.loadConfig('./ignition/configs/', 'subgraph-service', hre.network.name) - - // Deploy proxies - const { - DisputeManagerProxy, - DisputeManagerProxyAdmin, - SubgraphServiceProxy, - SubgraphServiceProxyAdmin, - } = await ignition.deploy(MigrateStep1, { - displayUi: true, - }) - - const PatchedSubgraphServiceConfig = IgnitionHelper.mergeConfigs(SubgraphServiceConfig, { - $global: { - subgraphServiceAddress: SubgraphServiceProxy.target as string, - subgraphServiceProxyAdminAddress: SubgraphServiceProxyAdmin.target as string, - disputeManagerAddress: DisputeManagerProxy.target as string, - disputeManagerProxyAdminAddress: DisputeManagerProxyAdmin.target as string, - }, - }) - - await ignition.deploy(MigrateStep2, { - displayUi: true, - parameters: PatchedSubgraphServiceConfig, - deploymentId: `subgraph-service-${hre.network.name}`, - }) -} -main().catch((error) => { - console.error(error) - process.exit(1) -}) diff --git a/packages/subgraph-service/tasks/deploy.ts b/packages/subgraph-service/tasks/deploy.ts new file mode 100644 index 000000000..03a2c800b --- /dev/null +++ b/packages/subgraph-service/tasks/deploy.ts @@ -0,0 +1,203 @@ +/* eslint-disable no-case-declarations */ +import { task, types } from 'hardhat/config' +import { IgnitionHelper } from 'hardhat-graph-protocol/sdk' + +import type { AddressBook } from '../../hardhat-graph-protocol/src/sdk/address-book' +import type { HardhatRuntimeEnvironment } from 'hardhat/types' + +import Deploy1Module from '../ignition/modules/deploy/deploy-1' +import Deploy2Module from '../ignition/modules/deploy/deploy-2' +import HorizonModule from '@graphprotocol/horizon/ignition/modules/deploy' + +// Horizon needs the SubgraphService proxy address before it can be deployed +// But SubgraphService and DisputeManager implementations need Horizon... +// So the deployment order is: +// - Deploy SubgraphService and DisputeManager proxies +// - Deploy Horizon +// - Deploy SubgraphService and DisputeManager implementations +task('deploy:protocol', 'Deploy a new version of the Graph Protocol Horizon contracts - with Subgraph Service') + .setAction(async (_, hre: HardhatRuntimeEnvironment) => { + const graph = hre.graph() + + // Load configuration files for the deployment + console.log('\n========== āš™ļø Deployment configuration ==========') + const { config: HorizonConfig, file: horizonFile } = IgnitionHelper.loadConfig('./node_modules/@graphprotocol/horizon/ignition/configs', 'horizon', hre.network.name) + const { config: SubgraphServiceConfig, file: subgraphServiceFile } = IgnitionHelper.loadConfig('./ignition/configs/', 'subgraph-service', hre.network.name) + console.log(`Loaded Horizon migration configuration from ${horizonFile}`) + console.log(`Loaded Subgraph Service migration configuration from ${subgraphServiceFile}`) + + // Display the deployer -- this also triggers the secure accounts prompt if being used + console.log('\n========== šŸ”‘ Deployer account ==========') + const signers = await hre.ethers.getSigners() + const deployer = signers[0] + console.log('Using deployer account:', deployer.address) + const balance = await hre.ethers.provider.getBalance(deployer.address) + console.log('Deployer balance:', hre.ethers.formatEther(balance), 'ETH') + if (balance === 0n) { + console.error('Error: Deployer account has no ETH balance') + process.exit(1) + } + + // 1. Deploy SubgraphService and DisputeManager proxies + console.log(`\n========== šŸš§ SubgraphService and DisputeManager proxies ==========`) + const proxiesDeployment = await hre.ignition.deploy(Deploy1Module, { + displayUi: true, + parameters: SubgraphServiceConfig, + }) + + // 2. Deploy Horizon + console.log(`\n========== šŸš§ Deploy Horizon ==========`) + const horizonDeployment = await hre.ignition.deploy(HorizonModule, { + displayUi: true, + parameters: IgnitionHelper.patchConfig(HorizonConfig, { + SubgraphService: { + subgraphServiceProxyAddress: proxiesDeployment.Transparent_Proxy_SubgraphService.target as string, + }, + }), + }) + + // 3. Deploy SubgraphService and DisputeManager implementations + console.log(`\n========== šŸš§ Deploy SubgraphService implementations and upgrade them ==========`) + const subgraphServiceDeployment = await hre.ignition.deploy(Deploy2Module, { + displayUi: true, + parameters: IgnitionHelper.patchConfig(SubgraphServiceConfig, { + $global: { + controllerAddress: horizonDeployment.Controller.target as string, + disputeManagerProxyAddress: proxiesDeployment.Transparent_Proxy_DisputeManager.target as string, + curationAddress: horizonDeployment.Graph_Proxy_L2Curation.target as string, + curationImplementationAddress: horizonDeployment.Implementation_L2Curation.target as string, + }, + DisputeManager: { + disputeManagerProxyAdminAddress: proxiesDeployment.Transparent_ProxyAdmin_DisputeManager.target as string, + }, + SubgraphService: { + subgraphServiceProxyAddress: proxiesDeployment.Transparent_Proxy_SubgraphService.target as string, + subgraphServiceProxyAdminAddress: proxiesDeployment.Transparent_ProxyAdmin_SubgraphService.target as string, + graphTallyCollectorAddress: horizonDeployment.GraphTallyCollector.target as string, + }, + }), + }) + + // Save the addresses to the address book + console.log('\n========== šŸ“– Updating address book ==========') + IgnitionHelper.saveToAddressBook(horizonDeployment, hre.network.config.chainId, graph.horizon!.addressBook) + IgnitionHelper.saveToAddressBook(proxiesDeployment, hre.network.config.chainId, graph.subgraphService!.addressBook) + IgnitionHelper.saveToAddressBook(subgraphServiceDeployment, hre.network.config.chainId, graph.subgraphService!.addressBook) + console.log(`Address book at ${graph.horizon!.addressBook.file} updated!`) + console.log(`Address book at ${graph.subgraphService!.addressBook.file} updated!`) + console.log('Note that Horizon deployment addresses are updated in the Horizon address book') + + console.log('\n\nšŸŽ‰ āœØ šŸš€ āœ… Deployment complete! šŸŽ‰ āœØ šŸš€ āœ…') + }) + +task('deploy:migrate', 'Deploy the Subgraph Service on an existing Horizon deployment') + .addOptionalParam('step', 'Migration step to run (1, 2)', undefined, types.int) + .addFlag('patchConfig', 'Patch configuration file using address book values - does not save changes') + .setAction(async (args, hre: HardhatRuntimeEnvironment) => { + // Task parameters + const step: number = args.step ?? 0 + const patchConfig: boolean = args.patchConfig ?? false + + const graph = hre.graph() + console.log(getHorizonBanner()) + + // Migration step to run + console.log('\n========== šŸ—ļø Migration steps ==========') + const validSteps = [1, 2] + if (!validSteps.includes(step)) { + console.error(`Error: Invalid migration step provided: ${step}`) + console.error(`Valid steps are: ${validSteps.join(', ')}`) + process.exit(1) + } + console.log(`Running migration step: ${step}`) + + // Load configuration for the migration + console.log('\n========== āš™ļø Deployment configuration ==========') + const { config: SubgraphServiceMigrateConfig, file } = IgnitionHelper.loadConfig('./ignition/configs/', 'subgraph-service-migrate', `subgraph-service-${hre.network.name}`) + console.log(`Loaded migration configuration from ${file}`) + + // Display the deployer -- this also triggers the secure accounts prompt if being used + console.log('\n========== šŸ”‘ Deployer account ==========') + const signers = await hre.ethers.getSigners() + const deployer = signers[0] + console.log('Using deployer account:', deployer.address) + const balance = await hre.ethers.provider.getBalance(deployer.address) + console.log('Deployer balance:', hre.ethers.formatEther(balance), 'ETH') + if (balance === 0n) { + console.error('Error: Deployer account has no ETH balance') + process.exit(1) + } + + // Run migration step + console.log(`\n========== šŸš§ Running migration: step ${step} ==========`) + const MigrationModule = require(`../ignition/modules/migrate/migrate-${step}`).default + const deployment = await hre.ignition.deploy( + MigrationModule, + { + displayUi: true, + parameters: patchConfig ? _patchStepConfig(step, SubgraphServiceMigrateConfig, graph.subgraphService!.addressBook, graph.horizon!.addressBook) : SubgraphServiceMigrateConfig, + deploymentId: `subgraph-service-${hre.network.name}`, + }) + + // Update address book + console.log('\n========== šŸ“– Updating address book ==========') + IgnitionHelper.saveToAddressBook(deployment, hre.network.config.chainId, graph.subgraphService!.addressBook) + console.log(`Address book at ${graph.subgraphService!.addressBook.file} updated!`) + + console.log('\n\nšŸŽ‰ āœØ šŸš€ āœ… Migration complete! šŸŽ‰ āœØ šŸš€ āœ…') + }) + +// This function patches the Ignition configuration object using an address book to fill in the gaps +// The resulting configuration is not saved back to the configuration file + +function _patchStepConfig( + step: number, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + config: any, + addressBook: AddressBook, + horizonAddressBook: AddressBook, + // eslint-disable-next-line @typescript-eslint/no-explicit-any +): any { + let patchedConfig = config + + switch (step) { + case 2: + const SubgraphService = addressBook.getEntry('SubgraphService') + const DisputeManager = addressBook.getEntry('DisputeManager') + const GraphTallyCollector = horizonAddressBook.getEntry('GraphTallyCollector') + + patchedConfig = IgnitionHelper.patchConfig(config, { + SubgraphService: { + subgraphServiceProxyAddress: SubgraphService.address, + subgraphServiceProxyAdminAddress: SubgraphService.proxyAdmin, + graphTallyCollectorAddress: GraphTallyCollector.address, + disputeManagerProxyAddress: DisputeManager.address, + }, + DisputeManager: { + disputeManagerProxyAddress: DisputeManager.address, + disputeManagerProxyAdminAddress: DisputeManager.proxyAdmin, + }, + }) + break + } + + return patchedConfig +} + +function getHorizonBanner(): string { + return ` + ā–ˆā–ˆā•— ā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā•— + ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•”ā•ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•‘ā•šā•ā•ā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā•”ā•ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā•‘ + ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā•‘ ā–ˆā–ˆā–ˆā•”ā• ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•”ā–ˆā–ˆā•— ā–ˆā–ˆā•‘ + ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•‘ ā–ˆā–ˆā–ˆā•”ā• ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ā•šā–ˆā–ˆā•—ā–ˆā–ˆā•‘ + ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā•šā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā•šā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā•‘ ā•šā–ˆā–ˆā–ˆā–ˆā•‘ + ā•šā•ā• ā•šā•ā• ā•šā•ā•ā•ā•ā•ā• ā•šā•ā• ā•šā•ā•ā•šā•ā•ā•šā•ā•ā•ā•ā•ā•ā• ā•šā•ā•ā•ā•ā•ā• ā•šā•ā• ā•šā•ā•ā•ā• + + ā–ˆā–ˆā•— ā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— + ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•”ā•ā•ā•ā•ā• ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•”ā•ā•ā•ā•ā• + ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā•‘ ā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā–ˆā–ˆā–ˆā•— + ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•”ā•ā•ā•ā• ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•—ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•”ā•ā•ā• + ā•šā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā•‘ ā•šā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— + ā•šā•ā•ā•ā•ā•ā• ā•šā•ā• ā•šā•ā•ā•ā•ā•ā• ā•šā•ā• ā•šā•ā•ā•šā•ā• ā•šā•ā•ā•šā•ā•ā•ā•ā•ā• ā•šā•ā•ā•ā•ā•ā•ā• + ` +} diff --git a/packages/subgraph-service/tsconfig.json b/packages/subgraph-service/tsconfig.json index 0cbbef9e2..4b7040a6c 100644 --- a/packages/subgraph-service/tsconfig.json +++ b/packages/subgraph-service/tsconfig.json @@ -14,6 +14,7 @@ "hardhat.config.ts", "types/**/*.ts", "scripts/**/*.ts", + "tasks/**/*.ts", "test/**/*.ts", "ignition/**/*.ts", "eslint.config.js", diff --git a/packages/subgraph-service/types/hardhat-graph-protocol.d.ts b/packages/subgraph-service/types/hardhat-graph-protocol.d.ts index ab3c30b4b..8b5985269 100644 --- a/packages/subgraph-service/types/hardhat-graph-protocol.d.ts +++ b/packages/subgraph-service/types/hardhat-graph-protocol.d.ts @@ -2,7 +2,7 @@ // So we need to re-type it... this file should be a copy of hardhat-graph-protocol/src/type-extensions.ts import 'hardhat/types/config' import 'hardhat/types/runtime' -import type { GraphDeployments, GraphRuntimeEnvironment, GraphRuntimeEnvironmentOptions } from 'hardhat-graph-protocol/src/types' +import type { GraphDeployments, GraphRuntimeEnvironment, GraphRuntimeEnvironmentOptions } from 'hardhat-graph-protocol' declare module 'hardhat/types/runtime' { interface HardhatRuntimeEnvironment { diff --git a/packages/token-distribution/scripts/build b/packages/token-distribution/scripts/build index 7805eacb4..5f63fc2ee 100755 --- a/packages/token-distribution/scripts/build +++ b/packages/token-distribution/scripts/build @@ -2,5 +2,10 @@ set -eo pipefail +if [ -z "${STUDIO_API_KEY}" ]; then + echo "Warning: STUDIO_API_KEY is not set. Skipping build steps. Some functionality may be limited." + exit 0 +fi + yarn graphclient build yarn run compile \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 837fcb1a2..bb708f2c9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2954,7 +2954,7 @@ __metadata: hardhat-contract-sizer: "npm:^2.10.0" hardhat-gas-reporter: "npm:^1.0.8" hardhat-graph-protocol: "workspace:^0.0.1" - hardhat-secure-accounts: "npm:^1.0.4" + hardhat-secure-accounts: "npm:^1.0.5" hardhat-storage-layout: "npm:^0.1.7" lint-staged: "npm:^15.2.2" prettier: "npm:^3.2.5" @@ -3049,7 +3049,7 @@ __metadata: hardhat-contract-sizer: "npm:^2.10.0" hardhat-gas-reporter: "npm:^1.0.8" hardhat-graph-protocol: "workspace:^0.0.1" - hardhat-secure-accounts: "npm:^1.0.4" + hardhat-secure-accounts: "npm:^1.0.5" hardhat-storage-layout: "npm:^0.1.7" json5: "npm:^2.2.3" lint-staged: "npm:^15.2.2" @@ -14537,6 +14537,22 @@ __metadata: languageName: node linkType: hard +"hardhat-secure-accounts@npm:^1.0.5": + version: 1.0.5 + resolution: "hardhat-secure-accounts@npm:1.0.5" + dependencies: + debug: "npm:^4.3.4" + enquirer: "npm:^2.3.6" + lodash.clonedeep: "npm:^4.5.0" + prompt-sync: "npm:^4.2.0" + peerDependencies: + "@nomicfoundation/hardhat-ethers": ^3.0.0 + ethers: ^6.13.0 + hardhat: ^2.22.0 + checksum: b1a30f083b1a2919d6aee2f79253edcadff70017e8006df914bba4e6ad9963db622317ca333dfce38f16a9d60ee9425c0591b578cbf8c6db52d617161d4b46ee + languageName: node + linkType: hard + "hardhat-storage-layout@npm:0.1.6": version: 0.1.6 resolution: "hardhat-storage-layout@npm:0.1.6"