TypeScript Example

Complete JavaScript/TypeScript implementation for streaming Solana transactions from NextBlock's TX Stream API, optimized for trading bots and fast transaction detection.

The example is hosted at github.com/nextblock-ag/nextblock-stream-examples

Prerequisites

bun add @grpc/grpc-js @grpc/proto-loader
bun add @solana/addresses @solana/codec-strings @solana/kit

# Generate the TX Stream proto client from the proto specification
# In the example repo, dependencies and the generated .ts files are already available

Example

import * as grpc from "@grpc/grpc-js";

import {
    NextStreamNotification,
    NextStreamServiceClient,
} from "./protos/stream";
import { NextStreamSubscription } from "./protos/stream";
import {
    createKeyPairFromBytes,
    getAddressFromPublicKey,
    getBase58Decoder,
    getBase58Encoder,
    getCompiledTransactionMessageDecoder,
    getSignatureFromTransaction,
    getTransactionDecoder,
    getUtf8Encoder,
    signBytes,
    type Address,
} from "@solana/kit";

/*
the auth message needs to be built clients-side.
it is a pipe-separated string made of:
1. domain that's being connected to (e.g. fra.stream.nextblock.io)
2. the publickey that sent the fee to strmuYvHKeA1qvHqooUpwUk2BFwaAmMbK9WXY9mh2GJ
3. a random nonce
4. the current unix timestamp

it is then signed by the supplied publickey.
*/

function buildAuthMessage(
    domain: string,
    authenticatedPublicKey: Address
): string {
    const nonce = Math.random().toString();
    const ts = Math.floor(Date.now());
    return `${domain}|${authenticatedPublicKey.toString()}|${nonce}|${ts}`;
}

async function main() {
    const domain = "fra.stream.nextblock.io:22221";
    const authenticationPrivateKey = await createKeyPairFromBytes(
        getBase58Encoder().encode("YOUR_PRIVATE_KEY")
    );
    const authenticationPublicKey = await getAddressFromPublicKey(
        authenticationPrivateKey.publicKey
    );

    const authenticationMessage = buildAuthMessage(
        domain,
        authenticationPublicKey
    );
    const authenticationSignature = await signBytes(
        authenticationPrivateKey.privateKey,
        getUtf8Encoder().encode(authenticationMessage)
    );

    const client = new NextStreamServiceClient(
        domain,
        grpc.credentials.createInsecure(),
        {
            "grpc.keepalive_time_ms": 5000,
            "grpc.keepalive_timeout_ms": 1000,
            "grpc.keepalive_permit_without_calls": 0,
        }
    );

    const req: NextStreamSubscription = {
        authenticationPublickey: authenticationPublicKey.toString(),
        authenticationMessage,
        authenticationSignature: getBase58Decoder().decode(
            authenticationSignature
        ),
        accounts: ["675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8"],
    };

    const stream = client.subscribeNextStream(req);

    stream.on("data", (msg: NextStreamNotification) => {
        try {
            const packet = msg.packet;
            if (!packet) {
                return;
            }

            const txBytes = packet.transaction;
            const slot = packet.slot;

            const tx = getTransactionDecoder().decode(txBytes);
            const message = getCompiledTransactionMessageDecoder().decode(
                tx.messageBytes
            );

            console.log(
                `got new sig ${getSignatureFromTransaction(
                    tx
                ).toString()} on slot ${slot}`
            );
        } catch (e) {
            console.error("error processing message:", e);
        }
    });

    stream.on("error", (err) => {
        console.error("error receiving from sub:", err);
        process.exit(1);
    });

    stream.on("end", () => {
        console.error("stream ended");
        process.exit(0);
    });
}

main();

Usage Examples

# Log signature + slot for all filtered transactions
bun index.ts

Available Endpoints

Choose the endpoint closest to your location:

  • Frankfurt: fra.stream.nextblock.io:22221

  • Amsterdam: amsterdam.stream.nextblock.io:22221

  • London: london.stream.nextblock.io:22221

  • Singapore: singapore.stream.nextblock.io:22221

  • Tokyo: tokyo.stream.nextblock.io:22221

  • New York: ny.stream.nextblock.io:22221

  • Salt Lake City: slc.stream.nextblock.io:22221

Best Practices for Trading Bots

  1. Use insecure connections: Better performance with NextBlock's optimized network

  2. Filter efficiently: Only subscribe to programs you need to reduce bandwidth

  3. Handle reconnections: Implement automatic reconnection logic

  4. Process quickly: Avoid blocking the stream receiver

  5. Monitor performance: Track processing latency and throughput

  6. Implement graceful shutdown: Handle interrupt signals properly

  7. Use multiple endpoints: Implement redundancy for critical applications

Last updated