Skip to content

Commit 1b9914d

Browse files
committed
feat: refactor deplyoment tooling for subgraph service
Signed-off-by: Tomás Migone <[email protected]>
1 parent 504d85b commit 1b9914d

23 files changed

+368
-305
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/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/sdk/address-book.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,16 @@ export abstract class AddressBook<
7676
this.chainId = _chainId
7777

7878
logDebug(`Loading address book from ${this.file}.`)
79-
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+
}
8086

8187
// Load address book and validate its shape
82-
// If it's empty, initialize it with an empty object
83-
const fileContents = JSON.parse(fs.readFileSync(this.file, 'utf8') || '{}')
88+
const fileContents = JSON.parse(fs.readFileSync(this.file, 'utf8'))
8489
if (!fileContents[this.chainId]) {
8590
fileContents[this.chainId] = {}
8691
}
@@ -98,6 +103,13 @@ export abstract class AddressBook<
98103
return this.validContracts
99104
}
100105

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+
101113
/**
102114
* Get an entry from the address book
103115
*

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: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ export const networksUserConfig: BaseNetworksUserConfig = {
4848
mnemonic: 'myth like bonus scare over problem client lizard pioneer submit female collect',
4949
},
5050
deployments: {
51-
horizon: require.resolve('@graphprotocol/horizon/addresses-local.json'),
51+
horizon: resolveLocalAddressBook('@graphprotocol/horizon/addresses.json'),
52+
subgraphService: resolveLocalAddressBook('@graphprotocol/subgraph-service/addresses.json'),
5253
},
5354
},
5455
localhost: {
@@ -58,7 +59,8 @@ export const networksUserConfig: BaseNetworksUserConfig = {
5859
enabled: true,
5960
},
6061
deployments: {
61-
horizon: require.resolve('@graphprotocol/horizon/addresses-local.json'),
62+
horizon: resolveLocalAddressBook('@graphprotocol/horizon/addresses.json'),
63+
subgraphService: resolveLocalAddressBook('@graphprotocol/subgraph-service/addresses.json'),
6264
},
6365
},
6466
arbitrumOne: {
@@ -77,6 +79,13 @@ export const networksUserConfig: BaseNetworksUserConfig = {
7779
},
7880
}
7981

82+
// Local address books are not commited to GitHub so they might not exist
83+
// require.resolve will throw an error if the file does not exist, so we hack it a bit
84+
function resolveLocalAddressBook(path: string) {
85+
const resolvedPath = require.resolve(path)
86+
return resolvedPath.replace('addresses.json', 'addresses-local.json')
87+
}
88+
8089
type BaseHardhatConfig = HardhatUserConfig &
8190
{ etherscan: Partial<EtherscanConfig> } &
8291
{ graph: GraphRuntimeEnvironmentOptions } &
@@ -91,6 +100,7 @@ export const hardhatBaseConfig: BaseHardhatConfig = {
91100
graph: {
92101
deployments: {
93102
horizon: require.resolve('@graphprotocol/horizon/addresses.json'),
103+
subgraphService: require.resolve('@graphprotocol/subgraph-service/addresses.json'),
94104
},
95105
},
96106
etherscan: etherscanUserConfig,

packages/hardhat-graph-protocol/src/sdk/ignition/ignition.ts

Lines changed: 16 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ require('json5/lib/register')
55

66
import fs from 'fs'
77
import path from 'path'
8-
import { AddressBook } from '../address-book'
8+
9+
import type { AddressBook } from '../address-book'
910

1011
export function loadConfig(configPath: string, prefix: string, networkName: string): any {
1112
const configFileCandidates = [
@@ -24,36 +25,22 @@ export function loadConfig(configPath: string, prefix: string, networkName: stri
2425
}
2526

2627
export function patchConfig(jsonData: any, patches: Record<string, any>) {
27-
function recursivePatch(obj: any) {
28-
if (typeof obj === 'object' && obj !== null) {
29-
for (const key in obj) {
30-
if (key in patches) {
31-
obj[key] = patches[key]
28+
function recursivePatch(obj: any, patchObj: any) {
29+
if (typeof obj === 'object' && obj !== null && typeof patchObj === 'object' && patchObj !== null) {
30+
for (const key in patchObj) {
31+
if (obj.hasOwnProperty(key) && typeof obj[key] === 'object' && typeof patchObj[key] === 'object') {
32+
// Both are objects, recursively merge
33+
recursivePatch(obj[key], patchObj[key])
3234
} else {
33-
recursivePatch(obj[key])
35+
// Either not an object or new key, directly assign
36+
obj[key] = patchObj[key]
3437
}
3538
}
3639
}
40+
return obj
3741
}
3842

39-
recursivePatch(jsonData)
40-
return jsonData
41-
}
42-
43-
export function mergeConfigs(obj1: any, obj2: any) {
44-
const merged = { ...obj1 }
45-
46-
for (const key in obj2) {
47-
if (obj2.hasOwnProperty(key)) {
48-
if (typeof obj2[key] === 'object' && obj2[key] !== null && obj1[key]) {
49-
merged[key] = mergeConfigs(obj1[key], obj2[key])
50-
} else {
51-
merged[key] = obj2[key]
52-
}
53-
}
54-
}
55-
56-
return merged
43+
return recursivePatch(jsonData, patches)
5744
}
5845

5946
export function saveToAddressBook<ChainId extends number, ContractName extends string>(
@@ -71,7 +58,7 @@ export function saveToAddressBook<ChainId extends number, ContractName extends s
7158
if (ignitionContractName.includes('_Proxy_')) {
7259
const contractName = ignitionContractName.replace(/(Transparent_Proxy_|Graph_Proxy_)/, '') as ContractName
7360
const proxy = ignitionContractName.includes('Transparent_Proxy_') ? 'transparent' : 'graph'
74-
const entry = addressBook.getEntry(contractName)
61+
const entry = addressBook.entryExists(contractName) ? addressBook.getEntry(contractName) : {}
7562
addressBook.setEntry(contractName, {
7663
...entry,
7764
address: (contract as any).target,
@@ -83,7 +70,7 @@ export function saveToAddressBook<ChainId extends number, ContractName extends s
8370
if (ignitionContractName.includes('_ProxyAdmin_')) {
8471
const contractName = ignitionContractName.replace(/(Transparent_ProxyAdmin_|Graph_ProxyAdmin_)/, '') as ContractName
8572
const proxy = ignitionContractName.includes('Transparent_ProxyAdmin_') ? 'transparent' : 'graph'
86-
const entry = addressBook.getEntry(contractName)
73+
const entry = addressBook.entryExists(contractName) ? addressBook.getEntry(contractName) : {}
8774
addressBook.setEntry(contractName, {
8875
...entry,
8976
proxy,
@@ -94,7 +81,7 @@ export function saveToAddressBook<ChainId extends number, ContractName extends s
9481
// Implementation contracts
9582
if (ignitionContractName.startsWith('Implementation_')) {
9683
const contractName = ignitionContractName.replace('Implementation_', '') as ContractName
97-
const entry = addressBook.getEntry(contractName)
84+
const entry = addressBook.entryExists(contractName) ? addressBook.getEntry(contractName) : {}
9885
addressBook.setEntry(contractName, {
9986
...entry,
10087
implementation: (contract as any).target,
@@ -103,7 +90,7 @@ export function saveToAddressBook<ChainId extends number, ContractName extends s
10390

10491
// Non proxied contracts
10592
if (addressBook.isContractName(ignitionContractName)) {
106-
const entry = addressBook.getEntry(ignitionContractName)
93+
const entry = addressBook.entryExists(ignitionContractName) ? addressBook.getEntry(ignitionContractName) : {}
10794
addressBook.setEntry(ignitionContractName, {
10895
...entry,
10996
address: (contract as any).target,

packages/horizon/README.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,17 @@ Note that this instructions will help you deploy Graph Horizon contracts, but no
3535
To deploy Graph Horizon from scratch run the following command:
3636

3737
```bash
38-
npx hardhat run scripts/deploy.ts --network hardhat
38+
npx hardhat deploy:protocol --network hardhat
3939
```
4040

4141
### Upgrade deployment
42-
To upgrade an existing deployment of the original Graph Protocol to Graph Horizon, run the following command:
42+
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 each step might need to be run by different accounts (deployer vs governor):
4343

4444
```bash
45-
npx hardhat run scripts/migrate.ts --network localhost
45+
npx hardhat deploy:migrate --network hardhat --step 1
46+
npx hardhat deploy:migrate --network hardhat --step 2 # Optionally add --patch-config
47+
npx hardhat deploy:migrate --network hardhat --step 3
48+
npx hardhat deploy:migrate --network hardhat --step 4 # Optionally add --patch-config
4649
```
4750

48-
Usually you would run this against a network (or a fork) where the original Graph Protocol was previously deployed.
51+
Steps 2 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.

packages/horizon/ignition/configs/horizon-migrate.default.json5

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@
1414
"curationAddress": "0xDe761f075200E75485F4358978FB4d1dC8644FD5",
1515

1616
// Must be set for step 4 of the migration
17-
"subgraphServiceAddress": "0x0000000000000000000000000000000000000000",
17+
"subgraphServiceAddress": "",
1818

1919
// Parameters
2020
"maxThawingPeriod": 2419200
2121
},
22-
"GraphPayments": {
22+
"GraphPayments": {
2323
"protocolPaymentCut": 10000
2424
},
2525
"PaymentsEscrow": {
@@ -31,20 +31,20 @@
3131
"revokeSignerThawingPeriod": 10000
3232
},
3333
"HorizonProxiesGovernor": {
34-
// These addresses must be set for step 2 of the migration
35-
"graphPaymentsAddress": "0x0000000000000000000000000000000000000000",
36-
"paymentsEscrowAddress": "0x0000000000000000000000000000000000000000"
34+
// Must be set for step 2 of the migration
35+
"graphPaymentsAddress": "",
36+
"paymentsEscrowAddress": ""
3737
},
3838
"HorizonStakingGovernor": {
3939
// Must be set for step 4 of the migration
40-
"horizonStakingImplementationAddress": "0x0000000000000000000000000000000000000000"
40+
"horizonStakingImplementationAddress": ""
4141
},
4242
"L2CurationGovernor": {
4343
// Must be set for step 4 of the migration
44-
"curationImplementationAddress": "0x0000000000000000000000000000000000000000"
44+
"curationImplementationAddress": ""
4545
},
4646
"RewardsManagerGovernor": {
4747
// Must be set for step 4 of the migration
48-
"rewardsManagerImplementationAddress": "0x0000000000000000000000000000000000000000"
48+
"rewardsManagerImplementationAddress": ""
4949
}
5050
}

packages/horizon/ignition/configs/horizon-migrate.horizon-virtualArbitrumOne.json5

Lines changed: 0 additions & 50 deletions
This file was deleted.

packages/horizon/tasks/deploy.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,18 @@ task('deploy:protocol', 'Deploy a new version of the Graph Protocol Horizon cont
1616
const { config: HorizonConfig, file } = IgnitionHelper.loadConfig('./ignition/configs/', 'horizon', hre.network.name)
1717
console.log(`Loaded migration configuration from ${file}`)
1818

19+
// Display the deployer -- this also triggers the secure accounts prompt if being used
20+
console.log('\n========== 🔑 Deployer account ==========')
21+
const signers = await hre.ethers.getSigners()
22+
const deployer = signers[0]
23+
console.log('Using deployer account:', deployer.address)
24+
const balance = await hre.ethers.provider.getBalance(deployer.address)
25+
console.log('Deployer balance:', hre.ethers.formatEther(balance), 'ETH')
26+
if (balance === 0n) {
27+
console.error('Error: Deployer account has no ETH balance')
28+
process.exit(1)
29+
}
30+
1931
// Deploy the contracts
2032
console.log(`\n========== 🚧 Deploy protocol ==========`)
2133
const deployment = await hre.ignition.deploy(DeployModule, {
@@ -27,10 +39,12 @@ task('deploy:protocol', 'Deploy a new version of the Graph Protocol Horizon cont
2739
console.log('\n========== 📖 Updating address book ==========')
2840
IgnitionHelper.saveToAddressBook(deployment, hre.network.config.chainId, graph.horizon!.addressBook)
2941
console.log(`Address book at ${graph.horizon!.addressBook.file} updated!`)
42+
43+
console.log('\n\n🎉 ✨ 🚀 ✅ Deployment complete! 🎉 ✨ 🚀 ✅')
3044
})
3145

3246
task('deploy:migrate', 'Upgrade an existing version of the Graph Protocol v1 to Horizon - no data services deployed')
33-
.addOptionalParam('step', 'Migration step to run (1, 2, 3 or 4) - runs all if not provided', undefined, types.int)
47+
.addOptionalParam('step', 'Migration step to run (1, 2, 3 or 4)', undefined, types.int)
3448
.addFlag('patchConfig', 'Patch configuration file using address book values - does not save changes')
3549
.setAction(async (args, hre: HardhatRuntimeEnvironment) => {
3650
// Task parameters
@@ -42,7 +56,7 @@ task('deploy:migrate', 'Upgrade an existing version of the Graph Protocol v1 to
4256

4357
// Migration step to run
4458
console.log('\n========== 🏗️ Migration steps ==========')
45-
const validSteps = [0, 1, 2, 3, 4]
59+
const validSteps = [1, 2, 3, 4]
4660
if (!validSteps.includes(step)) {
4761
console.error(`Error: Invalid migration step provided: ${step}`)
4862
console.error(`Valid steps are: ${validSteps.join(', ')}`)
@@ -83,7 +97,7 @@ task('deploy:migrate', 'Upgrade an existing version of the Graph Protocol v1 to
8397
IgnitionHelper.saveToAddressBook(deployment, hre.network.config.chainId, graph.horizon!.addressBook)
8498
console.log(`Address book at ${graph.horizon!.addressBook.file} updated!`)
8599

86-
console.log('\n\n🎉 ✨ 🚀 ✅ Migration successful! 🎉 ✨ 🚀 ✅')
100+
console.log('\n\n🎉 ✨ 🚀 ✅ Migration complete! 🎉 ✨ 🚀 ✅')
87101
})
88102

89103
// This function patches the Ignition configuration object using an address book to fill in the gaps

0 commit comments

Comments
 (0)