in-solana by Supanode

Solana gRPC in TypeScript: Examples

TL;DR

  • - Use @triton-one/yellowstone-grpc as the TypeScript gRPC client
  • - Connect with endpoint URL and optional auth token
  • - Subscribe by sending a filter object on the bidirectional stream
  • - Handle reconnection logic for production reliability

The @triton-one/yellowstone-grpc package provides a TypeScript client for Yellowstone gRPC. It handles protobuf serialization and provides typed interfaces for all subscription filters.

Installation

npm install @triton-one/yellowstone-grpc

Connecting

import Client from "@triton-one/yellowstone-grpc";

const client = new Client(
  "https://your-grpc-endpoint.example.com",
  "your-auth-token"
);
const stream = await client.subscribe();

The client establishes a bidirectional gRPC stream. You send filter requests and receive matching updates on the same connection.

Subscribing to account updates

stream.on("data", (data) => {
  if (data.account) {
    const account = data.account;
    console.log("Account update:", account.account.pubkey);
    console.log("Data length:", account.account.data.length);
  }
});

await stream.write({
  accounts: {
    tokenAccounts: {
      owner: ["TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"],
      account: [],
      filters: [],
    },
  },
  slots: {},
  transactions: {},
  blocks: {},
  blocksMeta: {},
  commitment: 1, // CONFIRMED
});

Subscribing to transactions

await stream.write({
  accounts: {},
  slots: {},
  transactions: {
    swaps: {
      vote: false,
      failed: false,
      accountInclude: [
        "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8", // Raydium AMM
      ],
      accountExclude: [],
    },
  },
  blocks: {},
  blocksMeta: {},
  commitment: 0, // PROCESSED
});

Handling disconnection

gRPC streams disconnect due to network issues, server restarts, or idle timeouts. Implement reconnection:

function connectWithRetry(maxRetries = 10) {
  let retries = 0;

  async function connect() {
    const stream = await client.subscribe();

    stream.on("error", async (err) => {
      console.error("Stream error:", err.message);
      if (retries < maxRetries) {
        retries++;
        const delay = Math.min(1000 * 2 ** retries, 30000);
        setTimeout(connect, delay);
      }
    });

    stream.on("end", () => {
      console.log("Stream ended, reconnecting...");
      if (retries < maxRetries) {
        retries++;
        setTimeout(connect, 1000);
      }
    });

    // Reset retry counter on successful data
    stream.on("data", () => {
      retries = 0;
    });

    // Send subscription filters
    await stream.write({ /* your filters */ });
  }

  connect();
}

FAQ

What commitment levels are available? 0 = PROCESSED, 1 = CONFIRMED, 2 = FINALIZED. See commitment levels in streaming.

Can I change filters without reconnecting? Yes. Send a new write() with updated filters. The server replaces the active subscription.

How do I decode transaction data? The response includes raw transaction bytes. Use @solana/kit to deserialize the transaction and parse instructions.

Updated: