Skip to content
Draft
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
15 changes: 15 additions & 0 deletions .claude/skills/deploy-test-subgraphs/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
name: deploy-test-subgraphs
description: Publish test subgraphs to GNS on the local network. Use when the user asks to "deploy subgraphs", "add subgraphs", "deploy 50 subgraphs", "create test subgraphs", or wants to populate the network with subgraphs for testing. Also trigger when the user says a number followed by "subgraphs" (e.g. "deploy 500 subgraphs").
argument-hint: "[count] [prefix]"
---

Run `python3 scripts/deploy-test-subgraph.py <count> [prefix]` from the local-network repo root.

- `count` defaults to 1 if the user doesn't specify a number
- `prefix` defaults to `test-subgraph` -- each subgraph is named `<prefix>-1`, `<prefix>-2`, etc.
- Subgraphs are published to GNS on-chain only -- they are NOT deployed to graph-node and will not be indexed

The script builds once (~10s), then each publish is sub-second. 100 subgraphs takes ~30s total.

After publishing, run `python3 scripts/network-status.py` and output the result in a code block so the user can see the updated network state.
162 changes: 162 additions & 0 deletions .claude/skills/fresh-deploy/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
---
name: fresh-deploy
description: Full stack reset and fresh deploy of the local-network Docker Compose environment. Use when the user asks to tear down and redeploy, do a fresh deploy, reset the stack, or bring everything up from scratch. Also use after merging PRs that change container code, or when debugging stuck state.
---

# Fresh Deploy

Reset the local-network Docker Compose environment to a clean state and bring all services up ready for DIPs testing.

## Prerequisites

The contracts repo at `$CONTRACTS_SOURCE_ROOT` (typically `/Users/samuel/Documents/github/contracts`) must be on `indexing-payments-management-audit` (PR #1301) with three local commits applied on top:

1. Cherry-pick `02b6996e` from `escrow-management` -- adds RecurringCollector Ignition module, wires it into SubgraphService deployment, and links external libraries
2. Cherry-pick `d2a0d30e` from `escrow-management` -- adds `RecurringCollector` to `GraphHorizonContractNameList` in toolshed so it gets written to horizon.json
3. Local fix for BUG-007 -- adds `{ after: [GraphPeripheryModule, HorizonProxiesModule] }` to the `deployImplementation` call in `packages/horizon/ignition/modules/core/HorizonStaking.ts`

After applying these, the toolshed package must be compiled: `cd packages/toolshed && pnpm build:self`.

To verify the local commits are present, check: `cd $CONTRACTS_SOURCE_ROOT && git log --oneline -5`. The top 3 commits should be the fix and two cherry-picks.

## Steps

### 1. Tear down everything including volumes

```bash
DOCKER_DEFAULT_PLATFORM= docker compose -f docker-compose.yaml -f compose/dev/dips.yaml down -v
```

This destroys all data: chain state, postgres, subgraph deployments, config volume with contract addresses.

### 2. Clear stale Ignition journals

If a previous deployment failed (especially `graph-contracts`), the Hardhat Ignition journal at `$CONTRACTS_SOURCE_ROOT/packages/subgraph-service/ignition/deployments/chain-1337/` will contain partial state that prevents a clean redeploy. Delete it:

```bash
rm -rf $CONTRACTS_SOURCE_ROOT/packages/subgraph-service/ignition/deployments/chain-1337
```

This is safe after a `down -v` since the chain state it references no longer exists.

### 3. Bring everything up

```bash
DOCKER_DEFAULT_PLATFORM= docker compose -f docker-compose.yaml -f compose/dev/dips.yaml up -d --build
```

The `--build` flag ensures any changes to `run.sh` scripts or Dockerfiles are picked up (e.g. chain's `--block-time` flag, config changes baked into images). Without it, Docker reuses cached images and local changes are silently ignored.

Wait for containers to stabilize. The `graph-contracts` container runs first (deploys all Solidity contracts and writes addresses to the config volume), then `subgraph-deploy` deploys three subgraphs (network, TAP, block-oracle). Other services start as their health check dependencies are met.

**Note:** The initial `up -d` may exit with an error if `start-indexing` fails. This is expected -- see step 5. If `graph-contracts` itself fails, check its logs -- the most likely cause is a missing prerequisite commit (see Prerequisites) or a stale Ignition journal (see step 2).

### 4. Verify RecurringCollector was written to horizon.json

```bash
DOCKER_DEFAULT_PLATFORM= docker compose -f docker-compose.yaml -f compose/dev/dips.yaml exec indexer-agent \
jq '.["1337"].RecurringCollector' /opt/config/horizon.json
```

If this returns null, the contracts toolshed wasn't rebuilt after cherry-picking the whitelist fix. Run `cd $CONTRACTS_SOURCE_ROOT/packages/toolshed && pnpm build:self` and repeat from step 1.

### 5. Fix nonce race failures

Multiple containers use ACCOUNT0 concurrently after `graph-contracts` finishes (`start-indexing`, `tap-escrow-manager`). This causes "nonce too low" errors that can fail either container. The cascade is the real problem: if `start-indexing` fails, `dipper` and `ready` never start because they depend on it.

Check whether `start-indexing` exited successfully:

```bash
DOCKER_DEFAULT_PLATFORM= docker compose -f docker-compose.yaml -f compose/dev/dips.yaml ps -a start-indexing --format '{{.Status}}'
```

If it shows `Exited (1)`, restart it:

```bash
DOCKER_DEFAULT_PLATFORM= docker compose -f docker-compose.yaml -f compose/dev/dips.yaml start start-indexing
```

Always restart `tap-escrow-manager` regardless of whether `start-indexing` succeeded. Even when authorization succeeds, the deposit step can hit "nonce too low" from competing with `start-indexing`. The `AlreadyAuthorized` error on restart is harmless -- it re-runs the deposit with a fresh nonce.

```bash
DOCKER_DEFAULT_PLATFORM= docker compose -f docker-compose.yaml -f compose/dev/dips.yaml restart tap-escrow-manager
```

### 6. Bring up any cascade-failed containers

If `start-indexing` failed on the initial `up -d`, containers that depend on it (`dipper`, `ready`) will be stuck in `Created` state. Run `up -d` again to catch them:

```bash
DOCKER_DEFAULT_PLATFORM= docker compose -f docker-compose.yaml -f compose/dev/dips.yaml up -d --build
```

This is idempotent -- already-running containers are left alone.

### 7. Verify signer authorization

```bash
DOCKER_DEFAULT_PLATFORM= docker compose -f docker-compose.yaml -f compose/dev/dips.yaml logs tap-escrow-manager --since 60s 2>&1 | grep -i "authorized"
```

Expected: either `authorized signer=0x70997970C51812dc3A010C7d01b50e0d17dc79C8` (fresh auth) or `AuthorizableSignerAlreadyAuthorized` (already done on first run). Both are fine.

### 8. Wait for TAP subgraph indexing, then verify dipper

The TAP subgraph needs to index the `SignerAuthorized` event before the indexer-service will accept paid queries. Dipper may restart once or twice with "bad indexers: BadResponse(402)" during this window -- this is normal and self-resolves.

Check:

```bash
DOCKER_DEFAULT_PLATFORM= docker compose -f docker-compose.yaml -f compose/dev/dips.yaml ps dipper --format '{{.Name}} {{.Status}}'
```

Should show `dipper Up ... (healthy)`. If still restarting after 60 seconds, check gateway logs for persistent 402s.

### 9. Full status check

```bash
DOCKER_DEFAULT_PLATFORM= docker compose -f docker-compose.yaml -f compose/dev/dips.yaml ps --format '{{.Name}} {{.Status}}' | sort
```

All services should be Up. The key health-checked services are: chain, graph-node, postgres, ipfs, redpanda, indexer-agent, indexer-service, gateway, iisa-scoring, iisa, block-oracle, dipper.

## Architecture notes

The authorization chain that makes gateway queries work:

1. `graph-contracts` deploys all contracts, writes addresses to config volume (`horizon.json`, `tap-contracts.json`)
2. `subgraph-deploy` deploys the TAP subgraph pointing at the Horizon PaymentsEscrow address (from `horizon.json`)
3. `tap-escrow-manager` authorizes ACCOUNT1 (gateway signer) on the PaymentsEscrow contract
4. The TAP subgraph indexes the `SignerAuthorized` event
5. `indexer-service` queries the TAP subgraph, sees ACCOUNT1 is authorized for ACCOUNT0 (the payer)
6. Gateway queries signed by ACCOUNT1 are accepted with 200 instead of 402

## Known issues

- **ACCOUNT0 nonce race**: `start-indexing` and `tap-escrow-manager` both use ACCOUNT0 concurrently after `graph-contracts` finishes. Either can fail with "nonce too low". If `start-indexing` fails, `dipper` and `ready` never start (cascade). The fix is to restart the failed container and run `up -d` again.
- **Stale Ignition journals**: After a failed `graph-contracts` deployment, the journal at `packages/subgraph-service/ignition/deployments/chain-1337/` contains partial state. A fresh `down -v` destroys the chain but not the journal (it's in the mounted source). Always delete it before retrying (step 2).
- The contracts toolshed must be compiled (JS, not just TS) for the RecurringCollector whitelist to take effect. Use `pnpm build:self` in `packages/toolshed` (not `pnpm build` which fails on the `interfaces` package).

## Key contract addresses (change each deploy)

Read from the config volume:

```bash
# All Horizon contracts
docker compose exec indexer-agent cat /opt/config/horizon.json | jq '.["1337"]'

# TAP contracts
docker compose exec indexer-agent cat /opt/config/tap-contracts.json

# Important ones for manual testing:
# GRT Token: jq '.["1337"].L2GraphToken.address' horizon.json
# PaymentsEscrow: jq '.["1337"].PaymentsEscrow.address' horizon.json
# RecurringCollector: jq '.["1337"].RecurringCollector.address' horizon.json
# GraphTallyCollector: jq '.["1337"].GraphTallyCollector.address' horizon.json
```

## Accounts

- ACCOUNT0 (`0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266`): deployer, admin, payer
- ACCOUNT1 (`0x70997970C51812dc3A010C7d01b50e0d17dc79C8`): gateway signer
- RECEIVER (`0xf4EF6650E48d099a4972ea5B414daB86e1998Bd3`): indexer (mnemonic index 0 of "test...zero")
8 changes: 8 additions & 0 deletions .claude/skills/network-status/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
name: network-status
description: Show the current state of the local Graph protocol network. Use when the user asks for "network status", "show me the network", "what's deployed", "which indexers", "which subgraphs", "what's running", or wants to see allocations, sync status, or the network tree.
---

Run `python3 scripts/network-status.py` from the local-network repo root to fetch the current network state.

Output the result directly as text in a code block so it renders inline without the user needing to expand tool results.
86 changes: 86 additions & 0 deletions .claude/skills/send-indexing-request/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
---
name: send-indexing-request
description: Send a test indexing request to dipper via the CLI. Use when testing the DIPs flow end-to-end, when the user asks to register an indexing request, send a test agreement, trigger the DIPs pipeline, or test dipper proposals.
---

# Send Indexing Request

Register an indexing request with dipper and monitor the full DIPs pipeline: IISA candidate selection, RCA proposal signing, and indexer-service accept/reject.

## Steps

### 1. Build the dipper CLI (if not already built)

```bash
cd /Users/samuel/Documents/github/dipper && cargo build --bin dipper-cli --release
```

### 2. Verify dipper is healthy

```bash
DOCKER_DEFAULT_PLATFORM= docker compose -f docker-compose.yaml -f compose/dev/dips.yaml ps dipper --format '{{.Status}}'
```

Should show `Up ... (healthy)`. If not, use the `fresh-deploy` skill first.

### 3. Send the indexing request

```bash
cd /Users/samuel/Documents/github/dipper && ./target/release/dipper-cli indexings register \
--server-url http://localhost:9000 \
--signing-key "0x2ee789a68207020b45607f5adb71933de0946baebbaaab74af7cbd69c8a90573" \
QmPdbQaRCMhgouSZSW3sHZxU3M8KwcngWASvreAexzmmrh \
1337
```

The signing key belongs to RECEIVER (`0xf4EF6650E48d099a4972ea5B414daB86e1998Bd3`). The admin RPC allowlist only accepts this address. ACCOUNT0's key will return 403.

On success, the CLI prints a UUID -- the indexing request ID.

To use a different deployment, query graph-node for available ones:

```bash
DOCKER_DEFAULT_PLATFORM= docker compose -f docker-compose.yaml -f compose/dev/dips.yaml exec graph-node \
curl -s -X POST -H "Content-Type: application/json" \
-d '{"query":"{ indexingStatuses { subgraph chains { network } } }"}' \
http://localhost:8030/graphql
```

### 4. Monitor the pipeline

Check logs from all three services involved in the flow:

```bash
DOCKER_DEFAULT_PLATFORM= docker compose -f docker-compose.yaml -f compose/dev/dips.yaml logs -f dipper iisa indexer-service --since 30s 2>&1
```

The expected sequence:

1. **dipper** receives the request and calls IISA for candidate selection
2. **iisa** scores indexers and returns candidates (only 1 indexer in local-network)
3. **dipper** constructs an RCA, signs it via EIP-712, sends a proposal to indexer-service
4. **indexer-service** validates the RCA and accepts or rejects

### 5. Check request status

```bash
cd /Users/samuel/Documents/github/dipper && ./target/release/dipper-cli indexings status \
--server-url http://localhost:9000 \
--signing-key "0x2ee789a68207020b45607f5adb71933de0946baebbaaab74af7cbd69c8a90573" \
<REQUEST_ID>
```

## Reference

| Detail | Value |
|--------|-------|
| Admin RPC port | 9000 |
| Signing key | RECEIVER: `0x2ee789a68207020b45607f5adb71933de0946baebbaaab74af7cbd69c8a90573` |
| Signing address | `0xf4EF6650E48d099a4972ea5B414daB86e1998Bd3` |
| Chain ID | 1337 (hardhat) |
| Default deployment | `QmPdbQaRCMhgouSZSW3sHZxU3M8KwcngWASvreAexzmmrh` |

## Common rejection reasons

- **SIGNER_NOT_AUTHORISED**: The payer (ACCOUNT0) isn't authorized as a signer on the RecurringCollector contract. The escrow manager authorizes signers on PaymentsEscrow (for TAP) but not on RecurringCollector.
- **PRICE_TOO_LOW**: Dipper's pricing config doesn't meet indexer-service's minimum. Compare `pricing_table` in dipper's run.sh with `min_grt_per_30_days` in indexer-service's config.
Loading