Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 21 additions & 16 deletions packages/hardhat-graph-protocol/src/sdk/address-book.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,15 +104,13 @@ export abstract class AddressBook<
* @returns the address book entry for the contract
* Returns an empty address book entry if the contract is not found
*/
getEntry(name: ContractName): AddressBookEntry {
try {
const entry = this.addressBook[this.chainId][name]
this._assertAddressBookEntry(entry)
return entry
} catch (_) {
// TODO: should we throw instead?
return { address: '0x0000000000000000000000000000000000000000' }
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 }
}
return entry
}

/**
Expand Down Expand Up @@ -215,13 +213,20 @@ export abstract class AddressBook<
}

// Asserts the provided object is a valid address book entry
_assertAddressBookEntry(json: unknown): asserts json is AddressBookEntry {
assertObject(json)

if (typeof json.address !== 'string') throw new AssertionError({ message: 'Invalid address' })
if (json.proxy && typeof json.proxy !== 'boolean')
throw new AssertionError({ message: 'Invalid proxy' })
if (json.implementation && typeof json.implementation !== 'object')
throw new AssertionError({ message: 'Invalid implementation' })
_assertAddressBookEntry(
entry: unknown,
): asserts entry is { address: string } {
if (typeof entry === 'string') {
// If it's a string, treat it as an address
return
}

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')
}
}
}
37 changes: 33 additions & 4 deletions packages/hardhat-graph-protocol/src/sdk/hardhat.base.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,18 @@ import type { EtherscanConfig } from '@nomicfoundation/hardhat-verify/types'

// This next import ensures secure accounts config is correctly typed
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import type { SecureAccountsOptions } from 'hardhat-secure-accounts/src/type-extensions'
import 'hardhat-secure-accounts'

// Environment variables
const ARBISCAN_API_KEY = vars.get('ARBISCAN_API_KEY', undefined)

// RPCs
const VIRTUAL_ARBITRUM_SEPOLIA_RPC = vars.has('VIRTUAL_ARBITRUM_SEPOLIA_RPC') ? vars.get('VIRTUAL_ARBITRUM_SEPOLIA_RPC') : undefined
const ARBITRUM_SEPOLIA_RPC = vars.get('ARBITRUM_SEPOLIA_RPC', 'https://sepolia-rollup.arbitrum.io/rpc')
const VIRTUAL_ARBITRUM_ONE_RPC = vars.has('VIRTUAL_ARBITRUM_ONE_RPC') ? vars.get('VIRTUAL_ARBITRUM_ONE_RPC') : undefined

// Tenderly API Key
const TENDERLY_API_KEY = vars.has('TENDERLY_API_KEY') ? vars.get('TENDERLY_API_KEY') : undefined

// Accounts
const DEPLOYER_PRIVATE_KEY = vars.get('DEPLOYER_PRIVATE_KEY', undefined)
Expand Down Expand Up @@ -47,13 +51,33 @@ export const projectPathsUserConfig: ProjectPathsUserConfig = {
export const etherscanUserConfig: Partial<EtherscanConfig> = {
apiKey: {
arbitrumSepolia: ARBISCAN_API_KEY,
...(TENDERLY_API_KEY && {
virtualArbitrumSepolia: TENDERLY_API_KEY,
virtualArbitrumOne: TENDERLY_API_KEY,
}),
},
customChains: [
{
network: 'arbitrumSepolia',
chainId: 421614,
urls: { apiURL: 'https://api-sepolia.arbiscan.io/api', browserURL: 'https://sepolia.arbiscan.io/' },
},
{
network: 'virtualArbitrumSepolia',
chainId: 421615,
urls: {
apiURL: VIRTUAL_ARBITRUM_SEPOLIA_RPC + '/verify/etherscan',
browserURL: VIRTUAL_ARBITRUM_SEPOLIA_RPC || 'https://sepolia.arbiscan.io/',
},
},
{
network: 'virtualArbitrumOne',
chainId: 42162,
urls: {
apiURL: VIRTUAL_ARBITRUM_ONE_RPC + '/verify/etherscan',
browserURL: VIRTUAL_ARBITRUM_SEPOLIA_RPC || 'https://arbiscan.io/',
},
},
],
}

Expand Down Expand Up @@ -85,6 +109,13 @@ export const networksUserConfig: NetworksUserConfig = {
accounts: getTestnetAccounts(),
},
}),
...(VIRTUAL_ARBITRUM_ONE_RPC && {
virtualArbitrumOne: {
chainId: 42162,
url: VIRTUAL_ARBITRUM_ONE_RPC,
accounts: getTestnetAccounts(),
},
}),
}

export const hardhatBaseConfig: HardhatUserConfig & { etherscan: Partial<EtherscanConfig> } = {
Expand All @@ -96,9 +127,7 @@ export const hardhatBaseConfig: HardhatUserConfig & { etherscan: Partial<Ethersc
networks: networksUserConfig,
graph: {
deployments: {
horizon: {
addressBook: 'addresses.json',
},
horizon: require.resolve('@graphprotocol/horizon/addresses.json'),
},
},
etherscan: etherscanUserConfig,
Expand Down
35 changes: 35 additions & 0 deletions packages/hardhat-graph-protocol/src/sdk/ignition/ignition.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/* eslint-disable no-prototype-builtins */
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-explicit-any */
require('json5/lib/register')

Expand All @@ -20,6 +22,39 @@ export function loadConfig(configPath: string, prefix: string, networkName: stri
return removeNFromBigInts(require(configFile))
}

export function patchConfig(jsonData: any, patches: Record<string, any>) {
function recursivePatch(obj: any) {
if (typeof obj === 'object' && obj !== null) {
for (const key in obj) {
if (key in patches) {
obj[key] = patches[key]
} else {
recursivePatch(obj[key])
}
}
}
}

recursivePatch(jsonData)
return jsonData
}

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(
contracts: any,
chainId: number | undefined,
Expand Down
4 changes: 2 additions & 2 deletions packages/hardhat-graph-protocol/src/sdk/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { loadConfig, saveAddressBook } from './ignition/ignition'
import { loadConfig, mergeConfigs, patchConfig, saveAddressBook } from './ignition/ignition'
import { hardhatBaseConfig } from './hardhat.base.config'

const IgnitionHelper = { saveAddressBook, loadConfig }
const IgnitionHelper = { saveAddressBook, loadConfig, patchConfig, mergeConfigs }
export { hardhatBaseConfig, IgnitionHelper }
1 change: 1 addition & 0 deletions packages/horizon/addresses.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
13 changes: 11 additions & 2 deletions packages/horizon/hardhat.config.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
import { HardhatUserConfig } from 'hardhat/config'
import { hardhatBaseConfig } from 'hardhat-graph-protocol/sdk'

// Hardhat plugins
import '@nomicfoundation/hardhat-foundry'
import '@nomicfoundation/hardhat-toolbox'
import '@nomicfoundation/hardhat-ignition-ethers'
import "@tenderly/hardhat-tenderly"
import 'hardhat-storage-layout'
import 'hardhat-contract-sizer'
import 'hardhat-secure-accounts'

if (process.env.BUILD_RUN !== 'true') {
require('hardhat-graph-protocol')
}

export default hardhatBaseConfig
const config: HardhatUserConfig = {
...hardhatBaseConfig,
tenderly: {
project: "graph-network",
username: "graphprotocol",
}
}

export default config
38 changes: 27 additions & 11 deletions packages/horizon/ignition/configs/horizon-migrate.default.json5
Original file line number Diff line number Diff line change
@@ -1,26 +1,25 @@
{
"$global": {
// Accounts
"governor": "0x72ee30d43Fb5A90B3FE983156C5d2fBE6F6d07B3",

// Addresses for contracts deployed in the original Graph Protocol
"graphProxyAdminAddress": "0x7474a6cc5fAeDEc620Db0fa8E4da6eD58477042C",
"controllerAddress": "0x9DB3ee191681f092607035d9BDA6e59FbEaCa695",
"horizonStakingAddress": "0x865365C425f3A593Ffe698D9c4E6707D14d51e08",
"epochManagerAddress": "0x88b3C7f37253bAA1A9b95feAd69bD5320585826D",
"graphTokenAddress": "0xf8c05dCF59E8B28BFD5eed176C562bEbcfc7Ac04",
"graphTokenGatewayAddress": "0xB24Ce0f8c18c4DdDa584A7EeC132F49C966813bb",
"rewardsManagerAddress": "0x1F49caE7669086c8ba53CC35d1E9f80176d67E79",
"curationAddress": "0xDe761f075200E75485F4358978FB4d1dC8644FD5",

// Placeholder address for a standalone Horizon deployment, see README.md for more details
"subgraphServiceAddress": "0x0000000000000000000000000000000000000000"
},
"RewardsManager": {
"rewardsManagerAddress": "0x1F49caE7669086c8ba53CC35d1E9f80176d67E79"
},
"L2Curation": {
"curationAddress": "0xDe761f075200E75485F4358978FB4d1dC8644FD5"
},
"HorizonStaking": {
// Must be set for step 4 of the migration
"subgraphServiceAddress": "0x0000000000000000000000000000000000000000",

// Parameters
"maxThawingPeriod": 2419200
},
"GraphPayments": {
"GraphPayments": {
"protocolPaymentCut": 10000
},
"PaymentsEscrow": {
Expand All @@ -30,5 +29,22 @@
"eip712Name": "TAPCollector",
"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"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{
"$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
},
"TAPCollector": {
"eip712Name": "TAPCollector",
"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"
}
}
1 change: 1 addition & 0 deletions packages/horizon/ignition/configs/horizon.default.json5
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"$global": {
"pauseGuardian": "0x95cED938F7991cd0dFcb48F0a06a40FA1aF46EBC",

// Placeholder address for a standalone Horizon deployment, see README.md for more details
"subgraphServiceAddress": "0x0000000000000000000000000000000000000000"
},
Expand Down
18 changes: 11 additions & 7 deletions packages/horizon/ignition/modules/core/GraphPayments.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { buildModule } from '@nomicfoundation/hardhat-ignition/modules'
import { deployImplementation } from '../proxy/implementation'
import { upgradeTransparentUpgradeableProxyNoLoad } from '../proxy/TransparentUpgradeableProxy'
import { upgradeTransparentUpgradeableProxy } from '../proxy/TransparentUpgradeableProxy'

import GraphPeripheryModule, { MigratePeripheryModule } from '../periphery/periphery'
import HorizonProxiesModule, { MigrateHorizonProxiesModule } from './HorizonProxies'
import HorizonProxiesModule, { MigrateHorizonProxiesDeployerModule } from './HorizonProxies'

import GraphPaymentsArtifact from '../../../build/contracts/contracts/payments/GraphPayments.sol/GraphPayments.json'

Expand All @@ -22,7 +22,7 @@ export default buildModule('GraphPayments', (m) => {
}, { after: [GraphPeripheryModule, HorizonProxiesModule] })

// Upgrade proxy to implementation contract
const GraphPayments = upgradeTransparentUpgradeableProxyNoLoad(m,
const GraphPayments = upgradeTransparentUpgradeableProxy(m,
GraphPaymentsProxyAdmin,
GraphPaymentsProxy,
GraphPaymentsImplementation, {
Expand All @@ -36,22 +36,26 @@ export default buildModule('GraphPayments', (m) => {
return { GraphPayments, GraphPaymentsProxyAdmin }
})

// Note that this module requires MigrateHorizonProxiesGovernorModule to be executed first
// The dependency is not made explicit to support the production workflow where the governor is a
// multisig owned by the Graph Council.
// For testnet, the dependency can be made explicit by having a parent module establish it.
export const MigrateGraphPaymentsModule = buildModule('GraphPayments', (m) => {
const { GraphPaymentsProxyAdmin, GraphPaymentsProxy } = m.useModule(MigrateHorizonProxiesModule)
const { GraphPaymentsProxyAdmin, GraphPaymentsProxy } = m.useModule(MigrateHorizonProxiesDeployerModule)
const { Controller } = m.useModule(MigratePeripheryModule)

const governor = m.getAccount(1)
const governor = m.getParameter('governor')
const protocolPaymentCut = m.getParameter('protocolPaymentCut')

// Deploy GraphPayments implementation
const GraphPaymentsImplementation = deployImplementation(m, {
name: 'GraphPayments',
artifact: GraphPaymentsArtifact,
constructorArgs: [Controller, protocolPaymentCut],
}, { after: [MigrateHorizonProxiesModule] })
})

// Upgrade proxy to implementation contract
const GraphPayments = upgradeTransparentUpgradeableProxyNoLoad(m,
const GraphPayments = upgradeTransparentUpgradeableProxy(m,
GraphPaymentsProxyAdmin,
GraphPaymentsProxy,
GraphPaymentsImplementation, {
Expand Down
Loading
Loading