Team Shyft
· January 24, 2026

When building production Solana applications on Yellowstone gRPC, your subscription needs can change mid-stream — a new token launches, a wallet surges in activity, or your users switch what they’re tracking.
The naive approach is to disconnect and reconnect with a new SubscribeRequest. But that creates a data gap, adds latency, and adds unnecessary complexity to your reconnect logic.
In this guide, you’ll learn how to dynamically update a Yellowstone gRPC SubscribeRequest mid-stream using stream.write() — no disconnection, no missed events, no data gaps.
What’s covered:
stream.write() enables live subscription updates.To get started, we will need a few things.
Shyft’s Yellowstone gRPC nodes are available in various locations all across EU and the US region. To access, we would require a region-specific gRPC endpoint and an access token, which is available to purchase on your Shyft Dashboard.
As gRPC services are unsupported in web-browsers, you would need a backend application such as C#, Go, Java, Python etc. to receive gRPC data.
The complete code for this article is available in our docs and on Replit — feel free to explore and test it out. We’ve also shared a collection of example use cases covering gRPC and DeFi on GitHub, which you can clone and experiment with.
Here is an example, which uses the client.subscribe() request to send a subscription, and the uses another client.write() to write the same subscribe request.
import Client, {
CommitmentLevel,
SubscribeRequestAccountsDataSlice,
SubscribeRequestFilterAccounts,
SubscribeRequestFilterBlocks,
SubscribeRequestFilterBlocksMeta,
SubscribeRequestFilterEntry,
SubscribeRequestFilterSlots,
SubscribeRequestFilterTransactions,
} from "@triton-one/yellowstone-grpc";
import { SubscribeRequestPing } from "@triton-one/yellowstone-grpc/dist/grpc/geyser";
interface SubscribeRequest {
accounts: { [key: string]: SubscribeRequestFilterAccounts };
slots: { [key: string]: SubscribeRequestFilterSlots };
transactions: { [key: string]: SubscribeRequestFilterTransactions };
transactionsStatus: { [key: string]: SubscribeRequestFilterTransactions };
blocks: { [key: string]: SubscribeRequestFilterBlocks };
blocksMeta: { [key: string]: SubscribeRequestFilterBlocksMeta };
entry: { [key: string]: SubscribeRequestFilterEntry };
commitment?: CommitmentLevel;
accountsDataSlice: SubscribeRequestAccountsDataSlice[];
ping?: SubscribeRequestPing;
}
const subscribedWalletsA: string[] = [
"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
"5n2WeFEQbfV65niEP63sZc3VA7EgC4gxcTzsGGuXpump",
"4oJh9x5Cr14bfaBtUsXN1YUZbxRhuae9nrkSyWGSpump",
"GBpE12CEBFY9C74gRBuZMTPgy2BGEJNCn4cHbEPKpump",
"oraim8c9d1nkfuQk9EzGYEUGxqL3MHQYndRw1huVo5h",
];
const subscribedWalletsB: string[] = [
"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P",
];
const subscribeRequest1: SubscribeRequest = {
accounts: {},
slots: {},
transactions: {
modifying_A: {
vote: false,
failed: false,
signature: undefined,
accountInclude: subscribedWalletsA,
accountExclude: [],
accountRequired: [],
},
},
transactionsStatus: {},
entry: {},
blocks: {},
blocksMeta: {},
accountsDataSlice: [],
ping: undefined,
commitment: CommitmentLevel.PROCESSED,
};
// Subscribes to account changes for program-owned accounts of subscribedWalletsB
const subscribeRequest2: SubscribeRequest = {
accounts: {
modifying_B: {
account: [],
filters: [],
owner: subscribedWalletsB,
},
},
slots: {},
transactions: {},
transactionsStatus: {},
blocks: {},
blocksMeta: {
block: [],
},
entry: {},
accountsDataSlice: [],
ping: undefined,
commitment: CommitmentLevel.PROCESSED,
};
/**
* Dynamically updates the current stream subscription with new request parameters.
*/
async function updateSubscription(stream: any, args: SubscribeRequest) {
try {
stream.write(args);
} catch (error) {
console.error("Failed to send updated subscription request:", error);
}
}
/**
* Handles a single streaming session.
* Automatically switches to a second subscription request after a timeout.
*/
async function handleStream(client: Client, args: SubscribeRequest) {
const stream = await client.subscribe();
// Waits for the stream to close or error out
const streamClosed = new Promise<void>((resolve, reject) => {
stream.on("error", (error) => {
console.error("Stream Error:", error);
reject(error);
stream.end();
});
stream.on("end", resolve);
stream.on("close", resolve);
});
// Automatically switch subscription after 10 seconds
setTimeout(async () => {
console.log("🔁 Switching to second subscription request...");
await updateSubscription(stream, subscribeRequest2);
}, 10000);
// Handle incoming data
stream.on("data", async (data) => {
try {
console.log("📦 Streamed Data:", data);
// You can add more processing logic here
} catch (error) {
console.error("Error processing stream data:", error);
}
});
// Send initial subscription request
await new Promise<void>((resolve, reject) => {
stream.write(args, (err: any) => (err ? reject(err) : resolve()));
}).catch((reason) => {
console.error("Initial stream write failed:", reason);
throw reason;
});
await streamClosed;
}
/**
* Starts the stream and continuously attempts to reconnect on errors.
*/
async function subscribeCommand(client: Client, args: SubscribeRequest) {
while (true) {
try {
await handleStream(client, args);
} catch (error) {
console.error("Stream error. Retrying in 1 second...", error);
await new Promise((resolve) => setTimeout(resolve, 1000));
}
}
}
const client = new Client("YOUR-GRPC-URL", "YOUR-GRPC-TOKEN", undefined);
// Start streaming with the first subscription
subscribeCommand(client, subscribeRequest1);
In the above illustrated example we can see that we have two different subscribe requests. The stream begins with subscribeRequest1, which listens for transactions involving a predefined set of wallet addresses (subscribedWalletsA).
“Build with Confidence. Our Dedicated Yellowstone gRPC Nodes are purpose-built for peak uptime and blazing-fast throughput—shred-accelerated nodes with resilient DNS and load balancing at their core.”
After 10 seconds, the code calls updateSubscription() with subscribeRequest2, which switches the stream to monitor accounts owned by another wallet set (subscribedWalletsB) and starts listening for blocksMeta.
This is done without ending or restarting the stream, thanks to the stream.write() method which sends a new SubscribeRequest mid-connection.
The handleStream() function listens for error, close, or end events. If the stream breaks, the subscribeCommand() wrapper retries the connection after a 1-second delay—ensuring resilience in unstable network conditions or during backend hiccups.
If you’ve mastered dynamic gRPC filters but need even faster raw data, check out Rabbitstream for shred-level speed with the same filtering precision.
Modifying a SubscribeRequest mid-stream in Solana Yellowstone gRPC is a critical feature for building responsive, production-ready applications that rely on real-time blockchain data. Here’s why this approach is so powerful:
In case you missed out, the complete code for this article is available in Shyft docs and on Replit — feel free to explore and test it out. We’ve also shared a collection of example use cases covering gRPC and DeFi on GitHub, which you can clone and experiment with.
You can explore our other related articles: Streaming Real-Time Data on Solana, Real-Time Data Streaming with gRPC: Accounts, Transactions, Blocks, How to Stream Real-Time Pump.fun Updates on Solana, and Tracking New Pools on Raydium.

In this article you will learn how to implement a reconnect logic for your Solana gRPC streams with replay functionality...
January 24, 2026

A comprehensive guide on how to stream real-time Solana accounts, transactions, and block updates using gRPC. Step-by-st...
January 22, 2026

In this blog, we’ll guide you through the process of getting a token’s liquidity from all major dexes with Shyft’s Solan...
January 22, 2026
Get in touch with our discord community and keep up with the latest feature
releases. Get help from our developers who are always here to help you take off.