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
85 changes: 85 additions & 0 deletions packages/address-book/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# @graphprotocol/address-book

Contract addresses for The Graph Protocol. This package provides JSON files containing contract addresses for different networks.

## Features

- Contract addresses for Horizon and Subgraph Service
- Network-specific deployment addresses
- Zero dependencies

## Installation

```bash
npm install @graphprotocol/address-book
# or
pnpm install @graphprotocol/address-book
```

## Usage

### Import addresses directly

```javascript
// CommonJS
const horizonAddresses = require('@graphprotocol/address-book/horizon/addresses.json')
const subgraphServiceAddresses = require('@graphprotocol/address-book/subgraph-service/addresses.json')

// ES Modules
import horizonAddresses from '@graphprotocol/address-book/horizon/addresses.json'
import subgraphServiceAddresses from '@graphprotocol/address-book/subgraph-service/addresses.json'
```

### Address format

The addresses are organized by chain ID and contract name:

```json
{
"1337": {
"Controller": {
"address": "0x...",
"proxy": "transparent",
"proxyAdmin": "0x...",
"implementation": "0x..."
}
}
}
```

## Development

This package uses symlinks to stay in sync with the source address files. On first install, symlinks are automatically created.

## npm Publishing

This package uses a special workflow to ensure address files are included in the published package:

### How It Works

**Development**: The package uses symlinks to stay in sync with source address files:

- `src/horizon/addresses.json` → symlink to `../../../horizon/addresses.json`
- `src/subgraph-service/addresses.json` → symlink to `../../../subgraph-service/addresses.json`

**Publishing**: npm doesn't include symlinks in packages, so we automatically handle this:

```bash
npm publish
```

**Automatic execution**:

1. **`prepublishOnly`** - Copies actual files to replace symlinks
2. **npm pack & publish** - Includes real address files in published package
3. **`postpublish`** - Restores symlinks for development

### Troubleshooting

If publishing fails, the `postpublish` script may not run, leaving copied files instead of symlinks. To restore symlinks manually:

```bash
pnpm restore-symlinks
```

All symlink management is handled automatically during successful publishes.
36 changes: 36 additions & 0 deletions packages/address-book/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"name": "@graphprotocol/address-book",
"version": "0.1.0",
"publishConfig": {
"access": "public"
},
"description": "Contract addresses for The Graph Protocol",
"author": "The Graph core devs",
"license": "GPL-2.0-or-later",
"exports": {
"./horizon/addresses.json": "./src/horizon/addresses.json",
"./subgraph-service/addresses.json": "./src/subgraph-service/addresses.json"
},
"files": [
"src/**/*.json",
"README.md"
],
"scripts": {
"lint": "prettier -w --cache --log-level warn '**/*.json'",
"restore-symlinks": "node scripts/restore-symlinks.js",
"prepublishOnly": "node scripts/copy-addresses-for-publish.js",
"postpublish": "node scripts/restore-symlinks.js"
},
"devDependencies": {
"prettier": "^3.0.0"
},
"keywords": [
"ethereum",
"smart-contracts",
"graph",
"graph-protocol",
"horizon",
"subgraph-service",
"address-book"
]
}
1 change: 1 addition & 0 deletions packages/address-book/prettier.config.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('../../prettier.config.cjs')
68 changes: 68 additions & 0 deletions packages/address-book/scripts/copy-addresses-for-publish.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#!/usr/bin/env node

/**
* Copy Addresses for Publishing
*
* This script copies the actual addresses.json files from horizon and subgraph-service
* packages to replace the symlinks before npm publish.
*
* Why we need this:
* - Development uses symlinks (committed to git) for convenience
* - npm publish doesn't include symlinks in the published package
* - We need actual files in the published package for consumers
*
* The postpublish script will restore the symlinks after publishing.
*/

const fs = require('fs')
const path = require('path')

const FILES_TO_COPY = [
{
source: '../../../horizon/addresses.json',
target: 'src/horizon/addresses.json',
},
{
source: '../../../subgraph-service/addresses.json',
target: 'src/subgraph-service/addresses.json',
},
]

function copyFileForPublish(source, target) {
const targetPath = path.resolve(__dirname, '..', target)
const sourcePath = path.resolve(path.dirname(targetPath), source)

// Ensure source exists
if (!fs.existsSync(sourcePath)) {
console.error(`❌ Source file ${sourcePath} does not exist`)
process.exit(1)
}

// Remove existing symlink
if (fs.existsSync(targetPath)) {
fs.unlinkSync(targetPath)
}

// Copy actual file
try {
fs.copyFileSync(sourcePath, targetPath)
console.log(`✅ Copied for publish: ${target} <- ${source}`)
} catch (error) {
console.error(`❌ Failed to copy ${source} to ${target}:`, error.message)
process.exit(1)
}
}

function main() {
console.log('📦 Copying address files for npm publish...')

for (const { source, target } of FILES_TO_COPY) {
copyFileForPublish(source, target)
}

console.log('✅ Address files copied for publish!')
}

if (require.main === module) {
main()
}
55 changes: 55 additions & 0 deletions packages/address-book/scripts/restore-symlinks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#!/usr/bin/env node

/**
* Restore Symlinks After Publishing
*
* This script restores the symlinks after npm publish completes.
* The prepublishOnly script replaces symlinks with actual files for publishing,
* and this script puts the symlinks back for development.
*/

const fs = require('fs')
const path = require('path')

const SYMLINKS_TO_RESTORE = [
{
target: '../../../horizon/addresses.json',
link: 'src/horizon/addresses.json',
},
{
target: '../../../subgraph-service/addresses.json',
link: 'src/subgraph-service/addresses.json',
},
]

function restoreSymlink(target, link) {
const linkPath = path.resolve(__dirname, '..', link)

// Remove the copied file
if (fs.existsSync(linkPath)) {
fs.unlinkSync(linkPath)
}

// Restore symlink
try {
fs.symlinkSync(target, linkPath)
console.log(`✅ Restored symlink: ${link} -> ${target}`)
} catch (error) {
console.error(`❌ Failed to restore symlink ${link}:`, error.message)
process.exit(1)
}
}

function main() {
console.log('🔗 Restoring symlinks after publish...')

for (const { target, link } of SYMLINKS_TO_RESTORE) {
restoreSymlink(target, link)
}

console.log('✅ Symlinks restored!')
}

if (require.main === module) {
main()
}
1 change: 1 addition & 0 deletions packages/address-book/src/horizon/addresses.json
1 change: 1 addition & 0 deletions packages/address-book/src/subgraph-service/addresses.json
3 changes: 1 addition & 2 deletions packages/horizon/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@
"files": [
"build/contracts/**/*",
"typechain-types/**/*",
"README.md",
"addresses.json"
"README.md"
],
"scripts": {
"lint": "pnpm lint:ts; pnpm lint:sol; pnpm lint:natspec; pnpm lint:json",
Expand Down
3 changes: 1 addition & 2 deletions packages/subgraph-service/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@
"files": [
"build/contracts/**/*",
"typechain-types/**/*",
"README.md",
"addresses.json"
"README.md"
],
"scripts": {
"lint": "pnpm lint:ts; pnpm lint:sol; pnpm lint:natspec; pnpm lint:json",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ describe('Indexing Disputes', () => {
await graphToken.connect(fisherman).approve(disputeManager.target, disputeDeposit)

// Create dispute
const tx = await disputeManager.connect(fisherman).createIndexingDispute(allocationId, poi)
const currentBlockNumber = await ethers.provider.getBlockNumber()
const tx = await disputeManager.connect(fisherman).createIndexingDispute(allocationId, poi, currentBlockNumber)
const receipt = await tx.wait()

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

// Create dispute
const tx = await disputeManager.connect(fisherman).createIndexingDispute(allocationId, poi)
const currentBlockNumber = await ethers.provider.getBlockNumber()
const tx = await disputeManager.connect(fisherman).createIndexingDispute(allocationId, poi, currentBlockNumber)
const receipt = await tx.wait()

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

// Create dispute
const poi = generatePOI()
const tx = await disputeManager.connect(fisherman).createIndexingDispute(allocationId, poi)
const currentBlockNumber = await ethers.provider.getBlockNumber()
const tx = await disputeManager.connect(fisherman).createIndexingDispute(allocationId, poi, currentBlockNumber)
const receipt = await tx.wait()

// Get dispute ID from event
Expand Down
1 change: 1 addition & 0 deletions packages/toolshed/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
"horizon"
],
"dependencies": {
"@graphprotocol/address-book": "workspace:^",
"@graphprotocol/interfaces": "workspace:^",
"@nomicfoundation/hardhat-ethers": "3.0.8",
"debug": "^4.4.0",
Expand Down
3 changes: 1 addition & 2 deletions packages/toolshed/src/deployments/contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,9 @@ export type ContractList<T extends string = string> = Partial<Record<T, unknown>
* Loads a contract from an address book
*
* @param name Name of the contract
* @param addressBook Address book to use
* @param address Contract address
* @param signerOrProvider Signer or provider to use
* @param enableTxLogging Enable transaction logging to console and output file. Defaults to false.
* @param optional If true, the contract is optional and will not throw if it cannot be loaded
* @returns the loaded contract
*
* @throws Error if the contract could not be loaded
Expand Down
2 changes: 1 addition & 1 deletion packages/toolshed/src/deployments/horizon/actions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { HardhatEthersSigner } from '@nomicfoundation/hardhat-ethers/signers'

import type { GraphHorizonContracts } from '.'
import type { GraphHorizonContracts } from './contracts'

/**
* It's important to use JSDoc in the return functions here for good developer experience as
Expand Down
3 changes: 2 additions & 1 deletion packages/toolshed/src/deployments/horizon/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ export function loadGraphHorizon(addressBookPath: string, chainId: number, provi
}

export function connectGraphHorizon(chainId: number, signerOrProvider: Signer | Provider, addressBookPath?: string) {
addressBookPath = addressBookPath ?? resolveAddressBook(require, '@graphprotocol/horizon', 'addresses.json')
addressBookPath =
addressBookPath ?? resolveAddressBook(require, '@graphprotocol/address-book', 'horizon/addresses.json')
if (!addressBookPath) {
throw new Error('Address book path not found')
}
Expand Down
3 changes: 2 additions & 1 deletion packages/toolshed/src/deployments/subgraph-service/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ export function loadSubgraphService(addressBookPath: string, chainId: number, pr
}

export function connectSubgraphService(chainId: number, signerOrProvider: Signer | Provider, addressBookPath?: string) {
addressBookPath = addressBookPath ?? resolveAddressBook(require, '@graphprotocol/subgraph-service', 'addresses.json')
addressBookPath =
addressBookPath ?? resolveAddressBook(require, '@graphprotocol/address-book', 'subgraph-service/addresses.json')
if (!addressBookPath) {
throw new Error('Address book path not found')
}
Expand Down
Loading
Loading