Skip to content

agent0lab/subgraph

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

26 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Agent0 SDK Subgraph

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

🌐 Supported Networks

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>.

πŸš€ Quick Start

Prerequisites

  • 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 ... or npm run ...)

Installation

# 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:all

πŸ› οΈ Multi-Chain Development

This 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.

Architecture Overview

πŸ“ 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

Key Commands

Development Workflow

# 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

Adding a New Network

  1. 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"
  }
}
  1. 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"
        }
      }
    }
  }
}
  1. Add chain ID mapping in src/utils/chain.ts
if (network == "new-network") {
  return 123456  // New Network chain ID
}
  1. 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
  )
}
  1. Generate and build: npm run build:all

Time to add new network: < 5 minutes

Deployment

# 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-local

πŸ“Š Overview

This 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

Key Features

  • πŸ” 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

πŸ—οΈ Architecture

The subgraph uses a hybrid on-chain/off-chain architecture:

On-Chain Entities (Mutable)

Core blockchain data stored directly from contract events:

Agent Entity

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!]!
}

Feedback Entity

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!]!
}

Validation Entity

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
}

Off-Chain Entities (Immutable from IPFS)

Rich metadata fetched from IPFS/HTTPS URIs:

AgentRegistrationFile

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!
}

FeedbackFile

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!
}

Analytics (Timeseries + Aggregations)

This subgraph uses Timeseries + @aggregation for scalable protocol- and agent-level analytics (daily/hourly rollups). This replaces the old GlobalStats/AgentStats pattern.

Protocol (chain-scoped root)

type Protocol @entity(immutable: false) {
  id: ID!                    # "chainId"
  chainId: BigInt!
  name: String!
  identityRegistry: Bytes!
  reputationRegistry: Bytes!
  validationRegistry: Bytes!
  createdAt: BigInt!
  updatedAt: BigInt!
}

Example aggregations (queryable with interval: "hour" | "day")

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 Examples

Get Complete Agent Profile

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
    }
  }
}

Find MCP-Compatible Agents

query GetAllMCPAgents {
  agentRegistrationFiles(
    where: { mcpEndpoint_not: null, active: true }
    first: 100
  ) {
    id
    agentId
    name
    description
    mcpEndpoint
    mcpVersion
    mcpTools
    supportedTrusts
  }
}

Search for High-Rated Feedback

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
    }
  }
}

Find Agents by Trust Model

query FindAgentsByTrustModel($trustModel: String!) {
  agentRegistrationFiles(
    where: { supportedTrusts_contains: [$trustModel], active: true }
    first: 50
  ) {
    agentId
    name
    description
    supportedTrusts
  }
}

Get Chain Statistics (Aggregations)

query GetSepoliaFeedbackStatsDaily {
  protocolFeedbackStats(
    interval: "day"
    current: include
    where: { protocol: "11155111" }
    first: 14
  ) {
    timestamp
    feedbackCreated
    feedbackRevoked
    valueSum
    valueDeltaSum
  }
}

πŸ“ IPFS File Data Sources

The subgraph uses File Data Sources to parse off-chain content:

RegistrationFile Data Source

  • Handler: src/registration-file.ts
  • Trigger: When agentURI points to IPFS/HTTPS content
  • Output: AgentRegistrationFile entity
  • Data Parsed: Metadata, capabilities, endpoints, identity information

FeedbackFile Data Source

  • Handler: src/feedback-file.ts
  • Trigger: When feedbackUri points to IPFS/HTTPS content
  • Output: FeedbackFile entity
  • Data Parsed: Detailed feedback text, proof of payment, context

Supported URI Formats

  • IPFS: ipfs://QmHash... or bare QmHash...
  • HTTPS: https://example.com/file.json
  • HTTP: http://example.com/file.json

πŸ”„ Data Flow

  1. On-chain Events β†’ Contract events trigger indexing
  2. URI Detection β†’ Subgraph detects IPFS/HTTPS URIs
  3. File Fetching β†’ File Data Sources fetch and parse JSON
  4. Entity Creation β†’ Immutable file entities created
  5. Relationship Links β†’ On-chain entities link to file entities
  6. Statistics Update β†’ Aggregate statistics computed

βš™οΈ Configuration

Contract Addresses

Addresses are managed in src/contract-addresses.ts for dynamic per-chain resolution.

πŸš€ Development

Local Development

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:

  1. Start local Graph node, IPFS, and PostgreSQL using Docker Compose:
docker compose up -d
  1. Wait for services to be ready (this may take a minute):
# Check service status
docker compose ps
  1. Create local subgraph:
npm run create-local
  1. Deploy locally:
npm run deploy-local
  1. 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-sdk

Testing Queries

See 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

πŸ“š Additional Resources

πŸ“„ License

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.

About

A subgraph for indexing ERC-8004 protocol data

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors