Overview

Transaction monitoring enables you to track transaction execution, success/failure status, program interactions, and token balance changes across Solana in real-time. This guide covers filtering strategies and practical implementations for different transaction monitoring use cases.

Prerequisites: This guide assumes you’ve completed the Yellowstone gRPC Quickstart and have a working stream setup.

Transaction Filtering Options

Monitor transactions involving specific programs

Track all transactions that interact with your programs:

const subscribeRequest: SubscribeRequest = {
  transactions: {
    client: {
      accountInclude: [
        "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", // Token Program
        "11111111111111111111111111111111",              // System Program
        "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8"  // Your program
      ],
      accountExclude: [],
      accountRequired: [],
      vote: false,
      failed: false
    }
  },
  commitment: CommitmentLevel.CONFIRMED
};

Best for: Program-specific monitoring, DeFi protocol tracking, smart contract interactions

Practical Examples

Example 1: Monitor DEX Transactions

Track all transactions involving popular DEX programs:

import { StreamManager } from './stream-manager'; // From quickstart guide

async function monitorDEXTransactions() {
  const streamManager = new StreamManager(
    "your-grpc-endpoint",
    "your-api-key",
    handleDEXTransaction
  );

  const subscribeRequest: SubscribeRequest = {
    transactions: {
      client: {
        accountInclude: [
          "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM", // Raydium V4
          "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8", // Raydium V5
          "CAMMCzo5YL8w4VFF8KVHrK22GGUQpMpTFb6xRmpLFGNnSm", // Raydium CLMM
          "JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4"   // Jupiter
        ],
        vote: false,
        failed: false
      }
    },
    commitment: CommitmentLevel.CONFIRMED
  };

  await streamManager.connect(subscribeRequest);
}

function handleDEXTransaction(data: any): void {
  if (data.transaction?.transaction) {
    const tx = data.transaction.transaction;
    console.log(`\n🔄 DEX Transaction:`);
    console.log(`  Signature: ${tx.signature}`);
    console.log(`  Slot: ${data.transaction.slot}`);
    console.log(`  Status: ${tx.meta?.err ? 'Failed' : 'Success'}`);
    console.log(`  Fee: ${tx.meta?.fee || 0} lamports`);
    console.log(`  Compute Units: ${tx.meta?.computeUnitsConsumed || 0}`);
    
    // Show token balance changes
    if (tx.meta?.preTokenBalances?.length > 0) {
      console.log(`  Token Balance Changes:`);
      tx.meta.preTokenBalances.forEach((preBalance: any, index: number) => {
        const postBalance = tx.meta.postTokenBalances[index];
        if (preBalance && postBalance) {
          const change = postBalance.uiTokenAmount.uiAmount - preBalance.uiTokenAmount.uiAmount;
          if (change !== 0) {
            console.log(`    ${preBalance.mint}: ${change > 0 ? '+' : ''}${change}`);
          }
        }
      });
    }
  }
}

Example 2: Monitor Failed Transactions

Track failed transactions to identify issues:

async function monitorFailedTransactions() {
  const streamManager = new StreamManager(
    "your-grpc-endpoint",
    "your-api-key",
    handleFailedTransaction
  );

  const subscribeRequest: SubscribeRequest = {
    transactions: {
      client: {
        accountInclude: ["YourProgramId"], // Your program
        vote: false,
        failed: true // Only failed transactions
      }
    },
    commitment: CommitmentLevel.CONFIRMED
  };

  await streamManager.connect(subscribeRequest);
}

function handleFailedTransaction(data: any): void {
  if (data.transaction?.transaction?.meta?.err) {
    const tx = data.transaction.transaction;
    console.log(`\n❌ Failed Transaction:`);
    console.log(`  Signature: ${tx.signature}`);
    console.log(`  Slot: ${data.transaction.slot}`);
    console.log(`  Error: ${JSON.stringify(tx.meta.err)}`);
    console.log(`  Fee: ${tx.meta.fee} lamports`);
    console.log(`  Compute Units: ${tx.meta.computeUnitsConsumed || 0}`);
    
    // Log instruction details for debugging
    if (tx.transaction?.message?.instructions) {
      console.log(`  Instructions:`);
      tx.transaction.message.instructions.forEach((inst: any, i: number) => {
        console.log(`    ${i}: Program ${inst.programIdIndex}, Data: ${inst.data}`);
      });
    }
  }
}

Example 3: Monitor High-Value Transactions

Track transactions with significant SOL transfers:

async function monitorHighValueTransactions() {
  const streamManager = new StreamManager(
    "your-grpc-endpoint",
    "your-api-key",
    handleHighValueTransaction
  );

  const subscribeRequest: SubscribeRequest = {
    transactions: {
      client: {
        accountInclude: ["11111111111111111111111111111111"], // System Program
        vote: false,
        failed: false
      }
    },
    commitment: CommitmentLevel.CONFIRMED
  };

  await streamManager.connect(subscribeRequest);
}

function handleHighValueTransaction(data: any): void {
  if (data.transaction?.transaction?.meta) {
    const tx = data.transaction.transaction;
    const preBalances = tx.meta.preBalances || [];
    const postBalances = tx.meta.postBalances || [];
    
    // Calculate largest balance change
    let maxChange = 0;
    preBalances.forEach((preBalance: number, index: number) => {
      const postBalance = postBalances[index] || 0;
      const change = Math.abs(postBalance - preBalance);
      maxChange = Math.max(maxChange, change);
    });
    
    // Only log transactions with > 10 SOL moved
    const changeInSOL = maxChange / 1e9;
    if (changeInSOL > 10) {
      console.log(`\n💰 High-Value Transaction:`);
      console.log(`  Signature: ${tx.signature}`);
      console.log(`  Slot: ${data.transaction.slot}`);
      console.log(`  Max SOL Transfer: ${changeInSOL.toFixed(2)} SOL`);
      console.log(`  Fee: ${tx.meta.fee / 1e9} SOL`);
      console.log(`  Accounts: ${tx.transaction?.message?.accountKeys?.length || 0}`);
    }
  }
}

Transaction Data Structure

Understanding the transaction data structure helps extract relevant information:

Filter Logic Reference

Include Logic (OR)

accountInclude: Transaction must involve ANY of these accounts

Example: ["A", "B"] matches transactions involving account A OR account B

Required Logic (AND)

accountRequired: Transaction must involve ALL of these accounts

Example: ["A", "B"] matches transactions involving account A AND account B

Exclude Logic (NOT)

accountExclude: Transaction must NOT involve any of these accounts

Example: ["A", "B"] excludes transactions involving account A or account B

Combined Logic

Final filter: (accountInclude OR empty) AND (accountRequired AND all) AND NOT (accountExclude OR any)

Performance Considerations

Transaction streams can be high-volume

  • Start with specific program filters
  • Use commitment levels appropriately
  • Monitor your processing capacity
  • Implement backpressure handling
// Rate limiting example
let transactionCount = 0;
const startTime = Date.now();

function handleTransaction(data: any): void {
  transactionCount++;
  
  if (transactionCount % 100 === 0) {
    const elapsed = (Date.now() - startTime) / 1000;
    const rate = transactionCount / elapsed;
    console.log(`Processing ${rate.toFixed(1)} tx/sec`);
  }
  
  // Your transaction processing logic
}

Error Handling

Common transaction monitoring errors and solutions:

Next Steps