Listening to High-Value SOL Transactions via Yellowstone Geyser gRPC with GetBlock

How to Build an App to Track Large Solana Transactions with GetBlock Yellowstone Geyer gRPC

Monitoring high-value SOL transactions is essential for understanding the dynamics of the Solana network. By using tools like Yellowstone Geyser gRPC with GetBlock, developers can gain real-time insights into significant financial movements, enabling them to make informed decisions and capitalize on trends within the Solana ecosystem.

In this tutorial, you'll build a monitoring app that detects high-value SOL transfers the moment they occur on the Solana blockchain.

You'll learn how to:

  • Connect to GetBlock's Yellowstone gRPC service for ultra-low latency blockchain data streaming

  • Subscribe to Solana's System Program to capture all native SOL transfers

  • Parse transfer instruction data to extract amounts, sender addresses, and recipient addresses

  • Filter transactions based on transfer amount thresholds

  • Display real-time alerts with formatted output and explorer links

  • Track statistics, including total volume and largest transfers

Technology Stack:

  • Node.js

  • @triton-one/yellowstone-grpc - Official Yellowstone gRPC client

  • bs58 - Base58 encoding for Solana addresses

  • GetBlock's Dedicated Solana Node with Yellowstone add-on

Prerequisites

Before you begin, ensure you have:

  • Node.js v18 or higher is installed on your system

  • Basic JavaScript knowledge

  • A GetBlock account - Free sign-up available at getblock.io

  • A Dedicated Solana Node subscription on GetBlock - Required for Yellowstone gRPC access

Step 1: Set Up Your GetBlock Yellowstone Endpoint

Deploy Your Dedicated Solana Node

First, you need to deploy a dedicated Solana node on GetBlock with the Yellowstone gRPC add-on enabled.

1. Sign up or log in

  • Go to GetBlock

  • Create an account or log in to your existing account

2. Deploy your dedicated node

  • Navigate to your user Dashboard

  • Switch to the "Dedicated nodes" tab

  • Scroll down to "My endpoints."

  • Under "Protocol", select Solana

  • Set the network to Mainnet

  • Click Get

3. Enable the Yellowstone gRPC add-on

  • In Step 3 of your node setup (Select API and Add-ons)

  • Check the box for Yellowstone gRPC under Add-ons

  • Complete payout and finalize the setup

Generate Your gRPC Access Token

Once your node is live, you'll create an access token to authenticate your gRPC connections.

1. Return to your dashboard

  • Go to "My endpoints" in your Dedicated node dashboard and generate a gRPC Access Token

2. Your endpoint URL

You'll receive an HTTPS-style gRPC endpoint URL based on your chosen region:

https://go.getblock.asia/YOUR_ACCESS_TOKEN/

Step 2: Initialize Your Project

  1. Open your terminal and create a new directory:

mkdir sol-transfer-monitor
cd sol-transfer-monitor
  1. Initialize Node.js Project

npm init -y
  1. Install the required packages:

npm install @triton-one/yellowstone-grpc [email protected] dotenv
  1. Create a new file:

touch sol-transfer-monitor.js
  1. Configure package.json:

{
  "name": "sol-transfer-monitor",
  "version": "1.0.0",
  "description": "A Listener that tracks high-value SOL transactions",
  "main": "sol-transfer-monitor.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node sol-transfer-monitor.js"
  },
  "keywords": [
    "Solana",
    "Liquidity Monitor",
    "Onchain transfer"
  ],
  "author": "",
  "license": "ISC",
  "type": "module",
  "dependencies": {
    "@triton-one/yellowstone-grpc": "^4.0.2",
    "bs58": "^5.0.0",
    "dotenv": "^17.2.3"
  }
}
  • "type": "module" - Enables ES6 import/export syntax (required for modern JavaScript)

  • "main": "sol-transfer-monitor.js" - Specifies the entry point of your application

  • "scripts" - Defines shortcuts like npm start

Project Structure

  1. Create the following files to have a basic structure for your project:

pumpfun-migrations-listener/
├── sol-transfer-monitor.js          # Main application logic
├── .env              # Environment variables (add to .gitignore)
├── .gitignore        # Git ignore file
└── package.json      # Project configuration
  1. Create a .gitignore file:

node_modules/
.env
*.log

Step 3: Start Building Your Monitor

You'll build everything in a single file called sol-transfer-monitor.js.

Import Dependencies

import Client from "@triton-one/yellowstone-grpc";
import { CommitmentLevel } from "@triton-one/yellowstone-grpc";
import bs58 from "bs58";
import { config } from "dotenv";
config();

What this does:

  • Client - The main class for establishing a connection to GetBlock's Yellowstone gRPC service

  • CommitmentLevel- Configuration options that determine how finalized transactions should be before you receive them (PROCESSED = fastest but can be rolled back, CONFIRMED = balanced, FINALIZED = slowest but guaranteed)

  • bs58 - A library that converts Solana's binary address format into the human-readable base58 strings you see on blockchain explorers (like "9wFFyRfZBsuAha4YcuxcXLKwMxJR...")

  • config: This loads the .env file

Add Your GetBlock Configuration

const ENDPOINT = "https://go.getblock.io";  // Your region's endpoint
const TOKEN = process.env.GETBLOCK_TOKEN;           // Your generated token

What this does: Stores your GetBlock credentials for authenticating your connection.

Add Solana System Program Constants

// System Program (handles all native SOL transfers)
const SYSTEM_PROGRAM = "11111111111111111111111111111111";

// Minimum transfer amount in SOL to alert on
const MIN_TRANSFER_AMOUNT = 100; // 100 SOL threshold

What this does:

  • SYSTEM_PROGRAM - This is the unique identifier for Solana's built-in System Program, which is responsible for all native SOL transfers on the network. Every time someone sends SOL from one wallet to another, this program processes it.

  • MIN_TRANSFER_AMOUNT - Sets the threshold for what qualifies as a "high-value" transfer. Only transfers of 100 SOL or more will trigger an alert. You can adjust this to any amount (e.g., 50 SOL, 1000 SOL, etc.).

Add Statistics Tracker

// Statistics Tracking
let stats = {
  startTime: Date.now(),
  totalTransfers: 0,
  totalVolume: 0,
  largestTransfer: 0,
  largestTransferTx: null
};

What this does:

  • Creates an object to track key metrics about the transfers you're monitoring. It records:

    • When the monitor started

    • How many high-value transfers have been detected

    • the cumulative volume of all transfers

    • information about the largest transfer seen so far.

Step 4: Build Utility Functions

Now you'll add helper functions to format data and display results.

Add SOL Formatter

function formatSOL(lamports) {
  return (lamports / 1_000_000_000).toFixed(4);
}

What this does:

  • Converts lamports (Solana's smallest unit) to SOL for human-readable display. Solana stores all amounts in lamports, where 1 SOL equals 1 billion lamports (similar to how 1 Bitcoin = 100 million satoshis). This function divides by 1 billion and rounds to 4 decimal places, so 5,500,000,000 lamports becomes "5.5000 SOL".

Add Display Function

function displayTransfer(transferData) {
  stats.totalTransfers++;
  stats.totalVolume += transferData.amount;
  
  if (transferData.amount > stats.largestTransfer) {
    stats.largestTransfer = transferData.amount;
    stats.largestTransferTx = transferData.signature;
  }
  
  const solAmount = formatSOL(transferData.amount);
  
  console.log("\n" + "=".repeat(80));
  console.log(`HIGH VALUE TRANSFER #${stats.totalTransfers}`);
  console.log("=".repeat(80));
  
  console.log(`\nAMOUNT:      ${solAmount} SOL`);
  console.log(`FROM:        ${transferData.from}`);
  console.log(`TO:          ${transferData.to}`);
  console.log(`SIGNATURE:   ${transferData.signature}`);
  console.log(`SLOT:        ${transferData.slot}`);
  
  console.log(`\nEXPLORE:`);
  console.log(`   TX:     https://solscan.io/tx/${transferData.signature}`);
  console.log(`   From:   https://solscan.io/account/${transferData.from}`);
  console.log(`   To:     https://solscan.io/account/${transferData.to}`);
  
  console.log("\n" + "=".repeat(80) + "\n");
}

What this does:

  • Formats and displays all transfer information clearly. It first updates your running statistics (incrementing the transfer count, adding to total volume, and checking if this is the largest transfer seen).

  • Then it converts the amount from lamports to SOL and prints out all the details, including sender, recipient, transaction signature, and slot number.

  • Finally, it includes clickable Solscan explorer links so you can investigate the transaction, sender, and recipient in more detail.

Step 5: Build the Transfer Parser

Now you'll create the function that extracts transfer details from instruction data.

Add Transfer Instruction Parser

function parseTransferInstruction(instruction, accountKeys) {
  try {
    const data = Buffer.from(instruction.data);
    
    // System program transfer instruction has type 2
    // Data format: [4 bytes: instruction type (2)][8 bytes: lamports amount]
    if (data.length < 12) return null;
    
    const instructionType = data.readUInt32LE(0);
    if (instructionType !== 2) return null; // Not a transfer instruction
    
    const lamports = data.readBigUInt64LE(4);
    
    // Get from and to accounts
    const accountIndices = Array.from(instruction.accounts);
    if (accountIndices.length < 2) return null;
    
    const fromAccount = bs58.encode(Buffer.from(accountKeys[accountIndices[0]]));
    const toAccount = bs58.encode(Buffer.from(accountKeys[accountIndices[1]]));
    
    return {
      amount: Number(lamports),
      from: fromAccount,
      to: toAccount
    };
  } catch (error) {
    return null;
  }
}

What this does:

  • Extracts the transfer amount and account addresses from a System Program instruction.

Step 6: Build the Main Monitor Function

Now you'll create the core function that connects to GetBlock and processes blockchain data.

Part A: Start the Function and Connect

async function monitorHighValueTransfers() {
  console.log("Starting High Value SOL Transfer Monitor");
  console.log(`Minimum amount: ${MIN_TRANSFER_AMOUNT} SOL`);
  console.log(`Watching: Native SOL transfers\n`);
  console.log("Waiting for high value transfers...\n");
  
  return new Promise(async (resolve, reject) => {
    try {
      const client = new Client(ENDPOINT, TOKEN, undefined);
      const stream = await client.subscribe();

What this does:

  • Initializes the monitor by displaying startup information and establishing a connection to GetBlock.

  • Creates a new Client instance using your endpoint and token, then opens a bidirectional streaming connection.

  • The subscribe() method returns a stream object that handles both sending requests to GetBlock and receiving blockchain data.

Part B: Configure Subscription

 const request = {
        accounts: {},
        slots: {},
        transactions: {
          sol_transfers: {
            accountInclude: [SYSTEM_PROGRAM],
            accountExclude: [],
            accountRequired: []
          }
        },
        transactionsStatus: {},
        entry: {},
        blocks: {},
        blocksMeta: {},
        commitment: CommitmentLevel.CONFIRMED,
        accountsDataSlice: [],
        ping: undefined
      };

What this does:

  • Builds the subscription request that tells GetBlock exactly what blockchain data you want to receive.

    • It filters sol_transfers

    • Send transactions that interact with the system

    • Only receive transactions that are CONFIRMED

Part C: Handle Incoming Data

stream.on("data", (message) => {
    try {
      if (message.pong) {
        stream.write({ ping: { id: message.pong.id } });
        return;
      }
      
      if (message.transaction && message.filters && 
          message.filters.includes('sol_transfers')) {
        
        const tx = message.transaction.transaction;
        const signature = bs58.encode(tx.signature);
        const slot = message.transaction.slot.toString();
        
        const txMessage = tx.transaction.message;
        const accountKeys = txMessage.accountKeys;
        const instructions = txMessage.instructions;

What this does:

  • Sets up an event handler that processes every message GetBlock sends through the stream.

Part D: Process Instructions

// Process each instruction
    for (const instruction of instructions) {
      const programIdx = instruction.programIdIndex;
      const programId = bs58.encode(accountKeys[programIdx]);
      
      // Only process System Program instructions
      if (programId !== SYSTEM_PROGRAM) continue;
      
      const transferData = parseTransferInstruction(instruction, accountKeys);
      
      if (!transferData) continue;
      
      // Check if transfer meets minimum threshold
      const solAmount = transferData.amount / 1_000_000_000;
      if (solAmount >= MIN_TRANSFER_AMOUNT) {
        displayTransfer({
          ...transferData,
          signature: signature,
          slot: slot
        });
      }
    }
  }
} catch (error) {
  console.error(`Error processing transaction: ${error.message}`);
}
});

What this does:

  • Loops through each instruction in the transaction to find and process SOL transfers.

Part E: Handle Stream Events

stream.on("error", (error) => {
        console.error(`Stream error: ${error.message}`);
        reject(error);
      });
      
      stream.on("end", () => resolve());
      stream.on("close", () => resolve());

What this does:

  • Sets up handlers for stream connection issues.

  • If the stream encounters an error (network problem, authentication issue, etc.), it logs the error message and rejects the Promise, which will trigger the auto-restart logic in the main() function.

  • The "end" and "close" events indicate the stream has been terminated gracefully, so it resolves the Promise normally.

Part F: Send Subscription Request

stream.write(request, (err) => {
        if (err) {
          reject(err);
        } else {
          console.log("Subscription active - monitoring blockchain...\n");
        }
      });
      
    } catch (error) {
      reject(error);
    }
  });
}

What this does:

  • Send your subscription request to GetBlock to activate the stream.

  • Once the request is sent successfully, it confirms that monitoring is active.

  • The callback function checks if there was an error sending the request - if so, it rejects the Promise; otherwise, it displays a success message. This completes the monitorHighValueTransfers() function.

Step 7: Add Execution Code

Finally, add the code to run your monitor and handle shutdown.

Add Main Execution Function

async function main() {
  try {
    await monitorHighValueTransfers();
  } catch (error) {
    console.error("Monitor crashed:", error.message);
    console.log("Restarting in 5 seconds...");
    setTimeout(main, 5000);
  }
}

What this does:

  • Wraps your monitor in an auto-restart loop. If the monitor crashes for any reason (network disconnection, API error, unexpected data format), it catches the error, displays an error message, waits 5 seconds, and then automatically restarts the monitor. This ensures your monitor keeps running even if temporary issues occur.

Add Shutdown Handler

process.on('SIGINT', () => {
  console.log("\n\nShutting down...");
  console.log(`\nTotal high value transfers: ${stats.totalTransfers}`);
  console.log(`Total volume: ${formatSOL(stats.totalVolume)} SOL`);
  console.log(`Largest transfer: ${formatSOL(stats.largestTransfer)} SOL`);
  if (stats.largestTransferTx) {
    console.log(`Largest TX: https://solscan.io/tx/${stats.largestTransferTx}`);
  }
  const uptime = Math.floor((Date.now() - stats.startTime) / 1000);
  console.log(`Uptime: ${Math.floor(uptime / 60)}m ${uptime % 60}s\n`);
  process.exit(0);
});

What this does:

  • Handles shutdown when you press Ctrl+C or cmd+C. Instead of abruptly terminating, it displays a summary of your monitoring session, including total transfers detected, cumulative volume, the largest transfer amount with a link to view it, and how long the monitor was running. Then it cleanly exits the process.

Start the Monitor

main();

What this does:

  • Run everything. This is the last line of your sol-transfer-monitor.js file and triggers the execution of your monitor.

TestStep 8: Tets Your Monitor

1. Start the Monitor

npm start
  1. Expected Output:

> [email protected] start
> node sol-transfer-monitor.js

[[email protected]] injecting env (1) from .env -- tip: ⚙️  load multiple .env files with { path: ['.env.local', '.env'] }
Starting High Value SOL Transfer Monitor
Minimum amount: 100 SOL
Watching: Native SOL transfers

Waiting for high value transfers...

Subscription active - monitoring blockchain...


================================================================================
HIGH VALUE TRANSFER #1
================================================================================

AMOUNT:      1990.0000 SOL
FROM:        5tzFkiKscXHK5ZXCGbXZxdw7gTjjD1mBwuoFbhUvuAi9
TO:          53AXjkQHZQEbHZvV55VHtcZtFzBzArLZZG5AF4ufkRhv
SIGNATURE:   4uJgnfRohiq6c9CDWUtk3w94ouYGya5dGkx4LSS5tuFGDRypuXtN7cQLLy3eNj5V1b5PwEWccJqLak1Rnd9kRtTB
SLOT:        382899112

EXPLORE:
   TX:     https://solscan.io/tx/4uJgnfRohiq6c9CDWUtk3w94ouYGya5dGkx4LSS5tuFGDRypuXtN7cQLLy3eNj5V1b5PwEWccJqLak1Rnd9kRtTB
   From:   https://solscan.io/account/5tzFkiKscXHK5ZXCGbXZxdw7gTjjD1mBwuoFbhUvuAi9
   To:     https://solscan.io/account/53AXjkQHZQEbHZvV55VHtcZtFzBzArLZZG5AF4ufkRhv

================================================================================

^C

Shutting down...

Total high value transfers: 7
Total volume: 5519.0244 SOL
Largest transfer: 2272.0770 SOL
Largest TX: https://solscan.io/tx/3p92tKFb4AGRUJKA87vJD4fYcb3Z9wKi5AuUskA6TAR5SYciZxL3qmSH88ubQP3g7xSJQjC5kEeMSr7Lhhn8Hkc4
Uptime: 1m 50s

Troubleshooting

  1. Permission denied:

Stream error: 7 PERMISSION_DENIED: RBAC: access denied
Monitor crashed: 7 PERMISSION_DENIED: RBAC: access denied

This means that your access token is missing or incomplete, or you are using the wrong Base URL.

2. No Transfers Showing

  • High-value transfers (100+ SOL) are less common than you might expect

  • Try lowering MIN_TRANSFER_AMOUNT to 10 or 50 SOL to see more activity

  • Use CommitmentLevel.PROCESSED for faster updates (though some may be rolled back)

Conclusion

In this guide, you've learnt how to build a monitor app that tracks high-value SOL transactions on Solana networks. It shows you the steps involved, such as:

  • Connects to GetBlock's Yellowstone gRPC service

  • Streams blockchain data with ~400ms latency

  • Filters for System Program transactions

  • Parses SOL transfer amounts and addresses

  • Filters by minimum transfer threshold

  • Displays formatted results with links

  • Tracks cumulative statistics

  • Handles errors and auto-reconnects

Additional Resources

Last updated

Was this helpful?