Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: refactor deployment tooling, use task instead of script #1111

Merged
merged 15 commits into from
Feb 18, 2025
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ addresses-fork.json

# Forge artifacts
cache_forge

# Graph client
.graphclient

Expand All @@ -62,4 +63,6 @@ tx-builder-*.json
**/chain-1377/
**/horizon-localhost/
**/horizon-hardhat/
**/subgraph-service-localhost/
**/subgraph-service-hardhat/
!**/ignition/**/artifacts/
4 changes: 2 additions & 2 deletions packages/hardhat-graph-protocol/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
6 changes: 1 addition & 5 deletions packages/hardhat-graph-protocol/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
5 changes: 3 additions & 2 deletions packages/hardhat-graph-protocol/src/gre.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -28,7 +29,7 @@ export const greExtendConfig = (config: HardhatConfig, userConfig: Readonly<Hard
}

export const greExtendEnvironment = (hre: HardhatRuntimeEnvironment) => {
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
Expand Down Expand Up @@ -80,5 +81,5 @@ export const greExtendEnvironment = (hre: HardhatRuntimeEnvironment) => {
assertGraphRuntimeEnvironment(gre)
logDebug('GRE initialized successfully!')
return gre
}
})
}
66 changes: 42 additions & 24 deletions packages/hardhat-graph-protocol/src/sdk/address-book.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ export type AddressBookJson<

export type AddressBookEntry = {
address: string
proxy?: boolean
implementation?: AddressBookEntry
proxy?: 'graph' | 'transparent'
proxyAdmin?: string
implementation?: string
}

/**
Expand All @@ -24,8 +25,9 @@ export type AddressBookEntry = {
* "<CHAIN_ID>": {
* "<CONTRACT_NAME>": {
* "address": "<ADDRESS>",
* "proxy": true, // optional
* "implementation": { ... } // optional, nested contract structure
* "proxy": "<graph|transparent>", // optional
* "proxyAdmin": "<ADDRESS>", // optional
* "implementation": "<ADDRESS>", // optional
* ...
* }
* }
Expand Down Expand Up @@ -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<ContractName, AddressBookEntry>
fileContents[this.chainId] = {}
}
this.assertAddressBookJson(fileContents)
this.addressBook = fileContents
Expand All @@ -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<AddressBookEntry>): void {
if (entry.address === undefined) {
entry.address = '0x0000000000000000000000000000000000000000'
}
this._assertAddressBookEntry(entry)
this.addressBook[this.chainId][name] = entry
try {
Expand Down Expand Up @@ -162,7 +180,8 @@ export abstract class AddressBook<
): ContractList<ContractName> {
const contracts = {} as ContractList<ContractName>
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)
Expand Down Expand Up @@ -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(', ')}`)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export const GraphHorizonContractNameList = [
'RewardsManager',
'L2GraphToken',
'L2GraphTokenGateway',
'L2Curation',

// @graphprotocol/horizon
'HorizonStaking',
Expand All @@ -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')

Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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')

Expand Down
43 changes: 23 additions & 20 deletions packages/hardhat-graph-protocol/src/sdk/hardhat.base.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -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: {
Expand All @@ -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<EtherscanConfig> } &
{ graph: GraphRuntimeEnvironmentOptions } &
Expand All @@ -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,
Expand Down
Loading
Loading