Skip to content

Roles Indexer

Comprehensive real-time indexing for the Roles contract, providing complete audit trails, user role management, and analytics for role-based access control events.

Overview

The Roles indexer processes all role management events from the Roles contract, transforming raw blockchain events into structured, queryable data. It provides real-time tracking of role assignments, ownership changes, and comprehensive analytics for governance transparency.

Features

🔍 Event Processing

  • RoleGranted: Real-time role assignment tracking
  • RoleRevoked: Role removal with state updates
  • OwnershipTransferred: Complete ownership history
  • OwnershipTransferStarted: 2-step ownership process support

📊 Data Analytics

  • Role holder statistics and metrics
  • User role state management
  • Historical audit trails
  • Ownership governance transparency

🛡️ Data Integrity

  • Duplicate event prevention
  • Transaction-level consistency
  • Type-safe data validation
  • Comprehensive error handling

Database Schema

roleActivities Table

Complete audit trail for all role operations.

{
  id: Hash,              // Ponder's unique event ID (composite primary key with chainId)
  chainId: number,       // Chain ID for multi-chain support
  type: number,          // RoleActivityType enum (GRANTED=0, REVOKED=1)
  role: Hash,            // Role identifier (bytes32)
  account: Address,      // Target account address
  sender: Address,       // Transaction initiator
  timestamp: bigint,     // Block timestamp
  blockNumber: bigint    // Block number
}
Use Cases:
  • Compliance auditing
  • Role assignment history
  • Transaction forensics
  • Governance reporting

users Table

Current role state for each user address.

{
  id: Address,           // User wallet address (composite primary key with chainId)
  chainId: number,       // Chain ID for multi-chain support
  roles: Hash[]          // Array of currently held roles
}
Use Cases:
  • Real-time permission checks
  • User role dashboards
  • Access control verification
  • Role inheritance tracking

roleStats Table

Statistical analytics for role distribution.

{
  id: Hash,              // Role identifier (composite primary key with chainId)
  chainId: number,       // Chain ID for multi-chain support
  totalHolders: number,  // Current number of role holders
  lastUpdated: bigint    // Last update timestamp
}
Use Cases:
  • Role distribution analytics
  • Governance metrics
  • Security monitoring
  • Administrative insights

rolesOwnershipHistory Table

Complete ownership transfer history for governance transparency.

{
  id: Hash,              // Unique ownership change ID (composite primary key with chainId)
  chainId: number,       // Chain ID for multi-chain support
  previousOwner: Address, // Previous contract owner
  newOwner: Address,     // New contract owner
  timestamp: bigint,     // Transfer timestamp
  blockNumber: bigint    // Block number
}
Use Cases:
  • Governance transparency
  • Ownership audit trails
  • Legal compliance
  • Historical analysis

GraphQL API

Core Queries

Get Recent Role Activities

query RecentActivities($limit: Int = 10) {
  roleActivities(
    limit: $limit, 
    orderBy: "timestamp", 
    orderDirection: "desc"
  ) {
    items {
      id
      type
      role
      account
      sender
      timestamp
      blockNumber
    }
  }
}

Get User Roles

query UserRoles($address: String!) {
  user(id: $address) {
    id
    roles
  }
}

Get Role Statistics

query RoleStats($role: String!) {
  roleStat(id: $role) {
    id
    totalHolders
    lastUpdated
  }
}

Get Ownership History

query OwnershipHistory($limit: Int = 20) {
  rolesOwnershipHistories(
    limit: $limit,
    orderBy: "timestamp",
    orderDirection: "desc"
  ) {
    items {
      id
      previousOwner
      newOwner
      timestamp
      blockNumber
    }
  }
}

Advanced Queries

Role Activity by Type

query RoleActivitiesByType($type: RoleActivityType!, $limit: Int = 50) {
  roleActivities(
    where: { type: $type },
    limit: $limit,
    orderBy: "timestamp",
    orderDirection: "desc"
  ) {
    items {
      id
      role
      account
      sender
      timestamp
    }
  }
}

Users with Specific Role

query UsersWithRole($role: String!) {
  users(where: { roles: { has: $role } }) {
    items {
      id
      roles
    }
  }
}

Recent Role Changes for User

query UserRoleHistory($account: String!, $limit: Int = 10) {
  roleActivities(
    where: { account: $account },
    limit: $limit,
    orderBy: "timestamp",
    orderDirection: "desc"
  ) {
    items {
      id
      type
      role
      sender
      timestamp
      blockNumber
    }
  }
}

Implementation Examples

TypeScript Integration

import { GraphQLClient } from 'graphql-request'
 
const client = new GraphQLClient('http://localhost:42069')
 
// Check if user has specific role
async function hasRole(userAddress: string, roleHash: string): Promise<boolean> {
  const query = `
    query CheckUserRole($address: String!) {
      user(id: $address) {
        roles
      }
    }
  `
  
  const response = await client.request(query, { address: userAddress })
  return response.user?.roles.includes(roleHash) || false
}
 
// Get role assignment history
async function getRoleHistory(userAddress: string) {
  const query = `
    query UserRoleHistory($account: String!) {
      roleActivities(
        where: { account: $account },
        orderBy: "timestamp",
        orderDirection: "desc"
      ) {
        items {
          type
          role
          timestamp
          sender
        }
      }
    }
  `
  
  return client.request(query, { account: userAddress })
}
 
// Monitor role statistics
async function getRoleMetrics(roleHash: string) {
  const query = `
    query RoleMetrics($role: String!) {
      roleStat(id: $role) {
        totalHolders
        lastUpdated
      }
      
      roleActivities(
        where: { role: $role },
        limit: 100,
        orderBy: "timestamp",
        orderDirection: "desc"
      ) {
        items {
          type
          timestamp
        }
      }
    }
  `
  
  return client.request(query, { role: roleHash })
}

React Hook Example

import { useQuery } from '@tanstack/react-query'
import { GraphQLClient } from 'graphql-request'
 
const client = new GraphQLClient('http://localhost:42069')
 
export function useUserRoles(address: string) {
  return useQuery({
    queryKey: ['userRoles', address],
    queryFn: async () => {
      const query = `
        query UserRoles($address: String!) {
          user(id: $address) {
            id
            roles
          }
        }
      `
      return client.request(query, { address })
    },
    enabled: !!address,
    staleTime: 1000 * 30, // 30 seconds
  })
}
 
export function useRoleActivities(limit = 10) {
  return useQuery({
    queryKey: ['roleActivities', limit],
    queryFn: async () => {
      const query = `
        query RecentActivities($limit: Int!) {
          roleActivities(
            limit: $limit,
            orderBy: "timestamp",
            orderDirection: "desc"
          ) {
            items {
              id
              type
              role
              account
              sender
              timestamp
            }
          }
        }
      `
      return client.request(query, { limit })
    },
    refetchInterval: 5000, // Refresh every 5 seconds
  })
}

Security Considerations

Data Validation

  • Type Safety: Strict TypeScript typing throughout
  • Input Sanitization: Address and hash validation
  • Range Checks: Timestamp and block number validation
  • Consistency Checks: Cross-table data integrity

Access Control

  • Read-only API: GraphQL endpoint provides read access only
  • Rate Limiting: API request throttling (configurable)
  • CORS Configuration: Controlled cross-origin access
  • Authentication: Optional API key protection

Error Handling

// Comprehensive error handling in event processors
try {
  await processRoleGranted(event)
} catch (error) {
  console.error(`Failed to process RoleGranted event: ${event.id}`, error)
  // Implement retry logic or alert systems
}

Deployment Guide

Local Development

# Start anvil blockchain
pnpm anvil
 
# Deploy contracts
pnpm contracts:script
 
# Start indexer
pnpm indexer:dev
 
# Access GraphQL playground
open http://localhost:42069

Production Setup

# Environment configuration
export PONDER_RPC_URL="https://your-rpc-endpoint"
export PONDER_DATABASE_URL="postgresql://..."
 
# Build and start
pnpm indexer:build
pnpm indexer:start

Configuration

// ponder.config.ts
export default createConfig({
  chains: {
    mainnet: {
      id: 1,
      rpc: process.env.PONDER_RPC_URL!,
    },
  },
  contracts: {
    Roles: {
      chain: "mainnet",
      abi: RolesAbi,
      address: "0x...", // Deployed contract address
      startBlock: 12345678, // Contract deployment block
    },
  },
})