Skip to content
Last updated

Monitor blockchain activity in real-time using GraphQL subscriptions. Track blocks, transactions, and events as they occur without polling.


WebSocket Endpoint

wss://graph.kadindexer.io/graphql

Protocol: graphql-ws

Subscription Limits by Tier:

  • Free: 5 concurrent subscriptions
  • Developer: 25 concurrent subscriptions
  • Team: Custom limits

newBlocks

Subscribe to new blocks as they are mined on specified chains.

subscription NewBlocks($chainIds: [String!]) {
  newBlocks(chainIds: $chainIds, quantity: 20) {
    hash
    height
    chainId
    creationTime
    minerAccount {
      accountName
    }
    transactions(first: 5) {
      totalCount
      edges {
        node {
          hash
        }
      }
    }
    totalGasUsedInKda
  }
}

Variables:

{
  "chainIds": ["0", "1", "2"]
}

Parameters:

  • chainIds - Filter specific chains (omit for all 20 chains)
  • quantity - Initial payload size (default: 20)

Use Cases:

  • Block explorer real-time feeds
  • Network health dashboards
  • Mining pool monitoring
  • Transaction volume tracking

Notes:

  • Streams continuous updates for each new block
  • Returns latest blocks immediately on connection
  • Use chainIds to reduce message volume

newBlocksFromDepth

Subscribe to blocks after they reach specified confirmation depth.

subscription ConfirmedBlocks($minimumDepth: Int!) {
  newBlocksFromDepth(
    chainIds: ["0", "1"]
    minimumDepth: $minimumDepth
    quantity: 10
  ) {
    hash
    height
    chainId
    canonical
    creationTime
    transactions {
      totalCount
    }
  }
}

Variables:

{
  "minimumDepth": 5
}

Parameters:

  • minimumDepth - Required confirmations before emission
  • chainIds - Filter specific chains
  • quantity - Initial payload size

Recommended Depths:

  • 5-10: Standard finality
  • 20+: Exchange deposits, high-value transactions

Use Cases:

  • Exchange deposit confirmations
  • Settlement systems
  • Finality-dependent processing
  • Reorg-resistant notifications

Notes:

  • Only emits blocks after reaching minimumDepth
  • Higher depth = greater finality, more latency
  • canonical flag indicates main chain inclusion

transaction

Track a specific transaction by request key until it's confirmed.

subscription TrackTransaction($requestKey: String!, $chainId: String) {
  transaction(requestKey: $requestKey, chainId: $chainId) {
    hash
    cmd {
      meta {
        chainId
        sender
      }
    }
    result {
      ... on TransactionResult {
        gas
        goodResult
        badResult
        block {
          height
          creationTime
        }
      }
    }
  }
}

Variables:

{
  "requestKey": "abc123keyXYZ",
  "chainId": "0"
}

Parameters:

  • requestKey - Transaction hash from submission (required)
  • chainId - Specific chain (omit to search all chains)

Use Cases:

  • Wallet transaction confirmations
  • Payment processing
  • User notifications
  • Transaction status tracking

Notes:

  • Emits once when transaction is mined and indexed
  • Omit chainId to monitor across all chains
  • Use requestKey from transaction submission response

transactions

Subscribe to all confirmed transactions in real-time.

subscription RecentTransactions {
  transactions(quantity: 20) {
    hash
    cmd {
      meta {
        sender
        chainId
        creationTime
      }
    }
    result {
      ... on TransactionResult {
        gas
      }
    }
  }
}

Parameters:

  • quantity - Initial payload size (default: 20)

Use Cases:

  • Network activity monitoring
  • Transaction feed displays
  • Real-time analytics
  • Activity dashboards

Notes:

  • Streams all confirmed transactions across chains
  • High message volume - use carefully
  • Consider filtering by chain client-side

events

Monitor specific blockchain events with filtering.

subscription CoinTransfers($chainId: String) {
  events(
    qualifiedEventName: "coin.TRANSFER"
    chainId: $chainId
    minimumDepth: 5
    quantity: 20
  ) {
    name
    parameterText
    block {
      height
      creationTime
    }
    transaction {
      hash
    }
  }
}

Variables:

{
  "chainId": "1"
}

Parameters:

  • qualifiedEventName - Event name (format: module.EVENT) (required)
  • chainId - Filter specific chain
  • minimumDepth - Wait for confirmations
  • parametersFilter - JSON filter for event parameters
  • quantity - Initial payload size

Common Event Names:

  • coin.TRANSFER - KDA transfers
  • coin.ALLOCATION - Gas allocations
  • Custom events: free.my-contract.EVENT_NAME

Use Cases:

  • Token transfer notifications
  • DeFi protocol monitoring
  • Smart contract event tracking
  • Cross-chain activity alerts

Notes:

  • Event names are case-sensitive
  • Use minimumDepth for finality confidence
  • parametersFilter uses Prisma JSON filter syntax

Event Parameter Filtering

Filter events by parameter values using JSON filters.

subscription TransfersFromAccount($accountName: String!) {
  events(
    qualifiedEventName: "coin.TRANSFER"
    parametersFilter: "{\"array_starts_with\":\"k:5a2afbc...\"}"
    minimumDepth: 5
  ) {
    parameterText
    block {
      height
    }
  }
}

Filter Syntax:

{
  "parametersFilter": "{\"array_starts_with\":\"k:account123\"}"
}

Common Patterns:

  • {"array_starts_with":"value"} - First element matches (sender)
  • {"array_contains":"value"} - Contains element anywhere
  • {"string_contains":"substring"} - String match

Use Cases:

  • Account-specific notifications
  • Value threshold alerts
  • Address filtering
  • Custom parameter matching

Notes:

  • Must be JSON-stringified Prisma filter
  • Use array_starts_with for sender in transfers
  • Combine with other filters for precision

Multi-Subscription Pattern

Combine multiple subscriptions for comprehensive monitoring.

subscription DashboardData {
  # New blocks on primary chains
  blocks: newBlocks(chainIds: ["0", "1", "8"]) {
    height
    chainId
  }
  
  # Confirmed transfers
  transfers: events(
    qualifiedEventName: "coin.TRANSFER"
    minimumDepth: 5
  ) {
    parameterText
  }
  
  # Specific transaction
  myTx: transaction(requestKey: "abc123") {
    hash
    result {
      ... on TransactionResult {
        goodResult
      }
    }
  }
}

Use Cases:

  • Comprehensive dashboards
  • Multi-chain monitoring
  • Parallel event tracking
  • Real-time analytics

Notes:

  • Each subscription counts toward tier limit
  • Use aliases to distinguish results
  • Close unused subscriptions promptly

Practical Examples

Example 1: Wallet Transaction Confirmation

subscription WalletConfirmation($requestKey: String!) {
  transaction(requestKey: $requestKey) {
    hash
    result {
      ... on TransactionResult {
        gas
        goodResult
        badResult
        block {
          height
        }
      }
    }
  }
}
// Usage
const unsubscribe = client.subscribe(query, {
  requestKey: userTransactionHash
}, {
  next: (data) => {
    if (data.transaction.result.goodResult) {
      showSuccess('Transaction confirmed!');
    } else {
      showError('Transaction failed');
    }
    unsubscribe();
  }
});

Example 2: DEX Activity Monitor

subscription DexActivity {
  swaps: events(
    qualifiedEventName: "free.my-dex.SWAP"
    chainId: "8"
  ) {
    parameterText
    block {
      creationTime
    }
  }
  
  liquidityAdds: events(
    qualifiedEventName: "free.my-dex.ADD_LIQUIDITY"
    chainId: "8"
  ) {
    parameterText
  }
}

Example 3: Mining Pool Dashboard

subscription MiningDashboard($minerAccount: String!) {
  newBlocks(chainIds: ["0", "1", "2", "3"]) {
    height
    chainId
    minerAccount {
      accountName
      balance
    }
    totalGasUsedInKda
  }
}

Filter client-side for specific miner:

client.subscribe(query, {}, {
  next: (data) => {
    const block = data.newBlocks[0];
    if (block.minerAccount.accountName === targetMiner) {
      updateMiningStats(block);
    }
  }
});

Example 4: Exchange Deposit Monitor

subscription ExchangeDeposits($exchangeAddress: String!) {
  events(
    qualifiedEventName: "coin.TRANSFER"
    parametersFilter: "{\"array_contains\":\"k:exchange-address\"}"
    minimumDepth: 20
  ) {
    parameterText
    block {
      height
    }
  }
}

Best Practices

Request Only Needed Fields

# ❌ Over-fetching
subscription {
  newBlocks(chainIds: ["0"]) {
    hash
    height
    chainId
    creationTime
    difficulty
    nonce
    weight
    powHash
    target
    # ... all fields
  }
}

# ✅ Minimal
subscription {
  newBlocks(chainIds: ["0"]) {
    hash
    height
  }
}

Use Appropriate Filters

# ❌ Broad (high message volume)
subscription {
  events(qualifiedEventName: "coin.TRANSFER") {
    parameterText
  }
}

# ✅ Filtered (targeted)
subscription {
  events(
    qualifiedEventName: "coin.TRANSFER"
    chainId: "0"
    minimumDepth: 5
  ) {
    parameterText
  }
}

Close Unused Subscriptions

const subscriptions = new Set();

function subscribe(query, variables, handlers) {
  const unsubscribe = client.subscribe(query, variables, handlers);
  subscriptions.add(unsubscribe);
  
  return () => {
    unsubscribe();
    subscriptions.delete(unsubscribe);
  };
}

// Clean up on exit
window.addEventListener('beforeunload', () => {
  subscriptions.forEach(unsub => unsub());
});

Handle High-Frequency Updates

const buffer = [];
let processing = false;

client.subscribe(query, {}, {
  next: (data) => {
    buffer.push(data);
    if (!processing) processBuffer();
  }
});

async function processBuffer() {
  processing = true;
  while (buffer.length > 0) {
    const batch = buffer.splice(0, 10);
    await processBatch(batch);
  }
  processing = false;
}

Troubleshooting

Not Receiving Events

Check:

  • Event name spelling (case-sensitive)
  • Chain ID matches event location
  • minimumDepth not too high
  • Connection still active
  • Subscription within tier limits

High Latency

Solutions:

  • Reduce requested fields
  • Apply more restrictive filters
  • Check network latency
  • Verify keepAlive configuration

Missing Initial Data

Solution: Use quantity parameter to get recent data on connection:

subscription {
  newBlocks(chainIds: ["0"], quantity: 20) {
    hash
    height
  }
}

Next Steps

Need help? toni@hackachain.io