AIOUM subgraph

Mirrored from subgraph/README.md in the repository — edit that file, not this page.

A Graph Protocol subgraph for AIOUM discovery & analytics. It indexes services, payment sessions, staking/delegation, reputation and identities into queryable GraphQL entities.

What it indexes

ContractEventsEntities touched
ServiceRegistryServiceRegistered, ServiceUpdatedNode, Service
PaymentManagerSessionOpened, SessionSettled, SessionReclaimed, SessionDisputedNode, Service, Session
StakingManagerStaked, Delegated, RewardNotified, SlashedNode, Delegation
ReputationRegistrySettlementRecorded, FeedbackSubmittedReputation, Feedback
IdentityRegistryRegisteredIdentity
DisputeManager, the arbitratorsDisputeRaised, DisputeResolved, votes, commits, revealsDispute, DisputeVote, DisputeCommit
EndpointRegistryEndpointSet, EndpointClearedEndpoint (a node's address and certificate pin)
OperatorSignersSignerSet, SignerClearedSigner
ReviewManagerReviewRequested, ReviewFinalizedPaidReview
AIOUMTokenTransferTokenHolder, TokenTransfer, TokenStats

Entities: Node, Service, Session, Delegation, Feedback, Reputation, Identity, Dispute, Endpoint, Signer, PaidReview, TokenHolder, TokenTransfer, TokenStats, with Protocol and the per-asset stats as the totals. With these, a front-end runs from an RPC and this subgraph alone: the app reads them when NEXT_PUBLIC_SUBGRAPH_URL names a graph-node that serves this subgraph (dapp/lib/subgraph.ts).

All mapping handlers are idempotent (load-or-create), so reprocessing a block (e.g. after a reorg) never double-counts, and entities referenced before their own "create" event (e.g. a settle before an indexed open) are materialised as stubs.

ABIs

abis/*.json contain only the .abi arrays extracted from the Foundry build artifacts in ../out/<Contract>.sol/<Contract>.json. To regenerate them after a contract change:

cd ..   # repo root (AIOUM)
for c in ServiceRegistry PaymentManager StakingManager ReputationRegistry IdentityRegistry; do
  jq '.abi' "out/$c.sol/$c.json" > "subgraph/abis/$c.json"
done

Base Sepolia

networks.json carries the Base Sepolia deploy of 2026-10-12 (offchain/deployments.84532.json, every contract from block 48,020,971; no arbitrator contract is deployed there, so those two stay at zero). The public RPCs for Base Sepolia prune old history and refuse block 0, which graph-node asks for to identify the chain: give it the deploy block instead with GRAPH_ETHEREUM_GENESIS_BLOCK_NUMBER=48020971, and keep GRAPH_ETHEREUM_MAX_BLOCK_RANGE_SIZE within what the RPC allows (publicnode: 1000).

npm run codegen && graph build --network base-sepolia
GRAPH_ETHEREUM="base-sepolia:https://base-sepolia-rpc.publicnode.com" GRAPH_MAX_BLOCK_RANGE=1000 GRAPH_GENESIS_BLOCK=48020971 docker compose up -d
npm run create-local
graph deploy --node http://localhost:8020/ --ipfs http://localhost:5001 --network base-sepolia --version-label v0.1.0 aioum/aioum-v2
# then: curl -s localhost:8000/subgraphs/name/aioum/aioum-v2 -d '{"query":"{ protocol(id:\"protocol\") { totalServices settledSessions } }"}'

Any host with Docker runs an indexer the same way: the compose file above, or two containers on their own (Postgres 16 made with POSTGRES_INITDB_ARGS="-E UTF8 --locale=C", and graph-node started by its image's own script with postgres_host, postgres_port, postgres_user, postgres_pass, postgres_db, node_id, ipfs=https://api.thegraph.com/ipfs/ and ethereum=base-sepolia:<rpc>, plus the two GRAPH_ETHEREUM_* settings). An indexer is one of any number: the app takes a comma-separated list of them in NEXT_PUBLIC_SUBGRAPH_URL and asks the next when one does not answer.

To The Graph's network instead: graph auth <deploy key> then graph deploy --studio aioum-base-sepolia --network base-sepolia; the query URL it prints goes into the app as NEXT_PUBLIC_SUBGRAPH_URL.

Networks

subgraph.yaml carries network: mainnet as the manifest's network key; the contract addresses for each target live in networks.json, one key per network. The checked-in entries hold the zero address with startBlock: 0 until you fill them in for your deployment.

Before deploying, set the deployed addresses in networks.json (see ../offchain/deployments.json) and the startBlock at which the contracts were deployed, then re-apply them to the manifest:

graph build --network mainnet      # injects networks.json[mainnet] into subgraph.yaml

To index your local development network, point graph-node at http://127.0.0.1:8545 (chainId 31337) and set the addresses from ../offchain/deployments.json:

  • serviceRegistry → ServiceRegistry
  • payment → PaymentManager
  • staking → StakingManager
  • reputation → ReputationRegistry
  • identity → IdentityRegistry

(You may rename the network key to e.g. anvil in both networks.json and the network: field of every dataSource in subgraph.yaml if your graph-node is configured with that network name.)

Build & deploy

Install the dependencies first:

cd subgraph
npm install           # installs @graphprotocol/graph-cli + graph-ts

Then the standard workflow:

npm run codegen       # graph codegen — generates ./generated/* from schema + ABIs
npm run build         # graph build  — compiles AssemblyScript to WASM

codegen must run before build (and before TypeScript/AssemblyScript will resolve the ../generated/* imports in src/mappings.ts).

Deploy to a local graph-node

npm run create-local  # registers the subgraph name on a local graph-node
npm run deploy-local  # graph deploy --node http://localhost:8020/ --ipfs http://localhost:5001

Deploy to a hosted/Studio endpoint

npm run deploy        # graph deploy --network mainnet aioum/aioum-v2

Adjust the subgraph slug (aioum/aioum-v2), --node, --ipfs and auth (graph auth) for your target. See https://thegraph.com/docs for the current Studio flow.

Example queries

# Active services with their node, cheapest first
{
  services(where: { active: true }, orderBy: pricePerCall, orderDirection: asc) {
    serviceId
    category
    pricePerCall
    node { operator totalStaked totalDelegated active }
  }
}

# A node's sessions and settlement split
{
  node(id: "0x...") {
    operator
    totalStaked
    sessions {
      sessionId
      status
      deposited
      gross
      operatorNet
      protocolFee
      delegatorShare
      refund
    }
  }
}

# Operator reputation roll-up
{
  reputations(orderBy: totalVolume, orderDirection: desc) {
    client
    operator
    settlementCount
    totalVolume
    feedbackCount
    scoreSum
  }
}