A multi-chain subgraph for indexing ERC-8004 Trustless Agents protocol data, providing GraphQL APIs for agent discovery, reputation tracking, and validation across multiple networks.
Bug reports & feedback: Telegram: Agent0 channel | Email: team@ag0.xyz
| Network | Chain ID | Status | Endpoint |
|---|---|---|---|
| Ethereum Mainnet | 1 | β Deployed | Endpoint |
| Base Mainnet | 8453 | β Deployed | Endpoint |
| BSC Mainnet | 56 | β Deployed | Endpoint |
| Polygon Mainnet | 137 | β Deployed | Endpoint |
| Monad | 143 | β Deployed | Endpoint |
| Ethereum Sepolia | 11155111 | β Deployed | Endpoint |
| Base Sepolia | 84532 | β Deployed | Endpoint |
| BSC Chapel | 97 | β Deployed | Endpoint |
| Monad Testnet | 10143 | β Deployed | Endpoint |
| Polygon Amoy | 80002 | βοΈ Contracts not deployed | - |
| Linea Sepolia | 59141 | βοΈ Contracts not deployed | - |
| Hedera Testnet | 296 | βοΈ Contracts not deployed | - |
| HyperEVM Testnet | 998 | βοΈ Contracts not deployed | - |
| SKALE Base Sepolia | 1351057110 | βοΈ Contracts not deployed | - |
Note: The Graph Gateway endpoints require authentication (API key / authorization header). If you see an βauth errorβ, use the gateway form https://gateway.thegraph.com/api/<API_KEY>/subgraphs/id/<SUBGRAPH_ID>.
- Node.js 20.18.1+ (required by the pinned
@graphprotocol/graph-cli) - The Graph CLI is installed as a project dependency (you can run it via
npx graph ...ornpm run ...)
# Install dependencies
npm install
# Validate network configurations
npm run validate
# Generate manifests for all networks
npm run generate
# Build all network deployments
npm run build:allThis subgraph uses a template-based multi-chain architecture inspired by Messari's subgraph infrastructure, enabling a single codebase to deploy across 9 networks with minimal duplication.
π Project Structure
βββ config/
β βββ networks/ # Network-specific configurations
β β βββ eth-sepolia.json # Contract addresses, start blocks
β β βββ base-sepolia.json
β β βββ ... (9 networks)
β βββ subgraph.template.yaml # Mustache template for manifests
βββ deployments/
β βββ deployment.json # Master deployment tracking
β βββ generated/ # Generated subgraph.yaml files
β βββ erc-8004-eth-sepolia/
β βββ erc-8004-base-sepolia/
β βββ ... (9 deployments)
βββ src/ # Shared handler code (95%+ reuse)
βββ scripts/ # Build automation
# Validate all network configurations
npm run validate
# Generate manifests from template
npm run generate
# Build all deployments (runs codegen + build for each network)
npm run build:all
# Build single deployment
DEPLOYMENT=erc-8004-base-sepolia npm run build:single- Create network config:
config/networks/new-network.json
{
"network": "new-network",
"chainId": "123456",
"displayName": "New Network",
"identityRegistry": {
"address": "0x...",
"startBlock": 1
},
"reputationRegistry": {
"address": "0x...",
"startBlock": 1
},
"validationRegistry": {
"address": "0x...",
"startBlock": 1
},
"graphNode": {
"network": "new-network"
}
}- Add to
deployments/deployment.json
{
"erc-8004": {
"deployments": {
"erc-8004-new-network": {
"network": "new-network",
"status": "prod",
"configFile": "config/networks/new-network.json",
"versions": {
"schema": "1.0.0",
"subgraph": "1.0.0"
}
}
}
}
}- Add chain ID mapping in
src/utils/chain.ts
if (network == "new-network") {
return 123456 // New Network chain ID
}- Add contract addresses in
src/contract-addresses.ts
if (chainId.equals(BigInt.fromI32(123456))) {
return new ContractAddresses(
Bytes.fromHexString("0x..."), // Identity
Bytes.fromHexString("0x..."), // Reputation
Bytes.fromHexString("0x...") // Validation
)
}- Generate and build:
npm run build:all
Time to add new network: < 5 minutes
# Deploy to The Graph Studio (requires auth token)
# Set your deployment key first:
# npx graph auth --studio <DEPLOY_KEY>
# Deploy specific network
DEPLOYMENT=erc-8004-eth-sepolia npm run deploy
# Deploy to Graph Studio (recommended; lets you set a release label)
# STUDIO_SLUG=<your-studio-subgraph-slug> DEPLOYMENT=erc-8004-eth-sepolia VERSION_LABEL=<your-release-label> npm run deploy:studio
# Or deploy locally for testing
npm run create-local && npm run deploy-localThis subgraph indexes data from three core smart contracts implementing the ERC-8004 standard:
| Contract | Purpose | Events Indexed |
|---|---|---|
| IdentityRegistry | Agent registration and metadata management | Registered, MetadataSet, UriUpdated, Transfer |
| ReputationRegistry | Feedback and reputation tracking | NewFeedback, FeedbackRevoked, ResponseAppended |
| ValidationRegistry | Agent validation and attestation | ValidationRequest, ValidationResponse |
- π Comprehensive Agent Data - On-chain registration with rich off-chain metadata
- π Real-time Reputation - Live feedback scoring and response tracking
- β Validation Tracking - Complete validation lifecycle with status management
- π IPFS Integration - Native JSON parsing via File Data Sources
- π Rich Relationships - Connected data through derived fields and references
- π Multi-Chain Support - Single codebase deploying to 8 networks
The subgraph uses a hybrid on-chain/off-chain architecture:
Core blockchain data stored directly from contract events:
type Agent @entity(immutable: false) {
id: ID! # "chainId:agentId"
chainId: BigInt! # Blockchain identifier
agentId: BigInt! # Agent ID on the chain
agentURI: String # Registration file URI
agentURIType: String # "ipfs", "https", "http", "unknown"
owner: Bytes! # Agent owner address
operators: [Bytes!]! # Authorized operators
createdAt: BigInt!
updatedAt: BigInt!
totalFeedback: BigInt! # Computed feedback count
lastActivity: BigInt! # Last activity timestamp
registrationFile: AgentRegistrationFile # Link to off-chain data
feedback: [Feedback!]!
validations: [Validation!]!
metadata: [AgentMetadata!]!
}type Feedback @entity(immutable: false) {
id: ID! # "chainId:agentId:clientAddress:index"
agent: Agent!
clientAddress: Bytes! # Feedback author
score: Int! # 0-100 score
tag1: String # Primary category tag
tag2: String # Secondary category tag
feedbackUri: String # IPFS/HTPPS URI for rich content
feedbackURIType: String
feedbackHash: Bytes!
isRevoked: Boolean!
createdAt: BigInt!
revokedAt: BigInt
feedbackFile: FeedbackFile # Link to off-chain data
responses: [FeedbackResponse!]!
}type Validation @entity(immutable: false) {
id: ID! # requestHash
agent: Agent!
validatorAddress: Bytes!
requestUri: String
requestHash: Bytes!
response: Int # 0-100 score (0 = pending)
responseUri: String
responseHash: Bytes
tag: String # Human-readable validation tag
status: ValidationStatus! # PENDING, COMPLETED, EXPIRED
createdAt: BigInt!
updatedAt: BigInt!
}
enum ValidationStatus {
PENDING
COMPLETED
EXPIRED
}Rich metadata fetched from IPFS/HTTPS URIs:
type AgentRegistrationFile @entity(immutable: true) {
id: ID! # Format: "transactionHash:cid"
cid: String! # IPFS CID (for querying by content)
agentId: String! # "chainId:agentId"
name: String # Agent display name
description: String # Agent description
image: String # Profile image URL
active: Boolean # Is agent active
x402Support: Boolean # Supports x402 payments
supportedTrusts: [String!]! # Trust models: "reputation", "cryptoeconomic", "tee-attestation"
mcpEndpoint: String # Model Context Protocol endpoint
mcpVersion: String
mcpTools: [String!]! # Available MCP tools
mcpPrompts: [String!]! # Available MCP prompts
mcpResources: [String!]! # Available MCP resources
a2aEndpoint: String # Agent-to-Agent endpoint
a2aVersion: String
a2aSkills: [String!]! # Available A2A skills
ens: String # ENS name
did: String # Decentralized identifier
agentWallet: Bytes # Agent wallet address
agentWalletChainId: BigInt # Wallet chain ID
createdAt: BigInt!
}type FeedbackFile @entity(immutable: true) {
id: ID! # Format: "transactionHash:cid"
cid: String! # IPFS CID (for querying by content)
feedbackId: String! # "chainId:agentId:clientAddress:index"
text: String # Detailed feedback text
capability: String # Capability being rated
name: String # Client name
skill: String # Skill being evaluated
task: String # Task context
context: String # Additional context
proofOfPaymentFromAddress: String
proofOfPaymentToAddress: String
proofOfPaymentChainId: String
proofOfPaymentTxHash: String
tag1: String # Fallback if on-chain tags empty
tag2: String
createdAt: BigInt!
}This subgraph uses Timeseries + @aggregation for scalable protocol- and agent-level analytics (daily/hourly rollups). This replaces the old GlobalStats/AgentStats pattern.
type Protocol @entity(immutable: false) {
id: ID! # "chainId"
chainId: BigInt!
name: String!
identityRegistry: Bytes!
reputationRegistry: Bytes!
validationRegistry: Bytes!
createdAt: BigInt!
updatedAt: BigInt!
}type ProtocolFeedbackStats @aggregation(intervals: ["hour", "day"], source: "FeedbackPoint") {
id: Int8!
timestamp: Timestamp!
protocol: Protocol!
feedbackCreated: Int8!
feedbackRevoked: Int8!
valueSum: BigDecimal!
valueDeltaSum: BigDecimal!
}
type AgentFeedbackStats @aggregation(intervals: ["hour", "day"], source: "FeedbackPoint") {
id: Int8!
timestamp: Timestamp!
protocol: Protocol!
agent: Agent!
feedbackCreated: Int8!
feedbackRevoked: Int8!
valueSum: BigDecimal!
valueDeltaSum: BigDecimal!
}query GetCompleteAgentDetails($agentId: ID!) {
agent(id: $agentId) {
id
chainId
agentId
owner
agentURI
createdAt
updatedAt
totalFeedback
lastActivity
registrationFile {
name
description
image
active
x402Support
supportedTrusts
mcpEndpoint
mcpVersion
mcpTools
a2aEndpoint
a2aVersion
a2aSkills
ens
did
agentWallet
agentWalletChainId
}
feedback(where: { isRevoked: false }, first: 10) {
score
tag1
tag2
clientAddress
createdAt
feedbackFile {
text
capability
skill
task
context
}
responses {
responder
createdAt
}
}
validations(orderBy: createdAt, orderDirection: desc) {
validatorAddress
response
status
tag
createdAt
}
}
}query GetAllMCPAgents {
agentRegistrationFiles(
where: { mcpEndpoint_not: null, active: true }
first: 100
) {
id
agentId
name
description
mcpEndpoint
mcpVersion
mcpTools
supportedTrusts
}
}query GetHighRatedFeedback($minScore: Int!) {
feedbacks(
where: { isRevoked: false, score_gte: $minScore }
first: 100
orderBy: score
orderDirection: desc
) {
id
score
tag1
tag2
agent {
id
registrationFile {
name
description
}
}
feedbackFile {
text
capability
skill
}
responses {
responder
createdAt
}
}
}query FindAgentsByTrustModel($trustModel: String!) {
agentRegistrationFiles(
where: { supportedTrusts_contains: [$trustModel], active: true }
first: 50
) {
agentId
name
description
supportedTrusts
}
}query GetSepoliaFeedbackStatsDaily {
protocolFeedbackStats(
interval: "day"
current: include
where: { protocol: "11155111" }
first: 14
) {
timestamp
feedbackCreated
feedbackRevoked
valueSum
valueDeltaSum
}
}The subgraph uses File Data Sources to parse off-chain content:
- Handler:
src/registration-file.ts - Trigger: When
agentURIpoints to IPFS/HTTPS content - Output:
AgentRegistrationFileentity - Data Parsed: Metadata, capabilities, endpoints, identity information
- Handler:
src/feedback-file.ts - Trigger: When
feedbackUripoints to IPFS/HTTPS content - Output:
FeedbackFileentity - Data Parsed: Detailed feedback text, proof of payment, context
- IPFS:
ipfs://QmHash...or bareQmHash... - HTTPS:
https://example.com/file.json - HTTP:
http://example.com/file.json
- On-chain Events β Contract events trigger indexing
- URI Detection β Subgraph detects IPFS/HTTPS URIs
- File Fetching β File Data Sources fetch and parse JSON
- Entity Creation β Immutable file entities created
- Relationship Links β On-chain entities link to file entities
- Statistics Update β Aggregate statistics computed
Addresses are managed in src/contract-addresses.ts for dynamic per-chain resolution.
Important: Local development requires Docker. The old graph node CLI command is no longer supported. Use Docker Compose as described below.
Prerequisites:
- Docker and Docker Compose installed (Get Docker)
Setup Steps:
- Start local Graph node, IPFS, and PostgreSQL using Docker Compose:
docker compose up -d- Wait for services to be ready (this may take a minute):
# Check service status
docker compose ps- Create local subgraph:
npm run create-local- Deploy locally:
npm run deploy-local- Query local endpoint:
# Simple query (just IDs)
curl -X POST \
-H "Content-Type: application/json" \
-d '{"query": "{ agents(first: 5) { id } }"}' \
http://localhost:8000/subgraphs/name/agent0-sdk/agent0-sdk
# Complete query with agent name (note: name is in registrationFile, not directly on Agent)
curl -X POST \
-H "Content-Type: application/json" \
-d '{"query": "{ agents(first: 5) { id registrationFile { name description } } }"}' \
http://localhost:8000/subgraphs/name/agent0-sdk/agent0-sdkSee examples/queries.graphql for comprehensive query examples:
- Complete agent profiles with relationships
- MCP/A2A protocol filtering
- Feedback analysis and search
- Global statistics and analytics
- Trust model filtering
Agent0 SDK is MIT-licensed public good brought to you by Marco De Rossi in collaboration with Consensys, π¦ MetaMask and Agent0, Inc. We are looking for co-maintainers. Please reach out if you want to help.
Thanks also to Edge & Node (The Graph), Protocol Labs and Pinata for their support.