Skip to main content

Using TheGraph for Indexing

The Graph Protocol is a decentralized indexing protocol that enables efficient querying of blockchain data. In Rahat, we use The Graph to index blockchain events and provide fast, reliable access to on-chain data through GraphQL APIs.

Overview

What is The Graph?

The Graph is an indexing protocol for querying networks like Ethereum and IPFS. Anyone can build and publish open APIs, called subgraphs, making data easily accessible.

Why Use Subgraphs in Rahat?

  • Real-time Data: Index blockchain events as they happen
  • Efficient Queries: GraphQL API for complex data queries
  • Decentralized: No single point of failure
  • Cost-effective: Reduce RPC calls and gas costs
  • Scalable: Handle high-volume blockchain data

Architecture

Rahat Subgraph Components

Rahat Platform
├── Smart Contracts (EVM/Stellar)
├── The Graph Subgraph
│ ├── Schema Definition
│ ├── Event Handlers
│ ├── Entity Mappings
│ └── GraphQL API
└── Frontend Applications

Indexed Events

The Rahat subgraph indexes the following blockchain events:

Token Events

  • Transfer - ERC-20 token transfers
  • Approval - Token approval events
  • Mint - Token minting operations
  • Burn - Token burning operations

Beneficiary Events

  • BeneficiaryAdded - New beneficiary registration
  • BeneficiaryRemoved - Beneficiary removal
  • BeneficiaryUpdated - Beneficiary information updates

Project Events

  • ProjectCreated - New project initialization
  • TokensAllocated - Token budget allocation
  • TokensDisbursed - Token distribution to beneficiaries
  • CampaignCreated - Aid campaign creation

Claim Events

  • ClaimCreated - New claim submission
  • ClaimProcessed - Claim processing completion
  • OtpVerified - OTP verification events
  • OfflineClaimProcessed - Offline claim processing

Vendor Events

  • VendorRegistered - Vendor registration
  • RedemptionProcessed - Token redemption events

Setup and Installation

Prerequisites

  1. Node.js (v18 or higher)
  2. Docker (for local Graph Node)
  3. Graph CLI - Install globally:
    npm install -g @graphprotocol/graph-cli

Environment Configuration

Create a .env file in your project root:

# Database
DATABASE_URL="postgresql://rahat:rahat@localhost:5432/rahat-rp"

# Redis
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=

# Blockchain
PRIVATE_KEY=your_private_key
RPC_URL=your_rpc_url

# The Graph
GRAPH_NODE_URL=http://localhost:8020
IPFS_URL=http://localhost:5001
GRAPH_STUDIO_KEY=your_graph_studio_key

Project Structure

apps/
└── graph/ # The Graph subgraph
├── src/
│ ├── mappings/ # Event handlers
│ └── schema.graphql # GraphQL schema
├── subgraph.yaml # Subgraph manifest
└── package.json

Subgraph Development

1. Schema Definition

Define your GraphQL schema in schema.graphql:

type Beneficiary @entity {
id: ID!
address: Bytes!
name: String
phone: String
type: BeneficiaryType!
createdAt: BigInt!
updatedAt: BigInt!
claims: [Claim!]! @derivedFrom(field: "beneficiary")
transfers: [Transfer!]! @derivedFrom(field: "to")
}

type Project @entity {
id: ID!
name: String!
token: Token!
totalAllocated: BigInt!
totalDisbursed: BigInt!
beneficiaries: [Beneficiary!]!
campaigns: [Campaign!]!
createdAt: BigInt!
}

type Token @entity {
id: ID!
address: Bytes!
name: String!
symbol: String!
decimals: Int!
totalSupply: BigInt!
transfers: [Transfer!]! @derivedFrom(field: "token")
}

type Transfer @entity {
id: ID!
token: Token!
from: Beneficiary
to: Beneficiary!
amount: BigInt!
timestamp: BigInt!
transactionHash: String!
}

type Claim @entity {
id: ID!
beneficiary: Beneficiary!
amount: BigInt!
status: ClaimStatus!
otpVerified: Boolean!
createdAt: BigInt!
processedAt: BigInt
}

enum BeneficiaryType {
ENROLLED
REFERRED
}

enum ClaimStatus {
PENDING
PROCESSED
REJECTED
}

2. Event Handlers

Create mapping functions in src/mappings/:

// src/mappings/beneficiary.ts
import { BeneficiaryAdded, BeneficiaryRemoved } from '../generated/RahatProject/RahatProject'
import { Beneficiary } from '../generated/schema'

export function handleBeneficiaryAdded(event: BeneficiaryAdded): void {
let beneficiary = new Beneficiary(event.params.beneficiary.toHexString())
beneficiary.address = event.params.beneficiary
beneficiary.name = event.params.name
beneficiary.phone = event.params.phone
beneficiary.type = event.params.beneficiaryType
beneficiary.createdAt = event.block.timestamp
beneficiary.updatedAt = event.block.timestamp
beneficiary.save()
}

export function handleBeneficiaryRemoved(event: BeneficiaryRemoved): void {
let beneficiary = Beneficiary.load(event.params.beneficiary.toHexString())
if (beneficiary) {
beneficiary.updatedAt = event.block.timestamp
beneficiary.save()
}
}
// src/mappings/token.ts
import { Transfer } from '../generated/RahatToken/RahatToken'
import { Transfer as TransferEntity, Token, Beneficiary } from '../generated/schema'

export function handleTransfer(event: Transfer): void {
let transfer = new TransferEntity(event.transaction.hash.toHexString())
transfer.token = event.address.toHexString()
transfer.from = event.params.from.toHexString()
transfer.to = event.params.to.toHexString()
transfer.amount = event.params.value
transfer.timestamp = event.block.timestamp
transfer.transactionHash = event.transaction.hash.toHexString()
transfer.save()
}

3. Subgraph Manifest

Configure your subgraph in subgraph.yaml:

specVersion: 0.0.4
schema:
file: ./schema.graphql
dataSources:
- kind: ethereum
name: RahatProject
network: mainnet
source:
address: "0x..."
abi: RahatProject
startBlock: 12345678
mapping:
kind: ethereum/events
apiVersion: 0.0.6
language: wasm/assemblyscript
entities:
- Beneficiary
- Project
- Claim
abis:
- name: RahatProject
file: ./abis/RahatProject.json
eventHandlers:
- event: BeneficiaryAdded(indexed address,string,string,uint8)
handler: handleBeneficiaryAdded
- event: BeneficiaryRemoved(indexed address)
handler: handleBeneficiaryRemoved
- event: ClaimCreated(indexed address,uint256)
handler: handleClaimCreated
file: ./src/mappings/beneficiary.ts
- kind: ethereum
name: RahatToken
network: mainnet
source:
address: "0x..."
abi: RahatToken
startBlock: 12345678
mapping:
kind: ethereum/events
apiVersion: 0.0.6
language: wasm/assemblyscript
entities:
- Token
- Transfer
abis:
- name: RahatToken
file: ./abis/RahatToken.json
eventHandlers:
- event: Transfer(indexed address,indexed address,uint256)
handler: handleTransfer
file: ./src/mappings/token.ts

Deployment

Local Development

  1. Start Local Graph Node:

    docker-compose up -d
  2. Create Local Subgraph:

    graph create --node http://localhost:8020 rahat/local
  3. Deploy to Local Node:

    graph deploy --node http://localhost:8020 --ipfs http://localhost:5001 rahat/local

Hosted Service Deployment

  1. Authenticate with Graph Studio:

    graph auth --product hosted-service <ACCESS_TOKEN>
  2. Deploy to Hosted Service:

    graph deploy --product hosted-service <GITHUB_USERNAME>/<SUBGRAPH_NAME>

Available Scripts

# Generate GraphQL types
pnpm graph:codegen

# Build subgraph
pnpm graph:build

# Deploy to hosted service
pnpm graph:deploy

# Local development
pnpm graph:deploy-local

# Create local subgraph
pnpm graph:create-local

Querying Data

GraphQL Queries

Once deployed, you can query your subgraph using GraphQL:

Get All Beneficiaries

query GetBeneficiaries {
beneficiaries {
id
address
name
phone
type
createdAt
}
}

Get Beneficiary with Claims

query GetBeneficiaryWithClaims($id: ID!) {
beneficiary(id: $id) {
id
name
claims {
id
amount
status
createdAt
}
}
}

Get Token Transfers

query GetTransfers($token: String!) {
transfers(where: { token: $token }) {
id
from {
name
}
to {
name
}
amount
timestamp
}
}

Get Project Statistics

query GetProjectStats($id: ID!) {
project(id: $id) {
id
name
totalAllocated
totalDisbursed
beneficiaries {
id
name
}
}
}

Using in Applications

JavaScript/TypeScript

import { GraphQuery } from '@rahataid/el-subgraph';

const query = `
query GetBeneficiaries {
beneficiaries {
id
name
type
}
}
`;

const result = await GraphQuery(query, {
endpoint: 'YOUR_SUBGRAPH_ENDPOINT',
});

React Hook

import { useQuery } from '@apollo/client';

const GET_BENEFICIARIES = gql`
query GetBeneficiaries {
beneficiaries {
id
name
type
}
}
`;

function BeneficiaryList() {
const { loading, error, data } = useQuery(GET_BENEFICIARIES);

if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;

return (
<ul>
{data.beneficiaries.map(beneficiary => (
<li key={beneficiary.id}>{beneficiary.name}</li>
))}
</ul>
);
}

Monitoring and Maintenance

Health Checks

  1. Subgraph Status: Monitor indexing status in Graph Studio
  2. Sync Status: Check if subgraph is synced with latest blocks
  3. Error Logs: Review failed indexing events
  4. Performance: Monitor query response times

Common Issues

Indexing Failures

  • Cause: Invalid event data or mapping errors
  • Solution: Check event handlers and entity definitions

Sync Delays

  • Cause: High blockchain activity or network issues
  • Solution: Monitor block processing and network status

Query Timeouts

  • Cause: Complex queries or large datasets
  • Solution: Optimize queries and add pagination

Best Practices

  1. Event Design: Emit events with indexed parameters for efficient querying
  2. Entity Relationships: Use derived fields to avoid redundant data
  3. Pagination: Implement cursor-based pagination for large datasets
  4. Caching: Cache frequently accessed data in your application
  5. Error Handling: Implement proper error handling for subgraph queries

Integration with Rahat Projects

C2C (Crypto-to-Crypto)

  • Indexes token transfers and beneficiary events
  • Tracks multi-signature approvals
  • Monitors project budget allocations

CVA (Cash Voucher Assistance)

  • Indexes claim creation and processing
  • Tracks vendor registrations and redemptions
  • Monitors token disbursements

AA (Anticipatory Action)

  • Indexes trigger events and forecasts
  • Tracks automated disbursements
  • Monitors communication events

Troubleshooting

Local Development Issues

  1. Docker Not Running:

    docker-compose up -d
  2. Port Conflicts:

    • Ensure ports 8000-8005, 8020, 5001 are available
    • Check for existing Graph Node instances
  3. Authentication Issues:

    graph auth --product hosted-service <NEW_TOKEN>

Deployment Issues

  1. Invalid ABI: Ensure contract ABIs are up-to-date
  2. Network Mismatch: Verify network configuration in subgraph.yaml
  3. Start Block: Set appropriate start block for indexing

Query Issues

  1. Schema Mismatch: Regenerate types after schema changes
  2. Missing Entities: Ensure all entities are defined in schema
  3. Invalid Queries: Use GraphQL playground to test queries

Resources

Support

For issues specific to Rahat subgraphs:

  • Check the project's GitHub repository
  • Review existing issues and pull requests
  • Contact the development team
  • Join the community Discord/Telegram channels