Skip to content

Commit ef8b05d

Browse files
authored
Merge pull request #1211 from graphprotocol/mde/address-book-package
chore: extract horizon and subgraph-service addresses to a new address-book package
2 parents d28477f + 4689fec commit ef8b05d

File tree

16 files changed

+277
-17
lines changed

16 files changed

+277
-17
lines changed

packages/address-book/README.md

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# @graphprotocol/address-book
2+
3+
Contract addresses for The Graph Protocol. This package provides JSON files containing contract addresses for different networks.
4+
5+
## Features
6+
7+
- Contract addresses for Horizon and Subgraph Service
8+
- Network-specific deployment addresses
9+
- Zero dependencies
10+
11+
## Installation
12+
13+
```bash
14+
npm install @graphprotocol/address-book
15+
# or
16+
pnpm install @graphprotocol/address-book
17+
```
18+
19+
## Usage
20+
21+
### Import addresses directly
22+
23+
```javascript
24+
// CommonJS
25+
const horizonAddresses = require('@graphprotocol/address-book/horizon/addresses.json')
26+
const subgraphServiceAddresses = require('@graphprotocol/address-book/subgraph-service/addresses.json')
27+
28+
// ES Modules
29+
import horizonAddresses from '@graphprotocol/address-book/horizon/addresses.json'
30+
import subgraphServiceAddresses from '@graphprotocol/address-book/subgraph-service/addresses.json'
31+
```
32+
33+
### Address format
34+
35+
The addresses are organized by chain ID and contract name:
36+
37+
```json
38+
{
39+
"1337": {
40+
"Controller": {
41+
"address": "0x...",
42+
"proxy": "transparent",
43+
"proxyAdmin": "0x...",
44+
"implementation": "0x..."
45+
}
46+
}
47+
}
48+
```
49+
50+
## Development
51+
52+
This package uses symlinks to stay in sync with the source address files. On first install, symlinks are automatically created.
53+
54+
## npm Publishing
55+
56+
This package uses a special workflow to ensure address files are included in the published package:
57+
58+
### How It Works
59+
60+
**Development**: The package uses symlinks to stay in sync with source address files:
61+
62+
- `src/horizon/addresses.json` → symlink to `../../../horizon/addresses.json`
63+
- `src/subgraph-service/addresses.json` → symlink to `../../../subgraph-service/addresses.json`
64+
65+
**Publishing**: npm doesn't include symlinks in packages, so we automatically handle this:
66+
67+
```bash
68+
npm publish
69+
```
70+
71+
**Automatic execution**:
72+
73+
1. **`prepublishOnly`** - Copies actual files to replace symlinks
74+
2. **npm pack & publish** - Includes real address files in published package
75+
3. **`postpublish`** - Restores symlinks for development
76+
77+
### Troubleshooting
78+
79+
If publishing fails, the `postpublish` script may not run, leaving copied files instead of symlinks. To restore symlinks manually:
80+
81+
```bash
82+
pnpm restore-symlinks
83+
```
84+
85+
All symlink management is handled automatically during successful publishes.

packages/address-book/package.json

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"name": "@graphprotocol/address-book",
3+
"version": "0.1.0",
4+
"publishConfig": {
5+
"access": "public"
6+
},
7+
"description": "Contract addresses for The Graph Protocol",
8+
"author": "The Graph core devs",
9+
"license": "GPL-2.0-or-later",
10+
"exports": {
11+
"./horizon/addresses.json": "./src/horizon/addresses.json",
12+
"./subgraph-service/addresses.json": "./src/subgraph-service/addresses.json"
13+
},
14+
"files": [
15+
"src/**/*.json",
16+
"README.md"
17+
],
18+
"scripts": {
19+
"lint": "prettier -w --cache --log-level warn '**/*.json'",
20+
"restore-symlinks": "node scripts/restore-symlinks.js",
21+
"prepublishOnly": "node scripts/copy-addresses-for-publish.js",
22+
"postpublish": "node scripts/restore-symlinks.js"
23+
},
24+
"devDependencies": {
25+
"prettier": "^3.0.0"
26+
},
27+
"keywords": [
28+
"ethereum",
29+
"smart-contracts",
30+
"graph",
31+
"graph-protocol",
32+
"horizon",
33+
"subgraph-service",
34+
"address-book"
35+
]
36+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = require('../../prettier.config.cjs')
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* Copy Addresses for Publishing
5+
*
6+
* This script copies the actual addresses.json files from horizon and subgraph-service
7+
* packages to replace the symlinks before npm publish.
8+
*
9+
* Why we need this:
10+
* - Development uses symlinks (committed to git) for convenience
11+
* - npm publish doesn't include symlinks in the published package
12+
* - We need actual files in the published package for consumers
13+
*
14+
* The postpublish script will restore the symlinks after publishing.
15+
*/
16+
17+
const fs = require('fs')
18+
const path = require('path')
19+
20+
const FILES_TO_COPY = [
21+
{
22+
source: '../../../horizon/addresses.json',
23+
target: 'src/horizon/addresses.json',
24+
},
25+
{
26+
source: '../../../subgraph-service/addresses.json',
27+
target: 'src/subgraph-service/addresses.json',
28+
},
29+
]
30+
31+
function copyFileForPublish(source, target) {
32+
const targetPath = path.resolve(__dirname, '..', target)
33+
const sourcePath = path.resolve(path.dirname(targetPath), source)
34+
35+
// Ensure source exists
36+
if (!fs.existsSync(sourcePath)) {
37+
console.error(`❌ Source file ${sourcePath} does not exist`)
38+
process.exit(1)
39+
}
40+
41+
// Remove existing symlink
42+
if (fs.existsSync(targetPath)) {
43+
fs.unlinkSync(targetPath)
44+
}
45+
46+
// Copy actual file
47+
try {
48+
fs.copyFileSync(sourcePath, targetPath)
49+
console.log(`✅ Copied for publish: ${target} <- ${source}`)
50+
} catch (error) {
51+
console.error(`❌ Failed to copy ${source} to ${target}:`, error.message)
52+
process.exit(1)
53+
}
54+
}
55+
56+
function main() {
57+
console.log('📦 Copying address files for npm publish...')
58+
59+
for (const { source, target } of FILES_TO_COPY) {
60+
copyFileForPublish(source, target)
61+
}
62+
63+
console.log('✅ Address files copied for publish!')
64+
}
65+
66+
if (require.main === module) {
67+
main()
68+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* Restore Symlinks After Publishing
5+
*
6+
* This script restores the symlinks after npm publish completes.
7+
* The prepublishOnly script replaces symlinks with actual files for publishing,
8+
* and this script puts the symlinks back for development.
9+
*/
10+
11+
const fs = require('fs')
12+
const path = require('path')
13+
14+
const SYMLINKS_TO_RESTORE = [
15+
{
16+
target: '../../../horizon/addresses.json',
17+
link: 'src/horizon/addresses.json',
18+
},
19+
{
20+
target: '../../../subgraph-service/addresses.json',
21+
link: 'src/subgraph-service/addresses.json',
22+
},
23+
]
24+
25+
function restoreSymlink(target, link) {
26+
const linkPath = path.resolve(__dirname, '..', link)
27+
28+
// Remove the copied file
29+
if (fs.existsSync(linkPath)) {
30+
fs.unlinkSync(linkPath)
31+
}
32+
33+
// Restore symlink
34+
try {
35+
fs.symlinkSync(target, linkPath)
36+
console.log(`✅ Restored symlink: ${link} -> ${target}`)
37+
} catch (error) {
38+
console.error(`❌ Failed to restore symlink ${link}:`, error.message)
39+
process.exit(1)
40+
}
41+
}
42+
43+
function main() {
44+
console.log('🔗 Restoring symlinks after publish...')
45+
46+
for (const { target, link } of SYMLINKS_TO_RESTORE) {
47+
restoreSymlink(target, link)
48+
}
49+
50+
console.log('✅ Symlinks restored!')
51+
}
52+
53+
if (require.main === module) {
54+
main()
55+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../../horizon/addresses.json
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../../subgraph-service/addresses.json

packages/horizon/package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@
1717
"files": [
1818
"build/contracts/**/*",
1919
"typechain-types/**/*",
20-
"README.md",
21-
"addresses.json"
20+
"README.md"
2221
],
2322
"scripts": {
2423
"lint": "pnpm lint:ts; pnpm lint:sol; pnpm lint:natspec; pnpm lint:json",

packages/subgraph-service/package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@
1515
"files": [
1616
"build/contracts/**/*",
1717
"typechain-types/**/*",
18-
"README.md",
19-
"addresses.json"
18+
"README.md"
2019
],
2120
"scripts": {
2221
"lint": "pnpm lint:ts; pnpm lint:sol; pnpm lint:natspec; pnpm lint:json",

packages/subgraph-service/test/integration/after-transition-period/dispute-manager/indexing-disputes.test.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ describe('Indexing Disputes', () => {
7272
await graphToken.connect(fisherman).approve(disputeManager.target, disputeDeposit)
7373

7474
// Create dispute
75-
const tx = await disputeManager.connect(fisherman).createIndexingDispute(allocationId, poi)
75+
const currentBlockNumber = await ethers.provider.getBlockNumber()
76+
const tx = await disputeManager.connect(fisherman).createIndexingDispute(allocationId, poi, currentBlockNumber)
7677
const receipt = await tx.wait()
7778

7879
// Get dispute ID from event
@@ -97,7 +98,8 @@ describe('Indexing Disputes', () => {
9798
await graphToken.connect(fisherman).approve(disputeManager.target, disputeDeposit)
9899

99100
// Create dispute
100-
const tx = await disputeManager.connect(fisherman).createIndexingDispute(allocationId, poi)
101+
const currentBlockNumber = await ethers.provider.getBlockNumber()
102+
const tx = await disputeManager.connect(fisherman).createIndexingDispute(allocationId, poi, currentBlockNumber)
101103
const receipt = await tx.wait()
102104

103105
// Get dispute ID from event
@@ -138,7 +140,8 @@ describe('Indexing Disputes', () => {
138140

139141
// Create dispute
140142
const poi = generatePOI()
141-
const tx = await disputeManager.connect(fisherman).createIndexingDispute(allocationId, poi)
143+
const currentBlockNumber = await ethers.provider.getBlockNumber()
144+
const tx = await disputeManager.connect(fisherman).createIndexingDispute(allocationId, poi, currentBlockNumber)
142145
const receipt = await tx.wait()
143146

144147
// Get dispute ID from event

0 commit comments

Comments
 (0)