learn.sol

Advanced GetBlock RPC Features

Master advanced GetBlock RPC features including WebSocket subscriptions, load balancing, transaction batching, MEV protection, and production monitoring for Solana apps.

Deep dive into GetBlock's advanced capabilities for professional Solana infrastructure.

What You'll Learn

  • WebSocket subscriptions for real-time data
  • Load balancing across multiple endpoints
  • Transaction optimization and batching
  • Advanced monitoring and debugging
  • MEV protection configuration

Prerequisites


WebSocket Subscriptions

GetBlock provides WebSocket endpoints for real-time blockchain updates:

websocket-subscriptions.ts
import { Connection, PublicKey } from "@solana/web3.js";

const connection = new Connection(
  process.env.NEXT_PUBLIC_SOLANA_RPC_MAINNET!,
  {
    commitment: "confirmed",
    wsEndpoint: "wss://go.getblock.io/{your-access-token}/",
  }
);

// Subscribe to account changes
async function subscribeToAccount(accountPubkey: string) {
  const pubkey = new PublicKey(accountPubkey);
  
  const subscriptionId = connection.onAccountChange(
    pubkey,
    (accountInfo, context) => {
      console.log("Account updated:", {
        slot: context.slot,
        lamports: accountInfo.lamports,
        owner: accountInfo.owner.toBase58(),
      });
    },
    "confirmed"
  );
  
  console.log(`Subscribed to account: ${accountPubkey}`);
  console.log(`Subscription ID: ${subscriptionId}`);
  
  return subscriptionId;
}

// Subscribe to program accounts (e.g., all token accounts)
async function subscribeToProgramAccounts(programId: string) {
  const pubkey = new PublicKey(programId);
  
  const subscriptionId = connection.onProgramAccountChange(
    pubkey,
    (keyedAccountInfo, context) => {
      console.log("Program account updated:", {
        publicKey: keyedAccountInfo.accountId.toBase58(),
        slot: context.slot,
        lamports: keyedAccountInfo.accountInfo.lamports,
      });
    },
    "confirmed"
  );
  
  return subscriptionId;
}

// Subscribe to logs (for transaction monitoring)
async function subscribeToLogs(address?: string) {
  const filter = address ? { mentions: [address] } : "all";
  
  const subscriptionId = connection.onLogs(
    filter,
    (logs, context) => {
      console.log("Logs received:", {
        signature: logs.signature,
        slot: context.slot,
        err: logs.err,
        logs: logs.logs,
      });
    },
    "confirmed"
  );
  
  return subscriptionId;
}

// Cleanup: Always remove subscriptions when done
async function cleanup(subscriptionId: number) {
  await connection.removeAccountChangeListener(subscriptionId);
  console.log(`Unsubscribed: ${subscriptionId}`);
}

Performance Best Practice
WebSocket subscriptions consume fewer resources than polling. Use them for real-time updates instead of repeated getAccountInfo calls.


Load Balancing and Failover

Implement automatic failover across multiple GetBlock endpoints:

load-balancer.ts
import { Connection, ConnectionConfig } from "@solana/web3.js";

interface EndpointConfig {
  url: string;
  weight: number;  // For weighted load balancing
  priority: number;  // For failover (lower = higher priority)
}

class LoadBalancedConnection {
  private endpoints: EndpointConfig[];
  private connections: Map<string, Connection>;
  private currentIndex: number = 0;
  private failedEndpoints: Set<string> = new Set();
  
  constructor(endpoints: EndpointConfig[], config?: ConnectionConfig) {
    this.endpoints = endpoints.sort((a, b) => a.priority - b.priority);
    this.connections = new Map();
    
    // Initialize all connections
    endpoints.forEach(endpoint => {
      this.connections.set(
        endpoint.url,
        new Connection(endpoint.url, config)
      );
    });
  }
  
  private getNextEndpoint(): Connection {
    // Filter out failed endpoints
    const availableEndpoints = this.endpoints.filter(
      ep => !this.failedEndpoints.has(ep.url)
    );
    
    if (availableEndpoints.length === 0) {
      // Reset if all endpoints failed
      this.failedEndpoints.clear();
      availableEndpoints.push(...this.endpoints);
    }
    
    // Round-robin selection
    const endpoint = availableEndpoints[this.currentIndex % availableEndpoints.length];
    this.currentIndex++;
    
    return this.connections.get(endpoint.url)!;
  }
  
  private markEndpointFailed(url: string) {
    this.failedEndpoints.add(url);
    console.warn(`Endpoint marked as failed: ${url}`);
    
    // Auto-recover after 30 seconds
    setTimeout(() => {
      this.failedEndpoints.delete(url);
      console.log(`Endpoint recovered: ${url}`);
    }, 30000);
  }
  
  async executeWithFailover<T>(
    operation: (connection: Connection) => Promise<T>,
    maxRetries: number = 3
  ): Promise<T> {
    let lastError: Error | null = null;
    
    for (let i = 0; i < maxRetries; i++) {
      const connection = this.getNextEndpoint();
      
      try {
        return await operation(connection);
      } catch (error) {
        lastError = error as Error;
        console.error(`Request failed, trying next endpoint...`, error);
        
        // Mark current endpoint as failed
        const currentUrl = [...this.connections.entries()]
          .find(([_, conn]) => conn === connection)?.[0];
        if (currentUrl) {
          this.markEndpointFailed(currentUrl);
        }
      }
    }
    
    throw new Error(`All endpoints failed: ${lastError?.message}`);
  }
  
  // Example usage methods
  async getAccountInfo(publicKey: PublicKey) {
    return this.executeWithFailover(
      async (conn) => conn.getAccountInfo(publicKey)
    );
  }
  
  async sendTransaction(transaction: Transaction, signers: Signer[]) {
    return this.executeWithFailover(
      async (conn) => conn.sendTransaction(transaction, signers)
    );
  }
}

// Usage
const loadBalancer = new LoadBalancedConnection([
  { url: "https://go.getblock.io/{primary-token}/", weight: 2, priority: 1 },
  { url: "https://go.getblock.io/{backup-token}/", weight: 1, priority: 2 },
], {
  commitment: "confirmed",
  confirmTransactionInitialTimeout: 60000,
});

Transaction Batching and Optimization

Optimize multiple RPC calls with batching:

transaction-batching.ts
import { Connection, PublicKey } from "@solana/web3.js";

class BatchedConnection {
  private connection: Connection;
  private batchQueue: Array<{
    method: string;
    args: any[];
    resolve: (value: any) => void;
    reject: (error: any) => void;
  }> = [];
  private batchTimeout: NodeJS.Timeout | null = null;
  private readonly BATCH_DELAY = 10; // ms
  private readonly MAX_BATCH_SIZE = 10;
  
  constructor(endpoint: string) {
    this.connection = new Connection(endpoint, "confirmed");
  }
  
  private scheduleBatch() {
    if (this.batchTimeout) return;
    
    this.batchTimeout = setTimeout(() => {
      this.executeBatch();
    }, this.BATCH_DELAY);
  }
  
  private async executeBatch() {
    const batch = this.batchQueue.splice(0, this.MAX_BATCH_SIZE);
    this.batchTimeout = null;
    
    if (batch.length === 0) return;
    
    console.log(`Executing batch of ${batch.length} requests`);
    
    // Execute all requests in parallel
    const results = await Promise.allSettled(
      batch.map(item => 
        this.connection._rpcRequest(item.method, item.args)
      )
    );
    
    // Resolve/reject each promise
    results.forEach((result, index) => {
      if (result.status === "fulfilled") {
        batch[index].resolve(result.value);
      } else {
        batch[index].reject(result.reason);
      }
    });
    
    // Process next batch if queue has items
    if (this.batchQueue.length > 0) {
      this.scheduleBatch();
    }
  }
  
  async getMultipleAccounts(publicKeys: PublicKey[]): Promise<any[]> {
    return new Promise((resolve, reject) => {
      this.batchQueue.push({
        method: "getMultipleAccounts",
        args: [publicKeys.map(pk => pk.toBase58())],
        resolve,
        reject,
      });
      this.scheduleBatch();
    });
  }
  
  async getAccountInfo(publicKey: PublicKey): Promise<any> {
    return new Promise((resolve, reject) => {
      this.batchQueue.push({
        method: "getAccountInfo",
        args: [publicKey.toBase58()],
        resolve,
        reject,
      });
      this.scheduleBatch();
    });
  }
}

MEV Protection

GetBlock provides reliable infrastructure for secure transaction routing. Here's how to leverage it:

mev-protection.ts
import { 
  Connection, 
  Transaction, 
  sendAndConfirmTransaction,
  Keypair
} from "@solana/web3.js";

class MEVProtectedConnection {
  private connection: Connection;
  
  constructor(endpoint: string) {
    this.connection = new Connection(endpoint, {
      commitment: "confirmed",
      // GetBlock's reliable infrastructure ensures secure transaction routing
      confirmTransactionInitialTimeout: 60000,
    });
  }
  
  async sendTransactionWithProtection(
    transaction: Transaction,
    signers: Keypair[],
    options?: {
      skipPreflight?: boolean;
      preflightCommitment?: Commitment;
      maxRetries?: number;
    }
  ) {
    // GetBlock routes transactions through reliable infrastructure
    const signature = await sendAndConfirmTransaction(
      this.connection,
      transaction,
      signers,
      {
        skipPreflight: options?.skipPreflight ?? false,
        preflightCommitment: options?.preflightCommitment ?? "confirmed",
        maxRetries: options?.maxRetries ?? 3,
      }
    );
    
    console.log("Transaction sent with MEV protection:", signature);
    return signature;
  }
  
  // For time-sensitive transactions (e.g., DEX trades)
  async sendUrgentTransaction(
    transaction: Transaction,
    signers: Keypair[]
  ) {
    // Use processed commitment for fastest inclusion
    const signature = await this.connection.sendTransaction(
      transaction,
      signers,
      {
        skipPreflight: true, // Skip simulation for speed
        preflightCommitment: "processed",
        maxRetries: 0, // Don't retry, send immediately
      }
    );
    
    // Manually confirm
    const confirmation = await this.connection.confirmTransaction(
      signature,
      "confirmed"
    );
    
    if (confirmation.value.err) {
      throw new Error(`Transaction failed: ${JSON.stringify(confirmation.value.err)}`);
    }
    
    return signature;
  }
}

MEV Protection Explained
GetBlock's infrastructure provides reliable transaction routing through secure channels. This is especially critical for DeFi applications handling swaps and arbitrage.


Advanced Monitoring

Comprehensive monitoring setup:

advanced-monitoring.ts
import { Connection, PublicKey } from "@solana/web3.js";

interface RPCMetrics {
  totalRequests: number;
  successfulRequests: number;
  failedRequests: number;
  averageLatency: number;
  p95Latency: number;
  p99Latency: number;
  requestsByMethod: Map<string, number>;
  latencies: number[];
}

class MonitoredGetBlockConnection extends Connection {
  private metrics: RPCMetrics = {
    totalRequests: 0,
    successfulRequests: 0,
    failedRequests: 0,
    averageLatency: 0,
    p95Latency: 0,
    p99Latency: 0,
    requestsByMethod: new Map(),
    latencies: [],
  };
  
  override async _rpcRequest(method: string, args: any[]) {
    const startTime = performance.now();
    this.metrics.totalRequests++;
    
    // Track method usage
    const currentCount = this.metrics.requestsByMethod.get(method) || 0;
    this.metrics.requestsByMethod.set(method, currentCount + 1);
    
    try {
      const result = await super._rpcRequest(method, args);
      const latency = performance.now() - startTime;
      
      this.metrics.successfulRequests++;
      this.recordLatency(latency);
      
      // Log slow requests
      if (latency > 1000) {
        console.warn(`Slow RPC request: ${method} took ${latency.toFixed(2)}ms`);
      }
      
      return result;
    } catch (error) {
      this.metrics.failedRequests++;
      console.error(`RPC request failed: ${method}`, error);
      throw error;
    }
  }
  
  private recordLatency(latency: number) {
    this.metrics.latencies.push(latency);
    
    // Keep only last 1000 latencies
    if (this.metrics.latencies.length > 1000) {
      this.metrics.latencies.shift();
    }
    
    // Calculate metrics
    this.calculateLatencyMetrics();
  }
  
  private calculateLatencyMetrics() {
    const sorted = [...this.metrics.latencies].sort((a, b) => a - b);
    const sum = sorted.reduce((acc, val) => acc + val, 0);
    
    this.metrics.averageLatency = sum / sorted.length;
    this.metrics.p95Latency = sorted[Math.floor(sorted.length * 0.95)] || 0;
    this.metrics.p99Latency = sorted[Math.floor(sorted.length * 0.99)] || 0;
  }
  
  getMetrics(): RPCMetrics {
    return { ...this.metrics };
  }
  
  printMetrics() {
    console.log("\n=== RPC Metrics ===");
    console.log(`Total Requests: ${this.metrics.totalRequests}`);
    console.log(`Success Rate: ${((this.metrics.successfulRequests / this.metrics.totalRequests) * 100).toFixed(2)}%`);
    console.log(`Average Latency: ${this.metrics.averageLatency.toFixed(2)}ms`);
    console.log(`P95 Latency: ${this.metrics.p95Latency.toFixed(2)}ms`);
    console.log(`P99 Latency: ${this.metrics.p99Latency.toFixed(2)}ms`);
    console.log("\nRequests by Method:");
    
    const sortedMethods = Array.from(this.metrics.requestsByMethod.entries())
      .sort((a, b) => b[1] - a[1]);
    
    sortedMethods.forEach(([method, count]) => {
      console.log(`  ${method}: ${count}`);
    });
  }
  
  // Export metrics for external monitoring (e.g., Datadog, Prometheus)
  exportMetrics() {
    return {
      timestamp: Date.now(),
      ...this.metrics,
      requestsByMethod: Object.fromEntries(this.metrics.requestsByMethod),
    };
  }
}

// Usage with periodic reporting
const connection = new MonitoredGetBlockConnection(
  process.env.NEXT_PUBLIC_SOLANA_RPC_MAINNET!,
  "confirmed"
);

// Report metrics every minute
setInterval(() => {
  connection.printMetrics();
}, 60000);

Performance Benchmarking

Compare GetBlock performance against other providers:

benchmark.ts
import { Connection, PublicKey } from "@solana/web3.js";

interface BenchmarkResult {
  provider: string;
  averageLatency: number;
  minLatency: number;
  maxLatency: number;
  successRate: number;
}

async function benchmarkProvider(
  endpoint: string,
  providerName: string,
  iterations: number = 100
): Promise<BenchmarkResult> {
  const connection = new Connection(endpoint, "confirmed");
  const latencies: number[] = [];
  let successes = 0;
  
  // Use a well-known account for testing
  const testAccount = new PublicKey("So11111111111111111111111111111111111111112"); // Wrapped SOL
  
  console.log(`Benchmarking ${providerName}...`);
  
  for (let i = 0; i < iterations; i++) {
    const startTime = performance.now();
    
    try {
      await connection.getAccountInfo(testAccount);
      const latency = performance.now() - startTime;
      latencies.push(latency);
      successes++;
    } catch (error) {
      console.error(`Request ${i + 1} failed for ${providerName}`);
    }
    
    // Small delay between requests
    await new Promise(resolve => setTimeout(resolve, 100));
  }
  
  const averageLatency = latencies.reduce((a, b) => a + b, 0) / latencies.length;
  const minLatency = Math.min(...latencies);
  const maxLatency = Math.max(...latencies);
  const successRate = (successes / iterations) * 100;
  
  return {
    provider: providerName,
    averageLatency,
    minLatency,
    maxLatency,
    successRate,
  };
}

async function runBenchmarks() {
  const providers = [
    { name: "GetBlock", endpoint: process.env.NEXT_PUBLIC_SOLANA_RPC_MAINNET! },
    // Add other providers for comparison
  ];
  
  const results: BenchmarkResult[] = [];
  
  for (const provider of providers) {
    const result = await benchmarkProvider(provider.endpoint, provider.name);
    results.push(result);
  }
  
  console.log("\n=== Benchmark Results ===");
  results.forEach(result => {
    console.log(`\n${result.provider}:`);
    console.log(`  Average Latency: ${result.averageLatency.toFixed(2)}ms`);
    console.log(`  Min Latency: ${result.minLatency.toFixed(2)}ms`);
    console.log(`  Max Latency: ${result.maxLatency.toFixed(2)}ms`);
    console.log(`  Success Rate: ${result.successRate.toFixed(2)}%`);
  });
}

Production Checklist

Before deploying to production with GetBlock:

API Key Security

  • Store keys in environment variables
  • Use separate keys for dev/prod
  • Rotate keys periodically

Error Handling

  • Implement retry logic
  • Add failover endpoints
  • Log errors for monitoring

Performance

  • Use appropriate commitment levels
  • Enable WebSocket for real-time data
  • Implement request batching

Monitoring

  • Track RPC metrics
  • Set up alerts for failures
  • Monitor latency trends

MEV Protection

  • Verify GetBlock's reliable infrastructure is configured
  • Test with time-sensitive transactions
  • Monitor for front-running attempts

Next Steps


Summary

You've mastered advanced GetBlock features including:

✅ Real-time WebSocket subscriptions
✅ Load balancing and automatic failover
✅ Transaction batching and optimization
✅ MEV protection for secure transactions
✅ Comprehensive monitoring and benchmarking

Your application is now production-ready with enterprise-grade infrastructure!

Solana Assistant

AI-powered documentation helper

Welcome to Solana Assistant

Ask specific questions about Solana development:

Ask specific questions for better results400px
    Advanced GetBlock RPC Features | learn.sol