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/https://go.getblock.us/YOUR_ACCESS_TOKEN/https://go.getblock.io/YOUR_ACCESS_TOKEN/Save both pieces of information:
Base Endpoint: https://go.getblock.io (or your region)
Access Token: The alphanumeric token generated
You'll use these in your code to authenticate with GetBlock's Yellowstone service.
Step 2: Initialize Your Project
Open your terminal and create a new directory:
mkdir sol-transfer-monitor
cd sol-transfer-monitorInitialize Node.js Project
npm init -yInstall the required packages:
npm install @triton-one/yellowstone-grpc [email protected] dotenvCreate a new file:
touch sol-transfer-monitor.jsConfigure
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 likenpm start
Project Structure
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 configurationCreate a
.gitignorefile:
node_modules/
.env
*.logStep 3: Start Building Your Monitor
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 serviceCommitmentLevel- 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.envfile
Add Your GetBlock Configuration
const ENDPOINT = "https://go.getblock.io"; // Your region's endpoint
const TOKEN = process.env.GETBLOCK_TOKEN; // Your generated tokenWhat this does: Stores your GetBlock credentials for authenticating your connection.
If you chose a different region during setup, use that base URL instead (e.g., https://go.getblock.us or https://go.getblock.asia).
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 thresholdWhat 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.
If anything goes wrong during parsing (malformed data, missing accounts, etc.), it catches the error and returns null.
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+Corcmd+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 startExpected 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 50sTroubleshooting
Permission denied:
Stream error: 7 PERMISSION_DENIED: RBAC: access denied
Monitor crashed: 7 PERMISSION_DENIED: RBAC: access deniedThis 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?