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
- Completed Getting Started with GetBlock RPC
- Understanding of async JavaScript/TypeScript
- Familiarity with Solana transaction lifecycle
WebSocket Subscriptions
GetBlock provides WebSocket endpoints for real-time blockchain updates:
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:
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:
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:
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:
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:
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
- Explore Optimizing RPC Performance for specific use cases
- Learn about GetBlock's Enterprise Features for scaling
- Join the GetBlock Documentation for support
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!