FortiBlox LogoFortiBlox Docs

Advanced Patterns

Complex multi-method workflows and production-ready patterns for Solana applications

Advanced Patterns

Master complex workflows combining multiple API methods to build production-ready Solana applications. Each pattern includes complete working code, error handling, and optimization strategies.

Payment Processing Pipeline

Build a robust payment processing system that monitors transactions, verifies confirmations, and handles edge cases like reorgs.

Use Case

Process customer payments in a Solana-based e-commerce platform, ensuring transactions are confirmed before fulfilling orders.

Architecture

sequenceDiagram
    participant Customer
    participant App
    participant WebSocket
    participant RPC
    participant Database

    Customer->>App: Initiate payment
    App->>Customer: Return payment address
    Customer->>Solana: Send transaction
    WebSocket->>App: Transaction notification
    App->>RPC: getTransaction (verify)
    RPC->>App: Transaction details
    App->>RPC: getSignatureStatuses (confirmations)
    RPC->>App: Confirmation status
    alt Confirmed
        App->>Database: Mark payment complete
        App->>Customer: Order fulfilled
    else Failed/Reorg
        App->>Customer: Retry required
    end

Complete Implementation

import { Connection, PublicKey, Commitment } from '@solana/web3.js';

interface PaymentConfig {
  recipientAddress: string;
  expectedAmount: number;
  timeout: number; // milliseconds
  requiredConfirmations: number;
}

class PaymentProcessor {
  private connection: Connection;
  private wsConnection: Connection;

  constructor(rpcUrl: string, wsUrl: string) {
    this.connection = new Connection(rpcUrl, 'confirmed');
    this.wsConnection = new Connection(wsUrl, {
      commitment: 'confirmed',
      wsEndpoint: wsUrl,
    });
  }

  /**
   * Monitor and process a payment
   */
  async processPayment(config: PaymentConfig): Promise<PaymentResult> {
    const recipient = new PublicKey(config.recipientAddress);
    const startTime = Date.now();

    return new Promise((resolve, reject) => {
      let subscriptionId: number | null = null;
      let timeoutId: NodeJS.Timeout;

      // Set timeout
      timeoutId = setTimeout(() => {
        if (subscriptionId !== null) {
          this.wsConnection.removeAccountChangeListener(subscriptionId);
        }
        reject(new Error('Payment timeout exceeded'));
      }, config.timeout);

      // Subscribe to account changes
      subscriptionId = this.wsConnection.onAccountChange(
        recipient,
        async (accountInfo, context) => {
          try {
            console.log(`Account changed at slot ${context.slot}`);

            // Get recent signatures
            const signatures = await this.connection.getSignaturesForAddress(
              recipient,
              { limit: 10 },
              'confirmed'
            );

            // Process each signature
            for (const sigInfo of signatures) {
              if (sigInfo.err) continue;

              // Verify transaction
              const result = await this.verifyPaymentTransaction(
                sigInfo.signature,
                config
              );

              if (result.valid) {
                // Check confirmations
                const confirmed = await this.waitForConfirmations(
                  sigInfo.signature,
                  config.requiredConfirmations
                );

                if (confirmed) {
                  clearTimeout(timeoutId);
                  if (subscriptionId !== null) {
                    this.wsConnection.removeAccountChangeListener(subscriptionId);
                  }

                  resolve({
                    success: true,
                    signature: sigInfo.signature,
                    amount: result.amount,
                    timestamp: Date.now(),
                    confirmations: config.requiredConfirmations,
                  });
                  return;
                }
              }
            }
          } catch (error) {
            console.error('Error processing account change:', error);
          }
        },
        'confirmed'
      );

      console.log(`Monitoring payments to ${config.recipientAddress}`);
    });
  }

  /**
   * Verify a transaction matches payment criteria
   */
  private async verifyPaymentTransaction(
    signature: string,
    config: PaymentConfig
  ): Promise<{ valid: boolean; amount?: number }> {
    try {
      const tx = await this.connection.getTransaction(signature, {
        maxSupportedTransactionVersion: 0,
        commitment: 'confirmed',
      });

      if (!tx || tx.meta?.err) {
        return { valid: false };
      }

      const recipient = new PublicKey(config.recipientAddress);
      const recipientIndex = tx.transaction.message.staticAccountKeys.findIndex(
        key => key.equals(recipient)
      );

      if (recipientIndex === -1) {
        return { valid: false };
      }

      // Check balance change
      const preBalance = tx.meta.preBalances[recipientIndex];
      const postBalance = tx.meta.postBalances[recipientIndex];
      const amountReceived = (postBalance - preBalance) / 1e9; // Convert to SOL

      if (amountReceived >= config.expectedAmount) {
        return { valid: true, amount: amountReceived };
      }

      return { valid: false };
    } catch (error) {
      console.error('Error verifying transaction:', error);
      return { valid: false };
    }
  }

  /**
   * Wait for required confirmations
   */
  private async waitForConfirmations(
    signature: string,
    requiredConfirmations: number
  ): Promise<boolean> {
    const maxAttempts = 30;
    const delayMs = 2000;

    for (let i = 0; i < maxAttempts; i++) {
      try {
        const statuses = await this.connection.getSignatureStatuses([signature]);
        const status = statuses.value[0];

        if (!status) {
          await this.delay(delayMs);
          continue;
        }

        if (status.err) {
          console.error('Transaction failed:', status.err);
          return false;
        }

        const confirmations = status.confirmations || 0;
        console.log(`Confirmations: ${confirmations}/${requiredConfirmations}`);

        if (confirmations >= requiredConfirmations) {
          return true;
        }

        // Check if finalized
        if (status.confirmationStatus === 'finalized') {
          return true;
        }

        await this.delay(delayMs);
      } catch (error) {
        console.error('Error checking confirmations:', error);
        await this.delay(delayMs);
      }
    }

    return false;
  }

  /**
   * Handle reorg detection
   */
  async detectReorg(signature: string): Promise<boolean> {
    try {
      const statuses = await this.connection.getSignatureStatuses([signature]);
      const status = statuses.value[0];

      // If transaction no longer exists, possible reorg
      if (!status) {
        console.warn(`Transaction ${signature} not found - possible reorg`);
        return true;
      }

      return false;
    } catch (error) {
      console.error('Error detecting reorg:', error);
      return false;
    }
  }

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

interface PaymentResult {
  success: boolean;
  signature: string;
  amount: number;
  timestamp: number;
  confirmations: number;
}

// Usage example
async function example() {
  const processor = new PaymentProcessor(
    'https://rpc.solana.com',
    'wss://rpc.solana.com'
  );

  try {
    const result = await processor.processPayment({
      recipientAddress: 'YourWalletAddressHere',
      expectedAmount: 0.1, // 0.1 SOL
      timeout: 300000, // 5 minutes
      requiredConfirmations: 15,
    });

    console.log('Payment processed:', result);
  } catch (error) {
    console.error('Payment processing failed:', error);
  }
}

Error Handling

class PaymentError extends Error {
  constructor(
    message: string,
    public code: PaymentErrorCode,
    public signature?: string
  ) {
    super(message);
    this.name = 'PaymentError';
  }
}

enum PaymentErrorCode {
  TIMEOUT = 'TIMEOUT',
  INSUFFICIENT_AMOUNT = 'INSUFFICIENT_AMOUNT',
  TRANSACTION_FAILED = 'TRANSACTION_FAILED',
  REORG_DETECTED = 'REORG_DETECTED',
  NETWORK_ERROR = 'NETWORK_ERROR',
}

// Enhanced error handling
async function processPaymentWithRetry(
  processor: PaymentProcessor,
  config: PaymentConfig,
  maxRetries: number = 3
): Promise<PaymentResult> {
  let lastError: Error | null = null;

  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await processor.processPayment(config);
    } catch (error) {
      lastError = error as Error;
      console.error(`Payment attempt ${attempt} failed:`, error);

      if (attempt < maxRetries) {
        const backoff = Math.pow(2, attempt) * 1000;
        console.log(`Retrying in ${backoff}ms...`);
        await new Promise(resolve => setTimeout(resolve, backoff));
      }
    }
  }

  throw new PaymentError(
    `Payment failed after ${maxRetries} attempts: ${lastError?.message}`,
    PaymentErrorCode.NETWORK_ERROR
  );
}

Performance Considerations

  • WebSocket over HTTP: Use WebSocket subscriptions for real-time updates instead of polling
  • Confirmation Strategy: Balance security (more confirmations) vs speed (fewer confirmations)
  • Connection Pooling: Reuse connections for multiple payments
  • Batch Verification: Check multiple transactions in a single getSignatureStatuses call

Cost Optimization

OperationCredits/CallFrequencyOptimization
onAccountChange10/secondContinuousSingle subscription per address
getTransaction10Per paymentCache verified transactions
getSignatureStatuses10PollingBatch multiple signatures
getSignaturesForAddress10Per changeLimit to recent signatures

Estimated cost per payment: 50-100 credits (depending on confirmation time)


NFT Marketplace Integration

Build a complete NFT marketplace that queries user collections, displays metadata, monitors listings, and updates in real-time.

Use Case

Create a marketplace dashboard showing user NFTs, available listings, and real-time sale notifications.

Architecture

graph TB
    A[User Wallet] --> B[Get Token Accounts]
    B --> C[Filter NFTs]
    C --> D[Fetch Metadata]
    D --> E[Display Collection]

    F[Marketplace Program] --> G[Get Program Accounts]
    G --> H[Parse Listings]
    H --> I[Display Marketplace]

    J[WebSocket] --> K[Account Updates]
    K --> L[Sale Events]
    L --> M[UI Updates]

    E --> N[Combined View]
    I --> N
    M --> N

Complete Implementation

import { Connection, PublicKey, AccountInfo } from '@solana/web3.js';
import { TOKEN_PROGRAM_ID } from '@solana/spl-token';
import axios from 'axios';

interface NFTMetadata {
  mint: string;
  name: string;
  symbol: string;
  uri: string;
  image?: string;
  attributes?: Array<{ trait_type: string; value: string }>;
}

interface MarketplaceListing {
  mint: string;
  seller: string;
  price: number;
  listingAccount: string;
}

class NFTMarketplace {
  private connection: Connection;
  private wsConnection: Connection;
  private marketplaceProgramId: PublicKey;

  constructor(rpcUrl: string, wsUrl: string, marketplaceProgramId: string) {
    this.connection = new Connection(rpcUrl, 'confirmed');
    this.wsConnection = new Connection(wsUrl, {
      commitment: 'confirmed',
      wsEndpoint: wsUrl,
    });
    this.marketplaceProgramId = new PublicKey(marketplaceProgramId);
  }

  /**
   * Get all NFTs owned by a wallet
   */
  async getUserNFTs(walletAddress: string): Promise<NFTMetadata[]> {
    const wallet = new PublicKey(walletAddress);

    console.log('Fetching token accounts...');
    const tokenAccounts = await this.connection.getTokenAccountsByOwner(
      wallet,
      { programId: TOKEN_PROGRAM_ID }
    );

    console.log(`Found ${tokenAccounts.value.length} token accounts`);

    // Filter for NFTs (amount = 1, decimals = 0)
    const nftMints: string[] = [];

    for (const { account } of tokenAccounts.value) {
      const data = account.data;
      // Parse token account data
      const amount = data.readBigUInt64LE(64);
      const decimals = data.readUInt8(44);

      if (amount === 1n && decimals === 0) {
        const mint = new PublicKey(data.slice(0, 32));
        nftMints.push(mint.toString());
      }
    }

    console.log(`Found ${nftMints.length} NFTs`);

    // Fetch metadata for all NFTs
    return await this.batchFetchMetadata(nftMints);
  }

  /**
   * Fetch metadata for multiple NFTs efficiently
   */
  private async batchFetchMetadata(mints: string[]): Promise<NFTMetadata[]> {
    const BATCH_SIZE = 10;
    const results: NFTMetadata[] = [];

    for (let i = 0; i < mints.length; i += BATCH_SIZE) {
      const batch = mints.slice(i, i + BATCH_SIZE);
      const batchResults = await Promise.allSettled(
        batch.map(mint => this.fetchNFTMetadata(mint))
      );

      for (const result of batchResults) {
        if (result.status === 'fulfilled' && result.value) {
          results.push(result.value);
        }
      }

      // Rate limiting
      if (i + BATCH_SIZE < mints.length) {
        await this.delay(100);
      }
    }

    return results;
  }

  /**
   * Fetch metadata for a single NFT using multiple methods
   */
  private async fetchNFTMetadata(mint: string): Promise<NFTMetadata | null> {
    try {
      // Method 1: Try Metaplex standard
      const metadata = await this.getMetaplexMetadata(mint);
      if (metadata) return metadata;

      // Method 2: Try direct URI from account data
      const directMetadata = await this.getDirectMetadata(mint);
      if (directMetadata) return directMetadata;

      // Method 3: Fallback to basic info
      return {
        mint,
        name: `NFT ${mint.slice(0, 8)}`,
        symbol: 'NFT',
        uri: '',
      };
    } catch (error) {
      console.error(`Error fetching metadata for ${mint}:`, error);
      return null;
    }
  }

  /**
   * Get metadata using Metaplex standard
   */
  private async getMetaplexMetadata(mint: string): Promise<NFTMetadata | null> {
    try {
      const METADATA_PROGRAM_ID = new PublicKey(
        'metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s'
      );

      // Derive metadata PDA
      const [metadataPDA] = PublicKey.findProgramAddressSync(
        [
          Buffer.from('metadata'),
          METADATA_PROGRAM_ID.toBuffer(),
          new PublicKey(mint).toBuffer(),
        ],
        METADATA_PROGRAM_ID
      );

      const accountInfo = await this.connection.getAccountInfo(metadataPDA);
      if (!accountInfo) return null;

      // Parse Metaplex metadata (simplified)
      const data = accountInfo.data;

      // Skip header and read name
      let offset = 1 + 32 + 32; // key + update authority + mint
      const nameLength = data.readUInt32LE(offset);
      offset += 4;
      const name = data.slice(offset, offset + nameLength).toString('utf8').replace(/\0/g, '');
      offset += nameLength;

      const symbolLength = data.readUInt32LE(offset);
      offset += 4;
      const symbol = data.slice(offset, offset + symbolLength).toString('utf8').replace(/\0/g, '');
      offset += symbolLength;

      const uriLength = data.readUInt32LE(offset);
      offset += 4;
      const uri = data.slice(offset, offset + uriLength).toString('utf8').replace(/\0/g, '');

      // Fetch off-chain metadata
      let image: string | undefined;
      let attributes: Array<{ trait_type: string; value: string }> | undefined;

      if (uri) {
        try {
          const response = await axios.get(uri, { timeout: 5000 });
          image = response.data.image;
          attributes = response.data.attributes;
        } catch (error) {
          console.warn(`Failed to fetch off-chain metadata from ${uri}`);
        }
      }

      return { mint, name, symbol, uri, image, attributes };
    } catch (error) {
      return null;
    }
  }

  /**
   * Get metadata directly from mint account
   */
  private async getDirectMetadata(mint: string): Promise<NFTMetadata | null> {
    try {
      const mintPubkey = new PublicKey(mint);
      const accountInfo = await this.connection.getAccountInfo(mintPubkey);

      if (!accountInfo) return null;

      // Basic mint info
      return {
        mint,
        name: `Token ${mint.slice(0, 8)}`,
        symbol: 'TOKEN',
        uri: '',
      };
    } catch (error) {
      return null;
    }
  }

  /**
   * Get all marketplace listings
   */
  async getAllListings(): Promise<MarketplaceListing[]> {
    console.log('Fetching marketplace listings...');

    const accounts = await this.connection.getProgramAccounts(
      this.marketplaceProgramId,
      {
        filters: [
          { dataSize: 120 }, // Adjust based on your listing account size
        ],
      }
    );

    console.log(`Found ${accounts.length} listing accounts`);

    const listings: MarketplaceListing[] = [];

    for (const { pubkey, account } of accounts) {
      try {
        const listing = this.parseListingAccount(account.data, pubkey.toString());
        if (listing) {
          listings.push(listing);
        }
      } catch (error) {
        console.error(`Error parsing listing ${pubkey.toString()}:`, error);
      }
    }

    return listings;
  }

  /**
   * Parse listing account data
   */
  private parseListingAccount(data: Buffer, pubkey: string): MarketplaceListing | null {
    try {
      // Adjust parsing based on your marketplace program structure
      // This is a generic example

      const mint = new PublicKey(data.slice(0, 32)).toString();
      const seller = new PublicKey(data.slice(32, 64)).toString();
      const price = Number(data.readBigUInt64LE(64)) / 1e9; // Convert lamports to SOL

      return {
        mint,
        seller,
        price,
        listingAccount: pubkey,
      };
    } catch (error) {
      return null;
    }
  }

  /**
   * Monitor marketplace sales in real-time
   */
  async monitorSales(callback: (sale: SaleEvent) => void): Promise<number> {
    const subscriptionId = this.wsConnection.onProgramAccountChange(
      this.marketplaceProgramId,
      async (keyedAccountInfo) => {
        try {
          const { accountId, accountInfo } = keyedAccountInfo;

          // Check if account was closed (sale completed)
          if (accountInfo.lamports === 0) {
            const sale: SaleEvent = {
              listingAccount: accountId.toString(),
              timestamp: Date.now(),
              type: 'sale_completed',
            };

            callback(sale);
          } else {
            // Check for new listing
            const listing = this.parseListingAccount(
              accountInfo.data,
              accountId.toString()
            );

            if (listing) {
              const sale: SaleEvent = {
                listingAccount: accountId.toString(),
                timestamp: Date.now(),
                type: 'new_listing',
                listing,
              };

              callback(sale);
            }
          }
        } catch (error) {
          console.error('Error processing sale event:', error);
        }
      },
      'confirmed'
    );

    console.log('Monitoring marketplace sales...');
    return subscriptionId;
  }

  /**
   * Get marketplace stats
   */
  async getMarketplaceStats(): Promise<MarketplaceStats> {
    const listings = await this.getAllListings();

    const totalListings = listings.length;
    const floorPrice = Math.min(...listings.map(l => l.price));
    const averagePrice = listings.reduce((sum, l) => sum + l.price, 0) / totalListings;
    const totalVolume = listings.reduce((sum, l) => sum + l.price, 0);

    return {
      totalListings,
      floorPrice,
      averagePrice,
      totalVolume,
    };
  }

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

interface SaleEvent {
  listingAccount: string;
  timestamp: number;
  type: 'sale_completed' | 'new_listing';
  listing?: MarketplaceListing;
}

interface MarketplaceStats {
  totalListings: number;
  floorPrice: number;
  averagePrice: number;
  totalVolume: number;
}

// Usage example
async function exampleNFTMarketplace() {
  const marketplace = new NFTMarketplace(
    'https://rpc.solana.com',
    'wss://rpc.solana.com',
    'YourMarketplaceProgramIdHere'
  );

  // Get user's NFTs
  const nfts = await marketplace.getUserNFTs('UserWalletAddressHere');
  console.log('User NFTs:', nfts);

  // Get all listings
  const listings = await marketplace.getAllListings();
  console.log('Marketplace listings:', listings);

  // Get stats
  const stats = await marketplace.getMarketplaceStats();
  console.log('Marketplace stats:', stats);

  // Monitor sales
  const subscriptionId = await marketplace.monitorSales((sale) => {
    console.log('Sale event:', sale);

    if (sale.type === 'sale_completed') {
      console.log('NFT sold!', sale.listingAccount);
    } else {
      console.log('New listing:', sale.listing);
    }
  });

  // Cleanup
  // marketplace.wsConnection.removeProgramAccountChangeListener(subscriptionId);
}

Performance Considerations

  • Batch Metadata Fetches: Process multiple NFTs concurrently with rate limiting
  • Cache Metadata: Store off-chain metadata locally to avoid repeated fetches
  • Filter Early: Use getTokenAccountsByOwner filters to reduce data transfer
  • Lazy Loading: Load NFT images progressively in UI

Cost Optimization

OperationCredits/CallOptimization
getTokenAccountsByOwner100Cache results, refresh periodically
getAccountInfo (per NFT)10Batch requests, use multicall
getProgramAccounts100Use filters to reduce data
onProgramAccountChange10/secondSingle subscription for all listings

Validator Monitoring Dashboard

Monitor validator performance, network topology, and create alerts for issues.

Use Case

Build a comprehensive dashboard for validator operators to track performance and network health.

Architecture

graph LR
    A[Dashboard] --> B[Vote Accounts]
    A --> C[Performance Samples]
    A --> D[Cluster Nodes]

    B --> E[Validator Stats]
    C --> E
    D --> E

    E --> F[Performance Trends]
    E --> G[Network Topology]
    E --> H[Alert System]

    H --> I[Email/SMS]
    H --> J[Webhook]

Complete Implementation

import { Connection, VoteAccountStatus, PerfSample, ContactInfo } from '@solana/web3.js';

interface ValidatorMetrics {
  identity: string;
  voteAccount: string;
  commission: number;
  activatedStake: number;
  lastVote: number;
  rootSlot: number;
  credits: number;
  epochCredits: number;
  skipRate: number;
  performance: number[];
}

interface AlertConfig {
  skipRateThreshold: number;
  performanceThreshold: number;
  delinquentAlert: boolean;
}

class ValidatorMonitor {
  private connection: Connection;
  private alertConfig: AlertConfig;
  private metrics: Map<string, ValidatorMetrics>;

  constructor(rpcUrl: string, alertConfig: AlertConfig) {
    this.connection = new Connection(rpcUrl, 'confirmed');
    this.alertConfig = alertConfig;
    this.metrics = new Map();
  }

  /**
   * Get complete validator information
   */
  async getValidatorInfo(voteAccount: string): Promise<ValidatorMetrics | null> {
    try {
      // Get vote accounts
      const voteAccounts = await this.connection.getVoteAccounts();

      // Find specific validator
      const validator = [...voteAccounts.current, ...voteAccounts.delinquent].find(
        v => v.votePubkey === voteAccount
      );

      if (!validator) {
        console.warn(`Validator ${voteAccount} not found`);
        return null;
      }

      // Get performance samples
      const perfSamples = await this.connection.getRecentPerformanceSamples(10);
      const avgPerformance = this.calculatePerformance(perfSamples);

      // Calculate skip rate
      const skipRate = this.calculateSkipRate(validator);

      const metrics: ValidatorMetrics = {
        identity: validator.nodePubkey,
        voteAccount: validator.votePubkey,
        commission: validator.commission,
        activatedStake: validator.activatedStake / 1e9, // Convert to SOL
        lastVote: validator.lastVote,
        rootSlot: validator.rootSlot,
        credits: validator.epochCredits.reduce((sum, [_, credits]) => sum + credits, 0),
        epochCredits: validator.epochCredits[0]?.[1] || 0,
        skipRate,
        performance: avgPerformance,
      };

      return metrics;
    } catch (error) {
      console.error('Error fetching validator info:', error);
      return null;
    }
  }

  /**
   * Get all validators with performance metrics
   */
  async getAllValidators(): Promise<ValidatorMetrics[]> {
    const voteAccounts = await this.connection.getVoteAccounts();
    const perfSamples = await this.connection.getRecentPerformanceSamples(10);
    const avgPerformance = this.calculatePerformance(perfSamples);

    const allValidators = [...voteAccounts.current, ...voteAccounts.delinquent];

    return allValidators.map(validator => ({
      identity: validator.nodePubkey,
      voteAccount: validator.votePubkey,
      commission: validator.commission,
      activatedStake: validator.activatedStake / 1e9,
      lastVote: validator.lastVote,
      rootSlot: validator.rootSlot,
      credits: validator.epochCredits.reduce((sum, [_, credits]) => sum + credits, 0),
      epochCredits: validator.epochCredits[0]?.[1] || 0,
      skipRate: this.calculateSkipRate(validator),
      performance: avgPerformance,
    }));
  }

  /**
   * Calculate skip rate for a validator
   */
  private calculateSkipRate(validator: any): number {
    const recentEpochs = validator.epochCredits.slice(0, 5);
    if (recentEpochs.length < 2) return 0;

    let totalSlots = 0;
    let totalCredits = 0;

    for (let i = 1; i < recentEpochs.length; i++) {
      const [epoch, credits, previousCredits] = recentEpochs[i];
      const [prevEpoch] = recentEpochs[i - 1];

      const creditsEarned = credits - previousCredits;
      const slotsInEpoch = 432000; // Approximate

      totalCredits += creditsEarned;
      totalSlots += slotsInEpoch;
    }

    const skipRate = ((totalSlots - totalCredits) / totalSlots) * 100;
    return Math.max(0, Math.min(100, skipRate));
  }

  /**
   * Calculate average performance from samples
   */
  private calculatePerformance(samples: PerfSample[]): number[] {
    return samples.map(sample => {
      const slotsPerSecond = sample.numSlots / sample.samplePeriodSecs;
      const transactionsPerSecond = sample.numTransactions / sample.samplePeriodSecs;
      return transactionsPerSecond;
    });
  }

  /**
   * Get network topology
   */
  async getNetworkTopology(): Promise<NetworkTopology> {
    const clusterNodes = await this.connection.getClusterNodes();

    const topology: NetworkTopology = {
      totalNodes: clusterNodes.length,
      nodesByVersion: {},
      nodesByRegion: {},
      nodes: clusterNodes.map(node => ({
        pubkey: node.pubkey,
        gossip: node.gossip,
        tpu: node.tpu,
        rpc: node.rpc || null,
        version: node.version || 'unknown',
      })),
    };

    // Group by version
    for (const node of clusterNodes) {
      const version = node.version || 'unknown';
      topology.nodesByVersion[version] = (topology.nodesByVersion[version] || 0) + 1;
    }

    return topology;
  }

  /**
   * Monitor validator and trigger alerts
   */
  async monitorValidator(
    voteAccount: string,
    callback: (alert: ValidatorAlert) => void
  ): Promise<void> {
    const checkInterval = 60000; // 1 minute

    const check = async () => {
      try {
        const metrics = await this.getValidatorInfo(voteAccount);

        if (!metrics) {
          callback({
            type: 'error',
            message: 'Failed to fetch validator metrics',
            timestamp: Date.now(),
            voteAccount,
          });
          return;
        }

        // Check skip rate
        if (metrics.skipRate > this.alertConfig.skipRateThreshold) {
          callback({
            type: 'skip_rate',
            message: `Skip rate ${metrics.skipRate.toFixed(2)}% exceeds threshold ${this.alertConfig.skipRateThreshold}%`,
            timestamp: Date.now(),
            voteAccount,
            metrics,
          });
        }

        // Check if delinquent
        const voteAccounts = await this.connection.getVoteAccounts();
        const isDelinquent = voteAccounts.delinquent.some(
          v => v.votePubkey === voteAccount
        );

        if (isDelinquent && this.alertConfig.delinquentAlert) {
          callback({
            type: 'delinquent',
            message: 'Validator is delinquent',
            timestamp: Date.now(),
            voteAccount,
            metrics,
          });
        }

        // Store metrics for trend analysis
        this.metrics.set(voteAccount, metrics);

      } catch (error) {
        console.error('Error monitoring validator:', error);
        callback({
          type: 'error',
          message: `Monitoring error: ${error}`,
          timestamp: Date.now(),
          voteAccount,
        });
      }
    };

    // Initial check
    await check();

    // Start monitoring
    setInterval(check, checkInterval);
  }

  /**
   * Generate performance report
   */
  async generateReport(voteAccount: string): Promise<ValidatorReport> {
    const metrics = await this.getValidatorInfo(voteAccount);
    if (!metrics) {
      throw new Error('Failed to fetch validator metrics');
    }

    const voteAccounts = await this.connection.getVoteAccounts();
    const allValidators = [...voteAccounts.current, ...voteAccounts.delinquent];

    // Calculate percentiles
    const skipRates = allValidators.map(v => this.calculateSkipRate(v));
    const stakes = allValidators.map(v => v.activatedStake);

    skipRates.sort((a, b) => a - b);
    stakes.sort((a, b) => a - b);

    const skipRatePercentile = this.getPercentile(skipRates, metrics.skipRate);
    const stakePercentile = this.getPercentile(stakes, metrics.activatedStake * 1e9);

    return {
      metrics,
      skipRatePercentile,
      stakePercentile,
      rank: this.calculateRank(metrics, allValidators),
      status: this.getValidatorStatus(metrics),
    };
  }

  private getPercentile(sortedArray: number[], value: number): number {
    const index = sortedArray.findIndex(v => v >= value);
    return (index / sortedArray.length) * 100;
  }

  private calculateRank(metrics: ValidatorMetrics, allValidators: any[]): number {
    const sorted = allValidators.sort((a, b) => b.activatedStake - a.activatedStake);
    return sorted.findIndex(v => v.votePubkey === metrics.voteAccount) + 1;
  }

  private getValidatorStatus(metrics: ValidatorMetrics): string {
    if (metrics.skipRate > 10) return 'poor';
    if (metrics.skipRate > 5) return 'fair';
    if (metrics.skipRate > 2) return 'good';
    return 'excellent';
  }
}

interface NetworkTopology {
  totalNodes: number;
  nodesByVersion: Record<string, number>;
  nodesByRegion: Record<string, number>;
  nodes: Array<{
    pubkey: string;
    gossip: string | null;
    tpu: string | null;
    rpc: string | null;
    version: string;
  }>;
}

interface ValidatorAlert {
  type: 'skip_rate' | 'delinquent' | 'performance' | 'error';
  message: string;
  timestamp: number;
  voteAccount: string;
  metrics?: ValidatorMetrics;
}

interface ValidatorReport {
  metrics: ValidatorMetrics;
  skipRatePercentile: number;
  stakePercentile: number;
  rank: number;
  status: string;
}

// Usage example
async function exampleValidatorMonitoring() {
  const monitor = new ValidatorMonitor(
    'https://rpc.solana.com',
    {
      skipRateThreshold: 5,
      performanceThreshold: 0.8,
      delinquentAlert: true,
    }
  );

  // Get validator info
  const info = await monitor.getValidatorInfo('VoteAccountAddressHere');
  console.log('Validator info:', info);

  // Get all validators
  const allValidators = await monitor.getAllValidators();
  console.log(`Total validators: ${allValidators.length}`);

  // Get network topology
  const topology = await monitor.getNetworkTopology();
  console.log('Network topology:', topology);

  // Monitor with alerts
  await monitor.monitorValidator('VoteAccountAddressHere', (alert) => {
    console.log('ALERT:', alert);

    // Send notification (email, webhook, etc.)
    if (alert.type === 'delinquent') {
      // Send urgent alert
      sendUrgentNotification(alert);
    }
  });

  // Generate report
  const report = await monitor.generateReport('VoteAccountAddressHere');
  console.log('Validator report:', report);
}

function sendUrgentNotification(alert: ValidatorAlert): void {
  // Implement notification logic
  console.log('URGENT:', alert.message);
}

Performance Considerations

  • Caching: Cache vote accounts and update every epoch
  • Batch Queries: Combine multiple validator queries
  • Historical Data: Store performance samples in database for trend analysis
  • Alert Throttling: Prevent alert spam with cooldown periods

Cost Optimization

OperationCredits/CallFrequencyOptimization
getVoteAccounts100Per epochCache for 1+ hours
getRecentPerformanceSamples10Every minuteBatch with other calls
getClusterNodes10HourlyCache topology data

DeFi Position Tracker

Track wallet positions across multiple DeFi protocols, calculate USD values, and monitor changes.

Use Case

Build a portfolio tracker that shows users their positions across lending platforms, DEXs, and yield farms.

Architecture

graph TB
    A[Wallet] --> B[Protocol 1]
    A --> C[Protocol 2]
    A --> D[Protocol 3]

    B --> E[Get Program Accounts]
    C --> E
    D --> E

    E --> F[Parse Positions]
    F --> G[Fetch Token Prices]
    G --> H[Calculate USD Values]

    H --> I[Portfolio Dashboard]

    J[WebSocket] --> K[Position Updates]
    K --> L[Rebalance Alerts]

Complete Implementation

import { Connection, PublicKey } from '@solana/web3.js';
import axios from 'axios';

interface Position {
  protocol: string;
  type: 'lending' | 'liquidity' | 'staking' | 'vault';
  asset: string;
  amount: number;
  usdValue: number;
  apy?: number;
  health?: number;
}

interface ProtocolConfig {
  name: string;
  programId: string;
  parser: (data: Buffer) => any;
}

class DeFiPositionTracker {
  private connection: Connection;
  private wsConnection: Connection;
  private protocols: ProtocolConfig[];
  private priceCache: Map<string, { price: number; timestamp: number }>;

  constructor(rpcUrl: string, wsUrl: string) {
    this.connection = new Connection(rpcUrl, 'confirmed');
    this.wsConnection = new Connection(wsUrl, {
      commitment: 'confirmed',
      wsEndpoint: wsUrl,
    });
    this.protocols = this.initializeProtocols();
    this.priceCache = new Map();
  }

  /**
   * Initialize supported protocols
   */
  private initializeProtocols(): ProtocolConfig[] {
    return [
      {
        name: 'Marinade',
        programId: 'MarBmsSgKXdrN1egZf5sqe1TMai9K1rChYNDJgjq7aD',
        parser: this.parseMarinadePosition,
      },
      {
        name: 'Orca',
        programId: 'whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc',
        parser: this.parseOrcaPosition,
      },
      // Add more protocols
    ];
  }

  /**
   * Get all positions for a wallet
   */
  async getAllPositions(walletAddress: string): Promise<Position[]> {
    const wallet = new PublicKey(walletAddress);
    const allPositions: Position[] = [];

    console.log('Fetching positions across all protocols...');

    for (const protocol of this.protocols) {
      try {
        const positions = await this.getProtocolPositions(wallet, protocol);
        allPositions.push(...positions);
      } catch (error) {
        console.error(`Error fetching ${protocol.name} positions:`, error);
      }
    }

    // Calculate USD values
    await this.enrichWithPrices(allPositions);

    return allPositions;
  }

  /**
   * Get positions for a specific protocol
   */
  private async getProtocolPositions(
    wallet: PublicKey,
    protocol: ProtocolConfig
  ): Promise<Position[]> {
    const programId = new PublicKey(protocol.programId);

    // Get all program accounts for this wallet
    const accounts = await this.connection.getProgramAccounts(programId, {
      filters: [
        {
          memcmp: {
            offset: 8, // Skip discriminator
            bytes: wallet.toBase58(),
          },
        },
      ],
    });

    console.log(`Found ${accounts.length} ${protocol.name} accounts`);

    const positions: Position[] = [];

    for (const { account } of accounts) {
      try {
        const parsed = protocol.parser(account.data);
        if (parsed) {
          positions.push({
            protocol: protocol.name,
            ...parsed,
            usdValue: 0, // Will be calculated later
          });
        }
      } catch (error) {
        console.error(`Error parsing ${protocol.name} account:`, error);
      }
    }

    return positions;
  }

  /**
   * Parse Marinade stake position
   */
  private parseMarinadePosition(data: Buffer): Partial<Position> | null {
    try {
      // Simplified parsing - adjust based on actual program structure
      const amount = Number(data.readBigUInt64LE(40)) / 1e9;

      return {
        type: 'staking',
        asset: 'mSOL',
        amount,
        apy: 7.5, // Would fetch from API
      };
    } catch (error) {
      return null;
    }
  }

  /**
   * Parse Orca liquidity position
   */
  private parseOrcaPosition(data: Buffer): Partial<Position> | null {
    try {
      // Simplified parsing
      const liquidity = Number(data.readBigUInt64LE(65));

      return {
        type: 'liquidity',
        asset: 'LP-Token',
        amount: liquidity / 1e9,
        apy: 15.2, // Would fetch from API
      };
    } catch (error) {
      return null;
    }
  }

  /**
   * Enrich positions with USD prices
   */
  private async enrichWithPrices(positions: Position[]): Promise<void> {
    const uniqueAssets = [...new Set(positions.map(p => p.asset))];

    // Fetch prices for all unique assets
    const prices = await this.fetchPrices(uniqueAssets);

    // Update positions with USD values
    for (const position of positions) {
      const price = prices.get(position.asset) || 0;
      position.usdValue = position.amount * price;
    }
  }

  /**
   * Fetch token prices from Jupiter or CoinGecko
   */
  private async fetchPrices(assets: string[]): Promise<Map<string, number>> {
    const prices = new Map<string, number>();
    const CACHE_DURATION = 60000; // 1 minute

    for (const asset of assets) {
      // Check cache
      const cached = this.priceCache.get(asset);
      if (cached && Date.now() - cached.timestamp < CACHE_DURATION) {
        prices.set(asset, cached.price);
        continue;
      }

      try {
        // Fetch from Jupiter Price API
        const response = await axios.get(
          `https://price.jup.ag/v4/price?ids=${asset}`,
          { timeout: 5000 }
        );

        const price = response.data.data[asset]?.price || 0;
        prices.set(asset, price);

        // Cache price
        this.priceCache.set(asset, { price, timestamp: Date.now() });
      } catch (error) {
        console.error(`Error fetching price for ${asset}:`, error);
        prices.set(asset, 0);
      }
    }

    return prices;
  }

  /**
   * Calculate portfolio summary
   */
  calculatePortfolioSummary(positions: Position[]): PortfolioSummary {
    const totalValue = positions.reduce((sum, p) => sum + p.usdValue, 0);

    const byProtocol: Record<string, number> = {};
    const byType: Record<string, number> = {};

    for (const position of positions) {
      byProtocol[position.protocol] = (byProtocol[position.protocol] || 0) + position.usdValue;
      byType[position.type] = (byType[position.type] || 0) + position.usdValue;
    }

    // Calculate weighted average APY
    const totalApy = positions
      .filter(p => p.apy)
      .reduce((sum, p) => sum + (p.apy! * p.usdValue), 0);
    const weightedApy = totalValue > 0 ? totalApy / totalValue : 0;

    return {
      totalValue,
      byProtocol,
      byType,
      weightedApy,
      positions: positions.length,
    };
  }

  /**
   * Monitor positions and trigger alerts
   */
  async monitorPositions(
    walletAddress: string,
    alertConfig: AlertConfig,
    callback: (alert: PositionAlert) => void
  ): Promise<void> {
    const wallet = new PublicKey(walletAddress);
    const subscriptions: number[] = [];

    for (const protocol of this.protocols) {
      const programId = new PublicKey(protocol.programId);

      const subscriptionId = this.wsConnection.onProgramAccountChange(
        programId,
        async (keyedAccountInfo) => {
          try {
            const { accountId, accountInfo } = keyedAccountInfo;

            // Check if account belongs to our wallet
            const accountData = accountInfo.data;
            // Simplified check - adjust based on program structure
            const ownerPubkey = new PublicKey(accountData.slice(8, 40));

            if (!ownerPubkey.equals(wallet)) return;

            // Parse position
            const parsed = protocol.parser(accountData);
            if (!parsed) return;

            const position: Position = {
              protocol: protocol.name,
              ...parsed,
              usdValue: 0,
            };

            // Enrich with price
            await this.enrichWithPrices([position]);

            // Check alerts
            this.checkAlerts(position, alertConfig, callback);

          } catch (error) {
            console.error('Error monitoring position:', error);
          }
        },
        'confirmed',
        [
          {
            memcmp: {
              offset: 8,
              bytes: wallet.toBase58(),
            },
          },
        ]
      );

      subscriptions.push(subscriptionId);
    }

    console.log(`Monitoring ${subscriptions.length} protocols for position changes...`);
  }

  /**
   * Check if position triggers any alerts
   */
  private checkAlerts(
    position: Position,
    config: AlertConfig,
    callback: (alert: PositionAlert) => void
  ): void {
    // Health factor alert (for lending positions)
    if (position.health && position.health < config.healthFactorThreshold) {
      callback({
        type: 'health_factor',
        message: `Health factor ${position.health.toFixed(2)} below threshold`,
        position,
        timestamp: Date.now(),
      });
    }

    // Large value change alert
    // Would need to track previous values for this

    // Rebalancing opportunity
    if (position.apy && position.apy > config.rebalanceApyThreshold) {
      callback({
        type: 'rebalance_opportunity',
        message: `High APY opportunity: ${position.apy.toFixed(2)}%`,
        position,
        timestamp: Date.now(),
      });
    }
  }

  /**
   * Export positions to CSV
   */
  exportToCSV(positions: Position[]): string {
    const headers = ['Protocol', 'Type', 'Asset', 'Amount', 'USD Value', 'APY', 'Health'];
    const rows = positions.map(p => [
      p.protocol,
      p.type,
      p.asset,
      p.amount.toFixed(6),
      p.usdValue.toFixed(2),
      p.apy?.toFixed(2) || '',
      p.health?.toFixed(2) || '',
    ]);

    return [headers, ...rows].map(row => row.join(',')).join('\n');
  }
}

interface PortfolioSummary {
  totalValue: number;
  byProtocol: Record<string, number>;
  byType: Record<string, number>;
  weightedApy: number;
  positions: number;
}

interface AlertConfig {
  healthFactorThreshold: number;
  rebalanceApyThreshold: number;
  valueChangeThreshold: number;
}

interface PositionAlert {
  type: 'health_factor' | 'value_change' | 'rebalance_opportunity';
  message: string;
  position: Position;
  timestamp: number;
}

// Usage example
async function exampleDeFiTracking() {
  const tracker = new DeFiPositionTracker(
    'https://rpc.solana.com',
    'wss://rpc.solana.com'
  );

  // Get all positions
  const positions = await tracker.getAllPositions('WalletAddressHere');
  console.log('All positions:', positions);

  // Calculate summary
  const summary = tracker.calculatePortfolioSummary(positions);
  console.log('Portfolio summary:', summary);
  console.log(`Total value: $${summary.totalValue.toFixed(2)}`);
  console.log(`Weighted APY: ${summary.weightedApy.toFixed(2)}%`);

  // Export to CSV
  const csv = tracker.exportToCSV(positions);
  console.log('CSV export:', csv);

  // Monitor positions
  await tracker.monitorPositions(
    'WalletAddressHere',
    {
      healthFactorThreshold: 1.5,
      rebalanceApyThreshold: 20,
      valueChangeThreshold: 1000,
    },
    (alert) => {
      console.log('POSITION ALERT:', alert);
    }
  );
}

Performance Considerations

  • Parallel Queries: Fetch positions from multiple protocols concurrently
  • Price Caching: Cache token prices to reduce API calls
  • Incremental Updates: Use WebSocket to update only changed positions
  • Database Storage: Store historical positions for trend analysis

Cost Optimization

OperationCredits/CallFrequencyOptimization
getProgramAccounts (per protocol)100On loadCache and use WebSocket for updates
Price API callsExternalPer unique assetCache for 1 minute
onProgramAccountChange10/secondContinuousSingle subscription per protocol

Transaction History Export

Efficiently export complete transaction history with pagination, parsing, and categorization.

Use Case

Export a wallet's complete transaction history for tax reporting, accounting, or analysis.

Architecture

graph LR
    A[Wallet Address] --> B[getSignaturesForAddress]
    B --> C[Paginate Results]
    C --> D[Batch getTransaction]
    D --> E[Parse & Categorize]
    E --> F[Export CSV/JSON]

    G[Progress Tracking] --> C
    H[Error Handling] --> D
    I[Rate Limiting] --> D

Complete Implementation

import { Connection, PublicKey, ParsedTransactionWithMeta } from '@solana/web3.js';
import * as fs from 'fs';

interface TransactionRecord {
  signature: string;
  timestamp: number;
  type: string;
  from: string;
  to: string;
  amount: number;
  fee: number;
  status: 'success' | 'failed';
  memo?: string;
}

interface ExportOptions {
  startTime?: number;
  endTime?: number;
  format: 'csv' | 'json';
  outputPath: string;
  batchSize: number;
  includeFailedTx: boolean;
}

class TransactionHistoryExporter {
  private connection: Connection;

  constructor(rpcUrl: string) {
    this.connection = new Connection(rpcUrl, 'confirmed');
  }

  /**
   * Export complete transaction history
   */
  async exportHistory(
    walletAddress: string,
    options: ExportOptions
  ): Promise<ExportResult> {
    const wallet = new PublicKey(walletAddress);
    const startTime = Date.now();

    console.log('Starting transaction history export...');
    console.log('Options:', options);

    // Step 1: Get all signatures with pagination
    const signatures = await this.getAllSignatures(wallet, options);
    console.log(`Found ${signatures.length} transactions`);

    // Step 2: Fetch transaction details in batches
    const transactions = await this.batchFetchTransactions(signatures, options.batchSize);
    console.log(`Fetched ${transactions.length} transaction details`);

    // Step 3: Parse and categorize transactions
    const records = this.parseTransactions(transactions, walletAddress);
    console.log(`Parsed ${records.length} transaction records`);

    // Step 4: Filter by time range
    const filtered = this.filterByTimeRange(records, options);
    console.log(`After filtering: ${filtered.length} records`);

    // Step 5: Export to file
    await this.exportToFile(filtered, options);

    const duration = Date.now() - startTime;

    return {
      totalTransactions: filtered.length,
      successfulTx: filtered.filter(r => r.status === 'success').length,
      failedTx: filtered.filter(r => r.status === 'failed').length,
      totalFees: filtered.reduce((sum, r) => sum + r.fee, 0),
      duration,
      outputPath: options.outputPath,
    };
  }

  /**
   * Get all signatures with pagination
   */
  private async getAllSignatures(
    wallet: PublicKey,
    options: ExportOptions
  ): Promise<string[]> {
    const allSignatures: string[] = [];
    let before: string | undefined;
    let hasMore = true;

    while (hasMore) {
      try {
        const signatures = await this.connection.getSignaturesForAddress(
          wallet,
          { before, limit: 1000 },
          'confirmed'
        );

        if (signatures.length === 0) {
          hasMore = false;
          break;
        }

        // Filter by time if specified
        for (const sig of signatures) {
          if (options.startTime && sig.blockTime && sig.blockTime < options.startTime) {
            hasMore = false;
            break;
          }

          // Skip failed transactions if not requested
          if (!options.includeFailedTx && sig.err) {
            continue;
          }

          allSignatures.push(sig.signature);
        }

        before = signatures[signatures.length - 1].signature;

        console.log(`Fetched ${allSignatures.length} signatures so far...`);

        // Rate limiting
        await this.delay(100);

      } catch (error) {
        console.error('Error fetching signatures:', error);
        break;
      }
    }

    return allSignatures;
  }

  /**
   * Batch fetch transaction details
   */
  private async batchFetchTransactions(
    signatures: string[],
    batchSize: number
  ): Promise<(ParsedTransactionWithMeta | null)[]> {
    const transactions: (ParsedTransactionWithMeta | null)[] = [];

    for (let i = 0; i < signatures.length; i += batchSize) {
      const batch = signatures.slice(i, i + batchSize);

      console.log(`Fetching batch ${Math.floor(i / batchSize) + 1}/${Math.ceil(signatures.length / batchSize)}`);

      try {
        // Fetch transactions concurrently within batch
        const batchPromises = batch.map(sig =>
          this.connection.getParsedTransaction(sig, {
            maxSupportedTransactionVersion: 0,
            commitment: 'confirmed',
          })
        );

        const batchResults = await Promise.allSettled(batchPromises);

        for (const result of batchResults) {
          if (result.status === 'fulfilled') {
            transactions.push(result.value);
          } else {
            console.error('Failed to fetch transaction:', result.reason);
            transactions.push(null);
          }
        }

        // Rate limiting between batches
        if (i + batchSize < signatures.length) {
          await this.delay(500);
        }

      } catch (error) {
        console.error('Error in batch fetch:', error);
      }
    }

    return transactions;
  }

  /**
   * Parse transactions into structured records
   */
  private parseTransactions(
    transactions: (ParsedTransactionWithMeta | null)[],
    walletAddress: string
  ): TransactionRecord[] {
    const records: TransactionRecord[] = [];

    for (const tx of transactions) {
      if (!tx) continue;

      try {
        const record = this.parseTransaction(tx, walletAddress);
        if (record) {
          records.push(record);
        }
      } catch (error) {
        console.error('Error parsing transaction:', error);
      }
    }

    return records;
  }

  /**
   * Parse a single transaction
   */
  private parseTransaction(
    tx: ParsedTransactionWithMeta,
    walletAddress: string
  ): TransactionRecord | null {
    const signature = tx.transaction.signatures[0];
    const timestamp = tx.blockTime || 0;
    const status = tx.meta?.err ? 'failed' : 'success';
    const fee = (tx.meta?.fee || 0) / 1e9;

    // Determine transaction type
    let type = 'unknown';
    let from = '';
    let to = '';
    let amount = 0;
    let memo: string | undefined;

    // Check for SPL token transfers
    if (tx.meta?.postTokenBalances && tx.meta?.preTokenBalances) {
      const postBalances = tx.meta.postTokenBalances;
      const preBalances = tx.meta.preTokenBalances;

      for (const postBalance of postBalances) {
        const preBalance = preBalances.find(
          pb => pb.accountIndex === postBalance.accountIndex
        );

        if (preBalance && postBalance.uiTokenAmount.uiAmount !== preBalance.uiTokenAmount.uiAmount) {
          type = 'token_transfer';
          amount = Math.abs(
            (postBalance.uiTokenAmount.uiAmount || 0) - (preBalance.uiTokenAmount.uiAmount || 0)
          );
          break;
        }
      }
    }

    // Check for SOL transfers
    if (type === 'unknown' && tx.meta?.postBalances && tx.meta?.preBalances) {
      const accountKeys = tx.transaction.message.accountKeys;

      for (let i = 0; i < accountKeys.length; i++) {
        const preBalance = tx.meta.preBalances[i];
        const postBalance = tx.meta.postBalances[i];
        const change = (postBalance - preBalance) / 1e9;

        if (Math.abs(change) > 0.000001) {
          const account = accountKeys[i].pubkey.toString();

          if (change < 0) {
            from = account;
            amount = Math.abs(change);
          } else if (change > 0 && account !== walletAddress) {
            to = account;
          }

          type = 'sol_transfer';
        }
      }
    }

    // Check for program interactions
    if (type === 'unknown') {
      const instructions = tx.transaction.message.instructions;
      if (instructions.length > 0) {
        // Simplified - would need more sophisticated parsing
        type = 'program_interaction';
      }
    }

    // Extract memo if present
    const logMessages = tx.meta?.logMessages || [];
    for (const log of logMessages) {
      if (log.includes('Memo')) {
        memo = log;
        break;
      }
    }

    return {
      signature,
      timestamp,
      type,
      from,
      to,
      amount,
      fee,
      status: status as 'success' | 'failed',
      memo,
    };
  }

  /**
   * Filter records by time range
   */
  private filterByTimeRange(
    records: TransactionRecord[],
    options: ExportOptions
  ): TransactionRecord[] {
    return records.filter(record => {
      if (options.startTime && record.timestamp < options.startTime) {
        return false;
      }
      if (options.endTime && record.timestamp > options.endTime) {
        return false;
      }
      return true;
    });
  }

  /**
   * Export records to file
   */
  private async exportToFile(
    records: TransactionRecord[],
    options: ExportOptions
  ): Promise<void> {
    if (options.format === 'csv') {
      await this.exportToCSV(records, options.outputPath);
    } else {
      await this.exportToJSON(records, options.outputPath);
    }

    console.log(`Exported to ${options.outputPath}`);
  }

  /**
   * Export to CSV format
   */
  private async exportToCSV(records: TransactionRecord[], outputPath: string): Promise<void> {
    const headers = ['Signature', 'Date', 'Type', 'From', 'To', 'Amount', 'Fee', 'Status', 'Memo'];

    const rows = records.map(record => [
      record.signature,
      new Date(record.timestamp * 1000).toISOString(),
      record.type,
      record.from,
      record.to,
      record.amount.toFixed(9),
      record.fee.toFixed(9),
      record.status,
      record.memo || '',
    ]);

    const csv = [headers, ...rows]
      .map(row => row.map(cell => `"${cell}"`).join(','))
      .join('\n');

    fs.writeFileSync(outputPath, csv);
  }

  /**
   * Export to JSON format
   */
  private async exportToJSON(records: TransactionRecord[], outputPath: string): Promise<void> {
    const json = JSON.stringify(records, null, 2);
    fs.writeFileSync(outputPath, json);
  }

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

interface ExportResult {
  totalTransactions: number;
  successfulTx: number;
  failedTx: number;
  totalFees: number;
  duration: number;
  outputPath: string;
}

// Usage example
async function exampleExport() {
  const exporter = new TransactionHistoryExporter('https://rpc.solana.com');

  try {
    const result = await exporter.exportHistory('WalletAddressHere', {
      format: 'csv',
      outputPath: '/tmp/transaction_history.csv',
      batchSize: 10,
      includeFailedTx: true,
      // Optional time range (Unix timestamps)
      // startTime: 1640000000,
      // endTime: 1672535999,
    });

    console.log('Export complete!');
    console.log(`Total transactions: ${result.totalTransactions}`);
    console.log(`Successful: ${result.successfulTx}`);
    console.log(`Failed: ${result.failedTx}`);
    console.log(`Total fees: ${result.totalFees.toFixed(9)} SOL`);
    console.log(`Duration: ${result.duration}ms`);
    console.log(`Output: ${result.outputPath}`);
  } catch (error) {
    console.error('Export failed:', error);
  }
}

Performance Considerations

  • Pagination Strategy: Fetch 1000 signatures per call (maximum)
  • Batch Size: Balance between speed and rate limits (10-20 concurrent requests)
  • Progress Tracking: Show progress for long-running exports
  • Memory Management: Stream to file for very large exports

Cost Optimization

OperationCredits/CallTotal CallsOptimization
getSignaturesForAddress10N/1000Minimize by caching known signatures
getParsedTransaction10NBatch requests, handle failures gracefully

Example cost for 10,000 transactions: ~100,100 credits (100 for signatures + 100,000 for transactions)


High-Frequency Trading Setup

Optimize for speed and reliability in high-frequency trading scenarios.

Use Case

Build a trading bot that executes trades based on market conditions with minimal latency.

Architecture

graph TB
    A[Market Data] --> B[WebSocket Feeds]
    B --> C[Price Analysis]
    C --> D[Trading Decision]

    D --> E[Connection Pool]
    E --> F[Send Transaction]

    G[Slot Updates] --> H[Timing Optimization]
    H --> F

    F --> I[Confirmation Monitor]
    I --> J[Success/Retry]

    K[Fallback RPC] --> E

Complete Implementation

import {
  Connection,
  Keypair,
  Transaction,
  PublicKey,
  TransactionInstruction,
  sendAndConfirmTransaction,
} from '@solana/web3.js';

interface ConnectionConfig {
  primary: string;
  fallbacks: string[];
  wsUrl: string;
  maxConnectionsPerEndpoint: number;
}

interface TradingConfig {
  maxSlippage: number;
  priorityFee: number;
  confirmationTimeout: number;
  retryAttempts: number;
}

class HighFrequencyTrader {
  private primaryPool: Connection[];
  private fallbackPools: Map<string, Connection[]>;
  private wsConnection: Connection;
  private currentSlot: number = 0;
  private config: TradingConfig;

  constructor(connectionConfig: ConnectionConfig, tradingConfig: TradingConfig) {
    this.config = tradingConfig;

    // Initialize connection pools
    this.primaryPool = this.createConnectionPool(
      connectionConfig.primary,
      connectionConfig.maxConnectionsPerEndpoint
    );

    this.fallbackPools = new Map();
    for (const fallback of connectionConfig.fallbacks) {
      this.fallbackPools.set(
        fallback,
        this.createConnectionPool(fallback, connectionConfig.maxConnectionsPerEndpoint)
      );
    }

    // WebSocket connection for real-time updates
    this.wsConnection = new Connection(connectionConfig.wsUrl, {
      commitment: 'confirmed',
      wsEndpoint: connectionConfig.wsUrl,
    });

    this.initializeSlotTracking();
  }

  /**
   * Create a pool of connections to the same endpoint
   */
  private createConnectionPool(url: string, size: number): Connection[] {
    return Array(size)
      .fill(null)
      .map(() => new Connection(url, {
        commitment: 'confirmed',
        confirmTransactionInitialTimeout: this.config.confirmationTimeout,
      }));
  }

  /**
   * Get a connection from the pool (round-robin)
   */
  private getConnection(preferFallback: boolean = false): Connection {
    if (preferFallback && this.fallbackPools.size > 0) {
      const [url, pool] = Array.from(this.fallbackPools.entries())[0];
      const index = Math.floor(Math.random() * pool.length);
      return pool[index];
    }

    const index = Math.floor(Math.random() * this.primaryPool.length);
    return this.primaryPool[index];
  }

  /**
   * Initialize slot tracking for timing optimization
   */
  private initializeSlotTracking(): void {
    this.wsConnection.onSlotChange((slotInfo) => {
      this.currentSlot = slotInfo.slot;
    });
  }

  /**
   * Execute trade with optimized routing
   */
  async executeTrade(
    instruction: TransactionInstruction,
    payer: Keypair
  ): Promise<TradeResult> {
    const startTime = Date.now();
    let attempts = 0;
    let lastError: Error | null = null;

    while (attempts < this.config.retryAttempts) {
      attempts++;

      try {
        // Get fresh blockhash
        const connection = this.getConnection();
        const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash('confirmed');

        // Build transaction
        const transaction = new Transaction({
          feePayer: payer.publicKey,
          blockhash,
          lastValidBlockHeight,
        });

        // Add priority fee
        if (this.config.priorityFee > 0) {
          transaction.add(
            // Priority fee instruction would go here
            // This is program-specific
          );
        }

        transaction.add(instruction);

        // Sign transaction
        transaction.sign(payer);

        // Send transaction using multiple strategies
        const signature = await this.sendTransactionOptimized(transaction);

        // Monitor confirmation
        const confirmed = await this.waitForConfirmation(signature, lastValidBlockHeight);

        if (confirmed) {
          const duration = Date.now() - startTime;

          return {
            success: true,
            signature,
            slot: this.currentSlot,
            duration,
            attempts,
          };
        }

        lastError = new Error('Transaction not confirmed');

      } catch (error) {
        lastError = error as Error;
        console.error(`Trade attempt ${attempts} failed:`, error);

        // Wait before retry with exponential backoff
        if (attempts < this.config.retryAttempts) {
          const backoff = Math.min(1000 * Math.pow(2, attempts), 5000);
          await this.delay(backoff);
        }
      }
    }

    const duration = Date.now() - startTime;

    return {
      success: false,
      error: lastError?.message || 'Unknown error',
      duration,
      attempts,
    };
  }

  /**
   * Send transaction using optimized strategies
   */
  private async sendTransactionOptimized(transaction: Transaction): Promise<string> {
    const serialized = transaction.serialize();

    // Strategy 1: Send to multiple endpoints simultaneously
    const sendPromises = [
      this.getConnection().sendRawTransaction(serialized, {
        skipPreflight: true,
        maxRetries: 0,
      }),
    ];

    // Add fallback sends
    for (const [_, pool] of this.fallbackPools) {
      sendPromises.push(
        pool[0].sendRawTransaction(serialized, {
          skipPreflight: true,
          maxRetries: 0,
        })
      );
    }

    // Race all sends, use first successful
    const results = await Promise.allSettled(sendPromises);

    for (const result of results) {
      if (result.status === 'fulfilled') {
        return result.value;
      }
    }

    throw new Error('All transaction sends failed');
  }

  /**
   * Wait for transaction confirmation with timeout
   */
  private async waitForConfirmation(
    signature: string,
    lastValidBlockHeight: number
  ): Promise<boolean> {
    const connection = this.getConnection();
    const startTime = Date.now();

    while (Date.now() - startTime < this.config.confirmationTimeout) {
      try {
        const status = await connection.getSignatureStatus(signature);

        if (status.value?.confirmationStatus === 'confirmed' ||
            status.value?.confirmationStatus === 'finalized') {
          return true;
        }

        if (status.value?.err) {
          console.error('Transaction failed:', status.value.err);
          return false;
        }

        // Check if blockhash expired
        const currentBlockHeight = await connection.getBlockHeight();
        if (currentBlockHeight > lastValidBlockHeight) {
          console.warn('Transaction expired');
          return false;
        }

        await this.delay(400);

      } catch (error) {
        console.error('Error checking confirmation:', error);
        await this.delay(400);
      }
    }

    return false;
  }

  /**
   * Batch multiple trades for efficiency
   */
  async executeBatchTrades(
    instructions: TransactionInstruction[],
    payer: Keypair
  ): Promise<BatchTradeResult> {
    const connection = this.getConnection();
    const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash('confirmed');

    // Split into multiple transactions if needed (max ~10 instructions per tx)
    const INSTRUCTIONS_PER_TX = 8;
    const transactions: Transaction[] = [];

    for (let i = 0; i < instructions.length; i += INSTRUCTIONS_PER_TX) {
      const batch = instructions.slice(i, i + INSTRUCTIONS_PER_TX);

      const transaction = new Transaction({
        feePayer: payer.publicKey,
        blockhash,
        lastValidBlockHeight,
      });

      batch.forEach(ix => transaction.add(ix));
      transaction.sign(payer);

      transactions.push(transaction);
    }

    // Send all transactions
    const results = await Promise.allSettled(
      transactions.map(tx => this.sendTransactionOptimized(tx))
    );

    const successful: string[] = [];
    const failed: string[] = [];

    for (const result of results) {
      if (result.status === 'fulfilled') {
        successful.push(result.value);
      } else {
        failed.push(result.reason.message);
      }
    }

    return {
      total: transactions.length,
      successful: successful.length,
      failed: failed.length,
      signatures: successful,
    };
  }

  /**
   * Monitor market data in real-time
   */
  subscribeToMarketData(
    account: PublicKey,
    callback: (data: MarketData) => void
  ): number {
    return this.wsConnection.onAccountChange(
      account,
      (accountInfo, context) => {
        const data: MarketData = {
          slot: context.slot,
          timestamp: Date.now(),
          data: accountInfo.data,
          lamports: accountInfo.lamports,
        };

        callback(data);
      },
      'confirmed'
    );
  }

  /**
   * Health check for all connections
   */
  async healthCheck(): Promise<HealthStatus> {
    const checks: Promise<boolean>[] = [];

    // Check primary pool
    for (const conn of this.primaryPool) {
      checks.push(
        conn.getSlot()
          .then(() => true)
          .catch(() => false)
      );
    }

    // Check fallback pools
    for (const [_, pool] of this.fallbackPools) {
      for (const conn of pool) {
        checks.push(
          conn.getSlot()
            .then(() => true)
            .catch(() => false)
        );
      }
    }

    const results = await Promise.all(checks);
    const healthy = results.filter(r => r).length;
    const total = results.length;

    return {
      healthy,
      total,
      healthPercentage: (healthy / total) * 100,
    };
  }

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

interface TradeResult {
  success: boolean;
  signature?: string;
  slot?: number;
  duration: number;
  attempts: number;
  error?: string;
}

interface BatchTradeResult {
  total: number;
  successful: number;
  failed: number;
  signatures: string[];
}

interface MarketData {
  slot: number;
  timestamp: number;
  data: Buffer;
  lamports: number;
}

interface HealthStatus {
  healthy: number;
  total: number;
  healthPercentage: number;
}

// Usage example
async function exampleHFT() {
  const trader = new HighFrequencyTrader(
    {
      primary: 'https://rpc.solana.com',
      fallbacks: [
        'https://api.mainnet-beta.solana.com',
        'https://solana-api.projectserum.com',
      ],
      wsUrl: 'wss://rpc.solana.com',
      maxConnectionsPerEndpoint: 5,
    },
    {
      maxSlippage: 0.01,
      priorityFee: 10000,
      confirmationTimeout: 30000,
      retryAttempts: 3,
    }
  );

  // Health check
  const health = await trader.healthCheck();
  console.log(`Connection health: ${health.healthPercentage.toFixed(1)}%`);

  // Subscribe to market data
  const marketAccount = new PublicKey('MarketAccountAddressHere');
  trader.subscribeToMarketData(marketAccount, (data) => {
    console.log(`Market update at slot ${data.slot}`);

    // Analyze and potentially execute trade
    // if (shouldTrade(data)) {
    //   trader.executeTrade(tradeInstruction, payer);
    // }
  });
}

Performance Considerations

  • Connection Pooling: Multiple connections to same endpoint for parallel requests
  • Skip Preflight: Disable preflight checks for faster submission
  • Priority Fees: Use priority fees to increase confirmation chances
  • Slot Tracking: Time transactions optimally using slot updates
  • Failover: Automatically switch to backup RPCs on failure

Cost Optimization

StrategyCredits SavedTrade-off
Connection pooling0 (same calls)More infrastructure
Skip preflight10 per txHigher failure risk
Batch transactions50-80%Atomic execution
WebSocket for data90% vs pollingReal-time only

Smart Caching Strategy

Implement intelligent caching to reduce costs and improve performance.

Use Case

Build a caching layer that optimally stores Solana data with appropriate invalidation strategies.

Architecture

graph TB
    A[Request] --> B{Cache Hit?}
    B -->|Yes| C[Return Cached]
    B -->|No| D[Fetch from RPC]
    D --> E[Store in Cache]
    E --> C

    F[WebSocket Update] --> G[Invalidate Cache]
    G --> H[Update Cache]

    I[TTL Expiration] --> G
    J[Commitment Level] --> K[Cache Strategy]

Complete Implementation

import { Connection, PublicKey, AccountInfo, Commitment } from '@solana/web3.js';
import Redis from 'ioredis';

interface CacheConfig {
  ttl: {
    accounts: number;
    blocks: number;
    transactions: number;
    programAccounts: number;
  };
  redis?: {
    host: string;
    port: number;
  };
}

class SmartCache {
  private connection: Connection;
  private wsConnection: Connection;
  private memoryCache: Map<string, CacheEntry>;
  private redis?: Redis;
  private config: CacheConfig;
  private subscriptions: Map<string, number>;

  constructor(rpcUrl: string, wsUrl: string, config: CacheConfig) {
    this.connection = new Connection(rpcUrl, 'confirmed');
    this.wsConnection = new Connection(wsUrl, {
      commitment: 'confirmed',
      wsEndpoint: wsUrl,
    });
    this.memoryCache = new Map();
    this.config = config;
    this.subscriptions = new Map();

    // Initialize Redis if configured
    if (config.redis) {
      this.redis = new Redis({
        host: config.redis.host,
        port: config.redis.port,
      });
    }

    // Start cache cleanup
    this.startCleanup();
  }

  /**
   * Get account with caching
   */
  async getAccountInfo(
    pubkey: PublicKey,
    commitment: Commitment = 'confirmed'
  ): Promise<AccountInfo<Buffer> | null> {
    const key = `account:${pubkey.toString()}:${commitment}`;

    // Check cache
    const cached = await this.get<AccountInfo<Buffer>>(key);
    if (cached) {
      console.log(`Cache hit: ${key}`);
      return cached;
    }

    console.log(`Cache miss: ${key}`);

    // Fetch from RPC
    const accountInfo = await this.connection.getAccountInfo(pubkey, commitment);

    // Cache result
    await this.set(key, accountInfo, this.config.ttl.accounts);

    // Subscribe to updates if not already subscribed
    if (commitment === 'confirmed' && !this.subscriptions.has(pubkey.toString())) {
      this.subscribeToAccount(pubkey);
    }

    return accountInfo;
  }

  /**
   * Get multiple accounts with batch caching
   */
  async getMultipleAccountsInfo(
    pubkeys: PublicKey[],
    commitment: Commitment = 'confirmed'
  ): Promise<(AccountInfo<Buffer> | null)[]> {
    const keys = pubkeys.map(pk => `account:${pk.toString()}:${commitment}`);

    // Check cache for all keys
    const cacheResults = await Promise.all(
      keys.map(key => this.get<AccountInfo<Buffer>>(key))
    );

    // Identify missing accounts
    const missingIndices: number[] = [];
    const missingPubkeys: PublicKey[] = [];

    cacheResults.forEach((result, i) => {
      if (result === null) {
        missingIndices.push(i);
        missingPubkeys.push(pubkeys[i]);
      }
    });

    // Fetch missing accounts
    if (missingPubkeys.length > 0) {
      console.log(`Cache miss for ${missingPubkeys.length} accounts`);

      const fetched = await this.connection.getMultipleAccountsInfo(
        missingPubkeys,
        commitment
      );

      // Cache fetched accounts
      for (let i = 0; i < fetched.length; i++) {
        const originalIndex = missingIndices[i];
        cacheResults[originalIndex] = fetched[i];

        await this.set(
          keys[originalIndex],
          fetched[i],
          this.config.ttl.accounts
        );
      }
    } else {
      console.log(`Cache hit for all ${pubkeys.length} accounts`);
    }

    return cacheResults;
  }

  /**
   * Get transaction with caching
   */
  async getTransaction(
    signature: string,
    commitment: Commitment = 'confirmed'
  ): Promise<any> {
    const key = `transaction:${signature}:${commitment}`;

    // Transactions are immutable once confirmed, cache indefinitely
    const cached = await this.get(key);
    if (cached) {
      console.log(`Cache hit: ${key}`);
      return cached;
    }

    console.log(`Cache miss: ${key}`);

    const transaction = await this.connection.getTransaction(signature, {
      maxSupportedTransactionVersion: 0,
      commitment,
    });

    if (transaction) {
      // Cache indefinitely for finalized transactions
      const ttl = commitment === 'finalized'
        ? this.config.ttl.transactions * 10
        : this.config.ttl.transactions;

      await this.set(key, transaction, ttl);
    }

    return transaction;
  }

  /**
   * Get program accounts with smart caching
   */
  async getProgramAccounts(
    programId: PublicKey,
    filters?: any[]
  ): Promise<any[]> {
    // Create cache key from filters
    const filterKey = filters ? JSON.stringify(filters) : 'all';
    const key = `program:${programId.toString()}:${filterKey}`;

    const cached = await this.get<any[]>(key);
    if (cached) {
      console.log(`Cache hit: ${key}`);
      return cached;
    }

    console.log(`Cache miss: ${key}`);

    const accounts = await this.connection.getProgramAccounts(programId, {
      filters,
    });

    // Shorter TTL for program accounts as they change frequently
    await this.set(key, accounts, this.config.ttl.programAccounts);

    return accounts;
  }

  /**
   * Subscribe to account updates and invalidate cache
   */
  private subscribeToAccount(pubkey: PublicKey): void {
    const subscriptionId = this.wsConnection.onAccountChange(
      pubkey,
      async (accountInfo, context) => {
        console.log(`Account updated: ${pubkey.toString()} at slot ${context.slot}`);

        // Invalidate all commitment levels for this account
        const commitments: Commitment[] = ['processed', 'confirmed', 'finalized'];

        for (const commitment of commitments) {
          const key = `account:${pubkey.toString()}:${commitment}`;
          await this.delete(key);

          // Update cache with new data for confirmed
          if (commitment === 'confirmed') {
            await this.set(key, accountInfo, this.config.ttl.accounts);
          }
        }
      },
      'confirmed'
    );

    this.subscriptions.set(pubkey.toString(), subscriptionId);
  }

  /**
   * Get from cache (memory first, then Redis)
   */
  private async get<T>(key: string): Promise<T | null> {
    // Check memory cache first
    const memCached = this.memoryCache.get(key);
    if (memCached && memCached.expiresAt > Date.now()) {
      return memCached.value as T;
    }

    // Check Redis if available
    if (this.redis) {
      try {
        const cached = await this.redis.get(key);
        if (cached) {
          const parsed = JSON.parse(cached);

          // Update memory cache
          this.memoryCache.set(key, {
            value: parsed,
            expiresAt: Date.now() + this.config.ttl.accounts * 1000,
          });

          return parsed as T;
        }
      } catch (error) {
        console.error('Redis get error:', error);
      }
    }

    return null;
  }

  /**
   * Set in cache (memory and Redis)
   */
  private async set(key: string, value: any, ttlSeconds: number): Promise<void> {
    const expiresAt = Date.now() + ttlSeconds * 1000;

    // Set in memory cache
    this.memoryCache.set(key, { value, expiresAt });

    // Set in Redis if available
    if (this.redis) {
      try {
        await this.redis.setex(key, ttlSeconds, JSON.stringify(value));
      } catch (error) {
        console.error('Redis set error:', error);
      }
    }
  }

  /**
   * Delete from cache
   */
  private async delete(key: string): Promise<void> {
    this.memoryCache.delete(key);

    if (this.redis) {
      try {
        await this.redis.del(key);
      } catch (error) {
        console.error('Redis delete error:', error);
      }
    }
  }

  /**
   * Start periodic cache cleanup
   */
  private startCleanup(): void {
    setInterval(() => {
      const now = Date.now();

      for (const [key, entry] of this.memoryCache.entries()) {
        if (entry.expiresAt <= now) {
          this.memoryCache.delete(key);
        }
      }

      console.log(`Cache cleanup: ${this.memoryCache.size} entries remaining`);
    }, 60000); // Every minute
  }

  /**
   * Get cache statistics
   */
  getStats(): CacheStats {
    let totalEntries = this.memoryCache.size;
    let expiredEntries = 0;
    const now = Date.now();

    for (const [_, entry] of this.memoryCache.entries()) {
      if (entry.expiresAt <= now) {
        expiredEntries++;
      }
    }

    return {
      totalEntries,
      activeEntries: totalEntries - expiredEntries,
      expiredEntries,
      subscriptions: this.subscriptions.size,
    };
  }

  /**
   * Clear all cache
   */
  async clearAll(): Promise<void> {
    this.memoryCache.clear();

    if (this.redis) {
      await this.redis.flushdb();
    }

    console.log('Cache cleared');
  }
}

interface CacheEntry {
  value: any;
  expiresAt: number;
}

interface CacheStats {
  totalEntries: number;
  activeEntries: number;
  expiredEntries: number;
  subscriptions: number;
}

// Usage example
async function exampleCaching() {
  const cache = new SmartCache(
    'https://rpc.solana.com',
    'wss://rpc.solana.com',
    {
      ttl: {
        accounts: 60,        // 1 minute
        blocks: 600,         // 10 minutes
        transactions: 3600,  // 1 hour
        programAccounts: 30, // 30 seconds
      },
      redis: {
        host: 'localhost',
        port: 6379,
      },
    }
  );

  const pubkey = new PublicKey('AccountAddressHere');

  // First call - cache miss
  let start = Date.now();
  let account1 = await cache.getAccountInfo(pubkey);
  console.log(`First call: ${Date.now() - start}ms`);

  // Second call - cache hit
  start = Date.now();
  let account2 = await cache.getAccountInfo(pubkey);
  console.log(`Second call: ${Date.now() - start}ms`);

  // Get multiple accounts
  const pubkeys = [
    new PublicKey('Account1'),
    new PublicKey('Account2'),
    new PublicKey('Account3'),
  ];

  const accounts = await cache.getMultipleAccountsInfo(pubkeys);
  console.log(`Fetched ${accounts.length} accounts`);

  // Cache stats
  const stats = cache.getStats();
  console.log('Cache stats:', stats);
}

Cache Strategy by Data Type

Data TypeTTLInvalidationCommitment
Finalized transactions1 hour+Neverfinalized
Confirmed transactions5 minutesOn reorgconfirmed
Account data (static)10 minutesOn updateconfirmed
Account data (dynamic)30 secondsWebSocketconfirmed
Program accounts30 secondsManualconfirmed
Blocks10 minutesNeverfinalized
Token prices1 minuteTime-basedN/A

Performance Gains

ScenarioWithout CacheWith CacheImprovement
Get account150ms2ms75x faster
Get 100 accounts2000ms50ms40x faster
Get transaction200ms2ms100x faster
Program accounts500ms5ms100x faster

Multi-Region Failover

Implement robust failover logic for global reliability.

Use Case

Ensure your application remains operational even when RPC endpoints fail or experience degraded performance.

Architecture

graph TB
    A[Request] --> B[Primary RPC]
    B -->|Timeout/Error| C[Health Check]
    C -->|Unhealthy| D[Switch to Fallback]
    D --> E[Fallback RPC 1]
    E -->|Timeout/Error| F[Fallback RPC 2]

    G[Monitor] --> H[Track Latency]
    H --> I[Update Rankings]
    I --> J[Select Best Endpoint]

Complete Implementation

import { Connection, Commitment } from '@solana/web3.js';
import axios from 'axios';

interface EndpointConfig {
  url: string;
  region: string;
  priority: number;
}

interface EndpointHealth {
  url: string;
  healthy: boolean;
  latency: number;
  lastCheck: number;
  successRate: number;
  errorCount: number;
}

class MultiRegionFailover {
  private endpoints: EndpointConfig[];
  private health: Map<string, EndpointHealth>;
  private currentEndpoint: string;
  private connections: Map<string, Connection>;

  constructor(endpoints: EndpointConfig[]) {
    this.endpoints = endpoints.sort((a, b) => a.priority - b.priority);
    this.health = new Map();
    this.connections = new Map();
    this.currentEndpoint = endpoints[0].url;

    // Initialize health tracking
    for (const endpoint of endpoints) {
      this.health.set(endpoint.url, {
        url: endpoint.url,
        healthy: true,
        latency: 0,
        lastCheck: 0,
        successRate: 100,
        errorCount: 0,
      });

      this.connections.set(
        endpoint.url,
        new Connection(endpoint.url, 'confirmed')
      );
    }

    // Start health monitoring
    this.startHealthMonitoring();
  }

  /**
   * Get current connection with automatic failover
   */
  getConnection(): Connection {
    const connection = this.connections.get(this.currentEndpoint);
    if (!connection) {
      throw new Error('No healthy endpoints available');
    }
    return connection;
  }

  /**
   * Execute request with automatic failover
   */
  async executeWithFailover<T>(
    operation: (connection: Connection) => Promise<T>,
    maxRetries: number = 3
  ): Promise<T> {
    let lastError: Error | null = null;
    let attemptedEndpoints: string[] = [];

    for (let attempt = 0; attempt < maxRetries; attempt++) {
      const endpoint = this.selectBestEndpoint(attemptedEndpoints);

      if (!endpoint) {
        throw new Error('No healthy endpoints available');
      }

      attemptedEndpoints.push(endpoint);
      const connection = this.connections.get(endpoint)!;
      const health = this.health.get(endpoint)!;

      try {
        const start = Date.now();
        const result = await this.withTimeout(
          operation(connection),
          10000 // 10 second timeout
        );

        // Update health metrics
        health.latency = Date.now() - start;
        health.successRate = Math.min(100, health.successRate + 1);
        health.errorCount = Math.max(0, health.errorCount - 1);

        return result;

      } catch (error) {
        lastError = error as Error;
        console.error(`Request failed on ${endpoint}:`, error);

        // Update health metrics
        health.errorCount++;
        health.successRate = Math.max(0, health.successRate - 5);

        // Mark as unhealthy if too many errors
        if (health.errorCount >= 3) {
          health.healthy = false;
          console.warn(`Marking ${endpoint} as unhealthy`);
        }

        // Try next endpoint
        if (attempt < maxRetries - 1) {
          await this.delay(Math.pow(2, attempt) * 100);
        }
      }
    }

    throw new Error(
      `All endpoints failed after ${maxRetries} attempts: ${lastError?.message}`
    );
  }

  /**
   * Select best endpoint based on health and priority
   */
  private selectBestEndpoint(exclude: string[] = []): string | null {
    const candidates = this.endpoints.filter(
      ep => !exclude.includes(ep.url)
    );

    // Filter to healthy endpoints
    const healthy = candidates.filter(ep => {
      const health = this.health.get(ep.url);
      return health?.healthy && health.successRate > 50;
    });

    if (healthy.length === 0) {
      // No healthy endpoints, try any not excluded
      if (candidates.length > 0) {
        return candidates[0].url;
      }
      return null;
    }

    // Sort by latency and priority
    healthy.sort((a, b) => {
      const healthA = this.health.get(a.url)!;
      const healthB = this.health.get(b.url)!;

      // Prioritize by priority first
      if (a.priority !== b.priority) {
        return a.priority - b.priority;
      }

      // Then by latency
      return healthA.latency - healthB.latency;
    });

    const selected = healthy[0].url;
    this.currentEndpoint = selected;

    return selected;
  }

  /**
   * Start periodic health monitoring
   */
  private startHealthMonitoring(): void {
    const CHECK_INTERVAL = 30000; // 30 seconds

    setInterval(async () => {
      for (const endpoint of this.endpoints) {
        await this.checkEndpointHealth(endpoint.url);
      }

      // Log health status
      this.logHealthStatus();
    }, CHECK_INTERVAL);

    // Initial health check
    this.endpoints.forEach(ep => this.checkEndpointHealth(ep.url));
  }

  /**
   * Check health of a specific endpoint
   */
  private async checkEndpointHealth(url: string): Promise<void> {
    const health = this.health.get(url);
    if (!health) return;

    try {
      const connection = this.connections.get(url)!;
      const start = Date.now();

      // Simple health check - get slot
      await this.withTimeout(connection.getSlot(), 5000);

      const latency = Date.now() - start;

      // Update health
      health.healthy = true;
      health.latency = latency;
      health.lastCheck = Date.now();
      health.errorCount = Math.max(0, health.errorCount - 1);

      console.log(`Health check passed for ${url}: ${latency}ms`);

    } catch (error) {
      console.error(`Health check failed for ${url}:`, error);

      health.errorCount++;
      health.lastCheck = Date.now();

      if (health.errorCount >= 3) {
        health.healthy = false;
      }
    }
  }

  /**
   * Log current health status
   */
  private logHealthStatus(): void {
    console.log('\n=== Endpoint Health Status ===');

    for (const endpoint of this.endpoints) {
      const health = this.health.get(endpoint.url);
      if (!health) continue;

      const status = health.healthy ? 'HEALTHY' : 'UNHEALTHY';
      const current = endpoint.url === this.currentEndpoint ? '[CURRENT]' : '';

      console.log(
        `${status} ${current} ${endpoint.region} - ${endpoint.url}` +
        `\n  Latency: ${health.latency}ms | Success Rate: ${health.successRate.toFixed(1)}% | ` +
        `Errors: ${health.errorCount}`
      );
    }

    console.log('=============================\n');
  }

  /**
   * Get health status for all endpoints
   */
  getHealthStatus(): EndpointHealth[] {
    return Array.from(this.health.values());
  }

  /**
   * Manually mark endpoint as healthy/unhealthy
   */
  setEndpointHealth(url: string, healthy: boolean): void {
    const health = this.health.get(url);
    if (health) {
      health.healthy = healthy;
      health.errorCount = healthy ? 0 : 5;
    }
  }

  /**
   * Add timeout to promise
   */
  private withTimeout<T>(promise: Promise<T>, timeoutMs: number): Promise<T> {
    return Promise.race([
      promise,
      new Promise<T>((_, reject) =>
        setTimeout(() => reject(new Error('Request timeout')), timeoutMs)
      ),
    ]);
  }

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

// Usage example
async function exampleFailover() {
  const failover = new MultiRegionFailover([
    {
      url: 'https://rpc.solana.com',
      region: 'US-East',
      priority: 1,
    },
    {
      url: 'https://api.mainnet-beta.solana.com',
      region: 'US-West',
      priority: 2,
    },
    {
      url: 'https://solana-api.projectserum.com',
      region: 'EU',
      priority: 3,
    },
  ]);

  // Execute operation with automatic failover
  try {
    const slot = await failover.executeWithFailover(
      async (connection) => {
        return await connection.getSlot();
      }
    );

    console.log('Current slot:', slot);
  } catch (error) {
    console.error('All endpoints failed:', error);
  }

  // Get health status
  const health = failover.getHealthStatus();
  console.log('Endpoint health:', health);

  // Use in application
  const connection = failover.getConnection();
  const balance = await failover.executeWithFailover(
    async (conn) => await conn.getBalance(new PublicKey('WalletAddressHere'))
  );

  console.log('Balance:', balance);
}

Failover Strategies

StrategyUse CaseImplementation
Round-robinEqual priorityRotate through endpoints
Latency-basedPerformance criticalSelect lowest latency
Priority-basedCost optimizationUse cheapest first
GeographicRegional appsSelect closest region
HybridProductionPriority + latency + health

Monitoring and Alerts

interface AlertConfig {
  webhook?: string;
  email?: string;
  slackChannel?: string;
}

class FailoverAlerts {
  private config: AlertConfig;

  constructor(config: AlertConfig) {
    this.config = config;
  }

  async sendAlert(alert: Alert): Promise<void> {
    console.error('ALERT:', alert.message);

    if (this.config.webhook) {
      try {
        await axios.post(this.config.webhook, {
          type: alert.type,
          message: alert.message,
          timestamp: alert.timestamp,
          endpoint: alert.endpoint,
        });
      } catch (error) {
        console.error('Failed to send webhook alert:', error);
      }
    }

    // Add email, Slack, etc.
  }
}

interface Alert {
  type: 'endpoint_down' | 'all_endpoints_down' | 'high_latency' | 'degraded_performance';
  message: string;
  timestamp: number;
  endpoint?: string;
}

Performance Considerations

  • Health Check Frequency: Balance between responsiveness and overhead (30-60 seconds)
  • Timeout Values: Set appropriate timeouts (5-10 seconds for health checks)
  • Connection Pooling: Maintain persistent connections to all endpoints
  • Circuit Breaker: Temporarily disable failing endpoints

Testing Approach

All patterns should include comprehensive tests:

// Example test structure
describe('PaymentProcessor', () => {
  let processor: PaymentProcessor;

  beforeEach(() => {
    processor = new PaymentProcessor(TEST_RPC, TEST_WS);
  });

  it('should process valid payment', async () => {
    // Test implementation
  });

  it('should handle timeout', async () => {
    // Test timeout scenario
  });

  it('should detect reorg', async () => {
    // Test reorg detection
  });
});

Best Practices Summary

  1. Error Handling: Always implement retry logic with exponential backoff
  2. Rate Limiting: Respect RPC rate limits with delays between batches
  3. Connection Management: Use connection pooling for high-frequency operations
  4. Caching: Cache immutable data (finalized transactions, blocks)
  5. Monitoring: Track success rates, latencies, and errors
  6. Failover: Implement multi-region failover for production
  7. Cost Optimization: Batch requests, use WebSocket, cache aggressively
  8. Testing: Test all edge cases, timeouts, and failure scenarios

Additional Resources