Overview

This quick guide shows you how to:

  • Check the Geyser connection using the @triton-one/yellowstone-grpc client in Node.js
  • Stream the programs using the @triton-one/yellowstone-grpc client in Node.js

Prerequisites

Implementation

Once you have the Chainstack Yellowstone gRPC Geyser endpoint and the authentication token, use the following examples to check the connection and to monitor programs.

Connection checker

const { default: Client } = require("@triton-one/yellowstone-grpc");

const ENDPOINT = "CHAINSTACK_GEYSER_URL"; // Replace with your actual endpoint
const TOKEN = "CHAINSTACK_GEYSER_TOKEN"; // Replace with your actual token

(async () => {
    const client = new Client(ENDPOINT, TOKEN);

    const version = await client.getVersion();
    console.log(version);
})();

This will print the connection status and the Geyser client version.

Program watcher

Note the use of ping every 10 seconds to keep the connection alive as specified in the docs: Sending pings to keep the stream alive.

const { default: Client } = require("@triton-one/yellowstone-grpc");
const Base58 = require('bs58');

const ENDPOINT = "CHAINSTACK_GEYSER_URL"; // Replace with your actual endpoint
const TOKEN = "CHAINSTACK_GEYSER_TOKEN"; // Replace with your actual token
const PING_INTERVAL_MILLISECONDS = 10000; // Send ping every 10 seconds

// DEX Program IDs to watch
const DEX_PROGRAM_IDS = [
    "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P",
    "pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA",
    "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8",
    "CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK",
    "CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C",
    "LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo",
    "Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB",
    "MoonCVVNZFSYkqNXP6bxHLPL6QQJiMagDL3qcqUQTrG",
    "FLUXubRmkEi2q6K3Y9kBPg9248ggaZVsoSFhtJHSrm1X",
    "whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc"
];

async function main() {
    const client = new Client(ENDPOINT, TOKEN);

    try {
        console.log('Connecting to Solana...');
        const stream = await client.subscribe();

        // Prepare ping request
        const pingRequest = {
            ping: { id: 1 },
            accounts: {},
            accountsDataSlice: [],
            transactions: {},
            transactionsStatus: {},
            blocks: {},
            blocksMeta: {},
            entry: {},
            slots: {},
        };

        // Set up periodic ping to keep the stream alive
        const pingInterval = setInterval(async () => {
            try {
                await new Promise((resolve, reject) => {
                    stream.write(pingRequest, (err) => {
                        if (err === null || err === undefined) {
                            resolve();
                        } else {
                            reject(err);
                        }
                    });
                });
                console.log("Ping sent to keep stream alive");
            } catch (error) {
                console.error("Error sending ping:", error);
            }
        }, PING_INTERVAL_MILLISECONDS);

        const request = {
            accounts: {},
            slots: {},
            transactions: {
                dex: {
                    vote: false,
                    failed: false,
                    accountExclude: [],
                    accountRequired: [],
                    accountInclude: DEX_PROGRAM_IDS
                }
            },
            transactionsStatus: {},
            entry: {},
            blocks: {},
            blocksMeta: {},
            commitment: 'confirmed',
            accountsDataSlice: [],
        };

        stream.write(request);
        console.log('Connection established - watching DEX transactions\n');
        console.log('Monitoring programs:', DEX_PROGRAM_IDS);

        stream.on('data', (data) => {
            // Handle pong responses
            if (data.pong) {
                console.log("Received Pong response");
                return;
            }

            if (data.transaction && data.transaction.transaction) {
                const tx = data.transaction;
                try {
                    // Convert signature to string
                    const signature = tx.transaction.signature.toString('hex');
                    
                    // Find which DEX program was involved in this transaction
                    let involvedPrograms = [];
                    if (tx.transaction.transaction.message.accountKeys) {
                        involvedPrograms = DEX_PROGRAM_IDS.filter(progId => 
                            tx.transaction.transaction.message.accountKeys.includes(progId));
                    }
                    
                    console.log('New DEX Transaction:', {
                        signature: signature,
                        slot: tx.slot,
                        success: tx.transaction.meta?.err === null,
                        accounts: tx.transaction.transaction.message.accountKeys.length,
                        instructions: tx.transaction.transaction.message.instructions.length,
                        lamportFee: tx.transaction.meta?.fee || 0,
                        computeUnits: tx.transaction.meta?.computeUnitsConsumed || 0,
                        involvedDEX: involvedPrograms
                    });

                    // Log transaction details
                    if (tx.transaction.meta?.logMessages) {
                        console.log('Transaction logs:');
                        tx.transaction.meta.logMessages.forEach(log => console.log(log));
                    }
                    console.log('----------------------------------------');

                } catch (err) {
                    console.error('Error processing transaction:', err);
                    console.error('Raw signature:', tx.transaction.signature);
                }
            }
        });

        stream.on("error", (error) => {
            console.error('Stream error:', error);
            clearInterval(pingInterval); // Clean up the ping interval on error
        });

        // Clean up on stream close
        stream.on("close", () => {
            console.log("Stream closed");
            clearInterval(pingInterval);
        });

    } catch (error) {
        console.error('Error in subscription process:', error);
    }
}

// Run the main function
main().catch((err) => {
    console.error('Unhandled error in main:', err);
});

// Handle shutdown
process.on('SIGINT', () => {
    console.log('Shutting down...');
    process.exit();
}); 

This will stream the data from the Solana program IDs as provided in DEX_PROGRAM_IDS.

For a Python example, see Solana: Listening to pump.fun token mint using Geyser.

8_Bi4fdM_400x400

Ake

Director of Developer Experience @ Chainstack

Talk to me all things Web3

20 years in technology | 8+ years in Web3 full time years experience

Trusted advisor helping developers navigate the complexities of blockchain infrastructure