Advanced GetBlock RPC Features
Master advanced GetBlock features for building high-performance, production-ready Solana applications
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!