Advanced Usage Guide

Complex Integration Patterns

Real-time Market Making

Track pool state changes and price movements in real-time:

import { createClient, gql } from '@apollo/client';

const POOL_STATE = gql`
  query GetPoolState($poolId: ID!, $lastTimestamp: Int!) {
    pandaPool(id: $poolId) {
      price
      pandaReserve
      baseReserve
      swaps(
        where: { timestamp_gt: $lastTimestamp }
        orderBy: timestamp
        orderDirection: desc
        first: 1
      ) {
        timestamp
        amountPandaIn
        amountPandaOut
        averagePrice
      }
    }
  }
`;

class MarketMaker {
  private lastPoll: number = 0;
  private readonly POLL_INTERVAL = 1000; // 1 second

  async monitorPool(poolId: string) {
    while (true) {
      const { data } = await client.query({
        query: POOL_STATE,
        variables: {
          poolId,
          lastTimestamp: this.lastPoll
        },
        fetchPolicy: 'network-only' // Bypass cache
      });

      this.updateStrategy(data.pandaPool);
      this.lastPoll = Math.floor(Date.now() / 1000);
      await new Promise(resolve => setTimeout(resolve, this.POLL_INTERVAL));
    }
  }

  private updateStrategy(poolData: any) {
    // Implement your market making strategy
  }
}

Historical Analysis Engine

Efficiently process historical data for analytics:

const HISTORICAL_DATA = gql`
  query GetHistoricalData($startTime: Int!, $endTime: Int!) {
    priceSnapshots(
      where: {
        timestamp_gte: $startTime,
        timestamp_lte: $endTime,
        timeframe: HOUR
      }
      orderBy: timestamp
      orderDirection: asc
    ) {
      timestamp
      open
      high
      low
      close
      volume
    }
  }
`;

class AnalysisEngine {
  async analyzeTimeframe(startTime: number, endTime: number) {
    const batchSize = 1000;
    let currentStart = startTime;
    const results = [];

    while (currentStart < endTime) {
      const batchEnd = Math.min(currentStart + batchSize * 3600, endTime);
      const { data } = await client.query({
        query: HISTORICAL_DATA,
        variables: {
          startTime: currentStart,
          endTime: batchEnd
        }
      });
      
      results.push(...data.priceSnapshots);
      currentStart = batchEnd + 1;
    }

    return this.processResults(results);
  }

  private processResults(snapshots: any[]) {
    // Implement your analysis logic
  }
}

Performance Optimization

Caching Strategies

Implement efficient caching for frequently accessed data:

class SubgraphCache {
  private cache: Map<string, {
    data: any,
    timestamp: number
  }> = new Map();

  private readonly TTL = 60 * 1000; // 1 minute

  async getCachedData(key: string, fetchFn: () => Promise<any>) {
    const cached = this.cache.get(key);
    
    if (cached && Date.now() - cached.timestamp < this.TTL) {
      return cached.data;
    }

    const data = await fetchFn();
    this.cache.set(key, {
      data,
      timestamp: Date.now()
    });

    return data;
  }

  invalidateCache(key?: string) {
    if (key) {
      this.cache.delete(key);
    } else {
      this.cache.clear();
    }
  }
}

Batch Processing

Efficiently handle multiple queries:

class BatchProcessor {
  private queue: Set<string> = new Set();
  private processing = false;
  private readonly BATCH_SIZE = 100;

  async queuePool(poolId: string) {
    this.queue.add(poolId);
    if (!this.processing) {
      await this.processBatch();
    }
  }

  private async processBatch() {
    this.processing = true;
    
    while (this.queue.size > 0) {
      const batch = Array.from(this.queue).slice(0, this.BATCH_SIZE);
      batch.forEach(id => this.queue.delete(id));

      const { data } = await client.query({
        query: POOL_DATA,
        variables: { poolIds: batch }
      });

      await this.handleBatchResults(data);
    }

    this.processing = false;
  }
}

Error Handling & Recovery

Robust Query Handler

Handle network issues and retry failed queries:

class RobustQueryHandler {
  private readonly MAX_RETRIES = 3;
  private readonly BACKOFF_BASE = 1000; // 1 second

  async executeQuery(query: any, variables: any) {
    for (let attempt = 0; attempt < this.MAX_RETRIES; attempt++) {
      try {
        return await client.query({ query, variables });
      } catch (error) {
        if (!this.shouldRetry(error) || attempt === this.MAX_RETRIES - 1) {
          throw error;
        }

        await this.sleep(this.getBackoffTime(attempt));
      }
    }
  }

  private shouldRetry(error: any): boolean {
    // Implement retry logic based on error type
    return error.message.includes('network')
      || error.message.includes('timeout');
  }

  private getBackoffTime(attempt: number): number {
    return this.BACKOFF_BASE * Math.pow(2, attempt);
  }

  private sleep(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
}

Edge Cases

Handling Network Upgrades

class NetworkUpgradeHandler {
  private lastKnownBlock: number = 0;

  async checkNetworkContinuity() {
    const { data } = await client.query({
      query: gql`
        {
          _meta {
            block {
              number
            }
          }
        }
      `
    });

    const currentBlock = data._meta.block.number;
    
    if (this.lastKnownBlock > 0) {
      const gap = currentBlock - this.lastKnownBlock;
      if (gap > 100) { // Potential network upgrade
        await this.handleNetworkUpgrade(this.lastKnownBlock, currentBlock);
      }
    }

    this.lastKnownBlock = currentBlock;
  }

  private async handleNetworkUpgrade(lastBlock: number, currentBlock: number) {
    // Implement upgrade handling logic
  }
}

Best Practices & Tips

1. Query Optimization

// Efficient field selection
const OPTIMIZED_QUERY = gql`
  query GetPoolData($poolId: ID!) {
    pandaPool(id: $poolId) {
      price   # Only request needed fields
      volumeUSD
      # Avoid requesting unnecessary relationships
    }
  }
`;

2. Rate Limiting

class RateLimiter {
  private timestamps: number[] = [];
  private readonly WINDOW_MS = 1000; // 1 second
  private readonly MAX_REQUESTS = 10;

  async executeWithRateLimit(fn: () => Promise<any>) {
    this.timestamps = this.timestamps.filter(
      ts => Date.now() - ts < this.WINDOW_MS
    );

    if (this.timestamps.length >= this.MAX_REQUESTS) {
      const oldestTimestamp = this.timestamps[0];
      const waitTime = this.WINDOW_MS - (Date.now() - oldestTimestamp);
      await new Promise(resolve => setTimeout(resolve, waitTime));
    }

    this.timestamps.push(Date.now());
    return fn();
  }
}

Monitoring & Debugging

Performance Monitoring

class QueryMonitor {
  private metrics: {
    [key: string]: {
      count: number;
      totalTime: number;
      errors: number;
    }
  } = {};

  async trackQuery(name: string, query: Promise<any>) {
    const start = Date.now();
    try {
      const result = await query;
      this.recordSuccess(name, Date.now() - start);
      return result;
    } catch (error) {
      this.recordError(name);
      throw error;
    }
  }

  getMetrics() {
    return Object.entries(this.metrics).map(([name, data]) => ({
      name,
      averageTime: data.totalTime / data.count,
      errorRate: data.errors / data.count,
      totalCalls: data.count
    }));
  }
}

Next Steps

  • Explore our WebSocket API for real-time updates

  • Check our GitHub for example implementations

  • Join our Developer Discord for advanced support

Last updated