Overview
Yellowstone gRPC provides two related but distinct subscription types for tracking network progress:
- Slots — lightweight updates indicating when a slot reaches
processed, confirmed, or finalized commitment. Low bandwidth, useful for consensus monitoring.
- Blocks — full block data including transactions, account updates, and entries. Higher bandwidth, useful for indexers and analytics.
Slot Subscriptions
Filter Parameters
| Parameter | Type | Description |
|---|
filterByCommitment | boolean | When true, only emit slot updates that match the subscription’s commitment level. When false (default), emit updates for all commitment levels. |
interslotUpdates | boolean | When true, emit intra-slot state changes, not just slot boundaries. Useful for granular real-time monitoring. |
Example: Track Confirmed Slots
import Client, { CommitmentLevel, SubscribeRequest } from "@triton-one/yellowstone-grpc";
const GRPC_URL = "https://your-endpoint.grpc.orbitflare.com";
const X_TOKEN = "YOUR_GRPC_TOKEN";
const client = new Client(GRPC_URL, X_TOKEN, {
"grpc.max_receive_message_length": 64 * 1024 * 1024,
});
async function main() {
const stream = await client.subscribe();
const streamClosed = new Promise<void>((resolve, reject) => {
stream.on("error", (error) => { reject(error); stream.end(); });
stream.on("end", () => resolve());
stream.on("close", () => resolve());
});
stream.on("data", (data) => {
if (data.slot) {
const { slot, parent, status } = data.slot;
console.log(`Slot ${slot} (parent: ${parent}) — status: ${status}`);
} else if (data.pong) {
console.log("Pong received");
}
});
const request: SubscribeRequest = {
slots: {
slotUpdates: {
filterByCommitment: true,
},
},
commitment: CommitmentLevel.CONFIRMED,
accounts: {},
accountsDataSlice: [],
transactions: {},
transactionsStatus: {},
blocks: {},
blocksMeta: {},
entry: {},
ping: { id: 1 },
};
await new Promise<void>((resolve, reject) => {
stream.write(request, (err) => {
if (err == null) resolve(); else reject(err);
});
});
setInterval(() => {
stream.write({ ping: { id: 1 }, accounts: {}, accountsDataSlice: [], transactions: {}, transactionsStatus: {}, slots: {}, blocks: {}, blocksMeta: {}, entry: {} }, () => {});
}, 30_000);
await streamClosed;
}
main();
Block Subscriptions
Filter Parameters
| Parameter | Type | Description |
|---|
accountInclude | string[] | Filter transactions and account updates within blocks to only include these accounts. |
includeTransactions | boolean | Include full transaction data in each block update. |
includeAccounts | boolean | Include account state updates in each block update. |
includeEntries | boolean | Include entries (execution units) in each block update. |
Block subscriptions with includeTransactions: true and no account filter produce very high data volumes. Use accountInclude to narrow the stream to accounts you care about, or combine with a dedicated node for sustained throughput.
Example: Stream Blocks with Transactions
import Client, { CommitmentLevel, SubscribeRequest } from "@triton-one/yellowstone-grpc";
const client = new Client(
"https://your-endpoint.grpc.orbitflare.com",
"YOUR_GRPC_TOKEN",
{ "grpc.max_receive_message_length": 1024 * 1024 * 1024 }
);
async function main() {
const stream = await client.subscribe();
const streamClosed = new Promise<void>((resolve, reject) => {
stream.on("error", (error) => { reject(error); stream.end(); });
stream.on("end", () => resolve());
stream.on("close", () => resolve());
});
stream.on("data", (data) => {
if (data.block) {
const { block } = data;
console.log(`Block slot: ${block.slot}`);
console.log(` Blockhash: ${block.blockhash}`);
console.log(` Transactions: ${block.transactions?.length ?? 0}`);
}
});
const request: SubscribeRequest = {
blocks: {
blockStream: {
accountInclude: [
"675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8", // Raydium AMM
],
includeTransactions: true,
includeAccounts: false,
includeEntries: false,
},
},
commitment: CommitmentLevel.CONFIRMED,
accounts: {},
accountsDataSlice: [],
transactions: {},
transactionsStatus: {},
slots: {},
blocksMeta: {},
entry: {},
ping: { id: 1 },
};
await new Promise<void>((resolve, reject) => {
stream.write(request, (err) => {
if (err == null) resolve(); else reject(err);
});
});
setInterval(() => {
stream.write({ ping: { id: 1 }, accounts: {}, accountsDataSlice: [], transactions: {}, transactionsStatus: {}, slots: {}, blocks: {}, blocksMeta: {}, entry: {} }, () => {});
}, 30_000);
await streamClosed;
}
main();
Use blocksMeta to receive lightweight block headers (slot, blockhash, block time) without transaction payloads:
const request: SubscribeRequest = {
blocksMeta: {
blockMeta: {},
},
commitment: CommitmentLevel.FINALIZED,
accounts: {},
accountsDataSlice: [],
transactions: {},
transactionsStatus: {},
slots: {},
blocks: {},
entry: {},
};
Choosing Between Slots and Blocks
| Need | Use |
|---|
| Detect when a slot finalizes | Slot subscription (filterByCommitment: true) |
| Track block height progress | Slot subscription |
| Index all transactions in a block | Block subscription (includeTransactions: true) |
| Monitor account state changes per block | Block subscription (includeAccounts: true) |
| Minimal latency consensus signal | Slot subscription at processed |
| Full archive-quality block data | Block subscription at finalized |