Skip to content

Commit 7bceb49

Browse files
authored
Merge pull request #1111 from graphprotocol/tmigone/horizon-deployment-tooling-fixes
2 parents 0df54e3 + cbeb2e5 commit 7bceb49

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+883
-558
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ addresses-fork.json
5151

5252
# Forge artifacts
5353
cache_forge
54+
5455
# Graph client
5556
.graphclient
5657

@@ -62,4 +63,6 @@ tx-builder-*.json
6263
**/chain-1377/
6364
**/horizon-localhost/
6465
**/horizon-hardhat/
66+
**/subgraph-service-localhost/
67+
**/subgraph-service-hardhat/
6568
!**/ignition/**/artifacts/

packages/hardhat-graph-protocol/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616
"main": "./dist/src/index.js",
1717
"exports": {
1818
".": {
19-
"types": "./dist/src/index.d.ts",
20-
"default": "./dist/src/index.js"
19+
"types": "./src/types.ts",
20+
"default": "./src/index.ts"
2121
},
2222
"./sdk": {
2323
"types": "./src/sdk/index.ts",

packages/hardhat-graph-protocol/src/config.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,7 @@ export function getAddressBookPath(
2828
}
2929

3030
const normalizedAddressBookPath = normalizePath(addressBookPath, hre.config.paths.graph)
31-
if (!fs.existsSync(normalizedAddressBookPath)) {
32-
throw new GraphPluginError(`Address book not found: ${normalizedAddressBookPath}`)
33-
}
34-
35-
logDebug(`Address book path found: ${normalizedAddressBookPath}`)
31+
logDebug(`Address book path: ${normalizedAddressBookPath}`)
3632
return normalizedAddressBookPath
3733
}
3834

packages/hardhat-graph-protocol/src/gre.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import path from 'path'
22

33
import { getAddressBookPath } from './config'
44
import { HardhatEthersProvider } from '@nomicfoundation/hardhat-ethers/internal/hardhat-ethers-provider'
5+
import { lazyFunction } from 'hardhat/plugins'
56
import { logDebug } from './logger'
67

78
import { GraphHorizonAddressBook } from './sdk/deployments/horizon'
@@ -28,7 +29,7 @@ export const greExtendConfig = (config: HardhatConfig, userConfig: Readonly<Hard
2829
}
2930

3031
export const greExtendEnvironment = (hre: HardhatRuntimeEnvironment) => {
31-
hre.graph = (opts: GraphRuntimeEnvironmentOptions = { deployments: {} }) => {
32+
hre.graph = lazyFunction(() => (opts: GraphRuntimeEnvironmentOptions = { deployments: {} }) => {
3233
logDebug('*** Initializing Graph Runtime Environment (GRE) ***')
3334
logDebug(`Main network: ${hre.network.name}`)
3435
const chainId = hre.network.config.chainId
@@ -80,5 +81,5 @@ export const greExtendEnvironment = (hre: HardhatRuntimeEnvironment) => {
8081
assertGraphRuntimeEnvironment(gre)
8182
logDebug('GRE initialized successfully!')
8283
return gre
83-
}
84+
})
8485
}

packages/hardhat-graph-protocol/src/sdk/address-book.ts

Lines changed: 42 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@ export type AddressBookJson<
1313

1414
export type AddressBookEntry = {
1515
address: string
16-
proxy?: boolean
17-
implementation?: AddressBookEntry
16+
proxy?: 'graph' | 'transparent'
17+
proxyAdmin?: string
18+
implementation?: string
1819
}
1920

2021
/**
@@ -24,8 +25,9 @@ export type AddressBookEntry = {
2425
* "<CHAIN_ID>": {
2526
* "<CONTRACT_NAME>": {
2627
* "address": "<ADDRESS>",
27-
* "proxy": true, // optional
28-
* "implementation": { ... } // optional, nested contract structure
28+
* "proxy": "<graph|transparent>", // optional
29+
* "proxyAdmin": "<ADDRESS>", // optional
30+
* "implementation": "<ADDRESS>", // optional
2931
* ...
3032
* }
3133
* }
@@ -74,13 +76,18 @@ export abstract class AddressBook<
7476
this.chainId = _chainId
7577

7678
logDebug(`Loading address book from ${this.file}.`)
77-
if (!fs.existsSync(this.file)) throw new Error(`Address book path provided does not exist!`)
79+
80+
// Create empty address book if file doesn't exist
81+
if (!fs.existsSync(this.file)) {
82+
const emptyAddressBook = { [this.chainId]: {} }
83+
fs.writeFileSync(this.file, JSON.stringify(emptyAddressBook, null, 2))
84+
logDebug(`Created new address book at ${this.file}`)
85+
}
7886

7987
// Load address book and validate its shape
80-
// If it's empty, initialize it with an empty object
81-
const fileContents = JSON.parse(fs.readFileSync(this.file, 'utf8') || '{}')
88+
const fileContents = JSON.parse(fs.readFileSync(this.file, 'utf8'))
8289
if (!fileContents[this.chainId]) {
83-
fileContents[this.chainId] = {} as Record<ContractName, AddressBookEntry>
90+
fileContents[this.chainId] = {}
8491
}
8592
this.assertAddressBookJson(fileContents)
8693
this.addressBook = fileContents
@@ -96,29 +103,40 @@ export abstract class AddressBook<
96103
return this.validContracts
97104
}
98105

106+
entryExists(name: string): boolean {
107+
if (!this.isContractName(name)) {
108+
throw new Error(`Contract name ${name} is not a valid contract name`)
109+
}
110+
return this.addressBook[this.chainId][name] !== undefined
111+
}
112+
99113
/**
100114
* Get an entry from the address book
101115
*
102116
* @param name the name of the contract to get
117+
* @param strict if true it will throw an error if the contract is not found
103118
* @returns the address book entry for the contract
104119
* Returns an empty address book entry if the contract is not found
105120
*/
106-
getEntry(name: ContractName): { address: string } {
107-
const entry = this.addressBook[this.chainId][name]
108-
// Handle both object and string formats
109-
if (typeof entry === 'string') {
110-
return { address: entry }
121+
getEntry(name: string): AddressBookEntry {
122+
if (!this.isContractName(name)) {
123+
throw new Error(`Contract name ${name} is not a valid contract name`)
111124
}
125+
const entry = this.addressBook[this.chainId][name]
126+
this._assertAddressBookEntry(entry)
112127
return entry
113128
}
114129

115130
/**
116131
* Save an entry to the address book
117-
*
132+
* Allows partial address book entries to be saved
118133
* @param name the name of the contract to save
119134
* @param entry the address book entry for the contract
120135
*/
121-
setEntry(name: ContractName, entry: AddressBookEntry): void {
136+
setEntry(name: ContractName, entry: Partial<AddressBookEntry>): void {
137+
if (entry.address === undefined) {
138+
entry.address = '0x0000000000000000000000000000000000000000'
139+
}
122140
this._assertAddressBookEntry(entry)
123141
this.addressBook[this.chainId][name] = entry
124142
try {
@@ -162,7 +180,8 @@ export abstract class AddressBook<
162180
): ContractList<ContractName> {
163181
const contracts = {} as ContractList<ContractName>
164182
if (this.listEntries().length == 0) {
165-
throw Error('No valid contracts found in address book')
183+
logError('No valid contracts found in address book')
184+
return contracts
166185
}
167186
for (const contractName of this.listEntries()) {
168187
const artifactPath = typeof artifactsPath === 'object' && !Array.isArray(artifactsPath)
@@ -214,18 +233,17 @@ export abstract class AddressBook<
214233
// Asserts the provided object is a valid address book entry
215234
_assertAddressBookEntry(
216235
entry: unknown,
217-
): asserts entry is { address: string } {
218-
if (typeof entry === 'string') {
219-
// If it's a string, treat it as an address
220-
return
221-
}
222-
236+
): asserts entry is AddressBookEntry {
223237
assertObject(entry)
224238
if (!('address' in entry)) {
225239
throw new Error('Address book entry must have an address field')
226240
}
227-
if (typeof entry.address !== 'string') {
228-
throw new Error('Address book entry address must be a string')
241+
242+
const allowedFields = ['address', 'implementation', 'proxyAdmin', 'proxy']
243+
const entryFields = Object.keys(entry)
244+
const invalidFields = entryFields.filter(field => !allowedFields.includes(field))
245+
if (invalidFields.length > 0) {
246+
throw new Error(`Address book entry contains invalid fields: ${invalidFields.join(', ')}`)
229247
}
230248
}
231249
}

packages/hardhat-graph-protocol/src/sdk/deployments/horizon/contracts.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export const GraphHorizonContractNameList = [
2424
'RewardsManager',
2525
'L2GraphToken',
2626
'L2GraphTokenGateway',
27+
'L2Curation',
2728

2829
// @graphprotocol/horizon
2930
'HorizonStaking',
@@ -32,7 +33,7 @@ export const GraphHorizonContractNameList = [
3233
'GraphTallyCollector',
3334
] as const
3435

35-
const root = path.resolve(__dirname, '../../../../..') // hardhat-graph-protocol root
36+
const root = path.resolve(__dirname, '../../../..') // hardhat-graph-protocol root
3637
export const CONTRACTS_ARTIFACTS_PATH = path.resolve(root, 'node_modules', '@graphprotocol/contracts/build/contracts')
3738
export const HORIZON_ARTIFACTS_PATH = path.resolve(root, 'node_modules', '@graphprotocol/horizon/build/contracts')
3839

@@ -44,6 +45,7 @@ export const GraphHorizonArtifactsMap = {
4445
RewardsManager: CONTRACTS_ARTIFACTS_PATH,
4546
L2GraphToken: CONTRACTS_ARTIFACTS_PATH,
4647
L2GraphTokenGateway: CONTRACTS_ARTIFACTS_PATH,
48+
L2Curation: CONTRACTS_ARTIFACTS_PATH,
4749

4850
// @graphprotocol/horizon
4951
HorizonStaking: HORIZON_ARTIFACTS_PATH,

packages/hardhat-graph-protocol/src/sdk/deployments/subgraph-service/contracts.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export const SubgraphServiceContractNameList = [
2525
'DisputeManager',
2626
] as const
2727

28-
const root = path.resolve(__dirname, '../../../../..') // hardhat-graph-protocol root
28+
const root = path.resolve(__dirname, '../../../..') // hardhat-graph-protocol root
2929
export const CONTRACTS_ARTIFACTS_PATH = path.resolve(root, 'node_modules', '@graphprotocol/contracts/build/contracts')
3030
export const SUBGRAPH_SERVICE_ARTIFACTS_PATH = path.resolve(root, 'node_modules', '@graphprotocol/subgraph-service/build/contracts')
3131

packages/hardhat-graph-protocol/src/sdk/hardhat.base.config.ts

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,11 @@ interface SecureAccountsOptions {
99
enabled?: boolean
1010
}
1111

12-
// RPCs
12+
// Hardhat variables
1313
const ARBITRUM_ONE_RPC = vars.get('ARBITRUM_ONE_RPC', 'https://arb1.arbitrum.io/rpc')
1414
const ARBITRUM_SEPOLIA_RPC = vars.get('ARBITRUM_SEPOLIA_RPC', 'https://sepolia-rollup.arbitrum.io/rpc')
15-
16-
// Accounts
17-
const getTestnetAccounts = () => {
18-
const accounts: string[] = []
19-
if (vars.has('DEPLOYER_PRIVATE_KEY')) accounts.push(vars.get('DEPLOYER_PRIVATE_KEY'))
20-
if (vars.has('GOVERNOR_PRIVATE_KEY')) accounts.push(vars.get('GOVERNOR_PRIVATE_KEY'))
21-
return accounts
22-
}
23-
const getHardhatAccounts = () => {
24-
return {
25-
mnemonic: 'myth like bonus scare over problem client lizard pioneer submit female collect',
26-
}
27-
}
15+
const LOCALHOST_RPC = vars.get('LOCALHOST_RPC', 'http://localhost:8545')
16+
const LOCALHOST_CHAIN_ID = vars.get('LOCALHOST_CHAIN_ID', '31337')
2817

2918
export const solidityUserConfig: SolidityUserConfig = {
3019
version: '0.8.27',
@@ -57,17 +46,23 @@ type BaseNetworksUserConfig = NetworksUserConfig &
5746
export const networksUserConfig: BaseNetworksUserConfig = {
5847
hardhat: {
5948
chainId: 31337,
60-
accounts: getHardhatAccounts(),
49+
accounts: {
50+
mnemonic: 'myth like bonus scare over problem client lizard pioneer submit female collect',
51+
},
6152
deployments: {
62-
horizon: require.resolve('@graphprotocol/horizon/addresses-local.json'),
53+
horizon: resolveLocalAddressBook('@graphprotocol/horizon/addresses.json'),
54+
subgraphService: resolveLocalAddressBook('@graphprotocol/subgraph-service/addresses.json'),
6355
},
6456
},
6557
localhost: {
66-
chainId: 31337,
67-
url: 'http://localhost:8545',
68-
accounts: getTestnetAccounts(),
58+
chainId: parseInt(LOCALHOST_CHAIN_ID),
59+
url: LOCALHOST_RPC,
60+
secureAccounts: {
61+
enabled: true,
62+
},
6963
deployments: {
70-
horizon: require.resolve('@graphprotocol/horizon/addresses-local.json'),
64+
horizon: resolveLocalAddressBook('@graphprotocol/horizon/addresses.json'),
65+
subgraphService: resolveLocalAddressBook('@graphprotocol/subgraph-service/addresses.json'),
7166
},
7267
},
7368
arbitrumOne: {
@@ -86,6 +81,13 @@ export const networksUserConfig: BaseNetworksUserConfig = {
8681
},
8782
}
8883

84+
// Local address books are not commited to GitHub so they might not exist
85+
// require.resolve will throw an error if the file does not exist, so we hack it a bit
86+
function resolveLocalAddressBook(path: string) {
87+
const resolvedPath = require.resolve(path)
88+
return resolvedPath.replace('addresses.json', 'addresses-local.json')
89+
}
90+
8991
type BaseHardhatConfig = HardhatUserConfig &
9092
{ etherscan: Partial<EtherscanConfig> } &
9193
{ graph: GraphRuntimeEnvironmentOptions } &
@@ -100,6 +102,7 @@ export const hardhatBaseConfig: BaseHardhatConfig = {
100102
graph: {
101103
deployments: {
102104
horizon: require.resolve('@graphprotocol/horizon/addresses.json'),
105+
subgraphService: require.resolve('@graphprotocol/subgraph-service/addresses.json'),
103106
},
104107
},
105108
etherscan: etherscanUserConfig,

0 commit comments

Comments
 (0)