What Are Entries?
Entries are the fundamental execution units within a Solana block. Each block is composed of one or more entries, where each entry contains a batch of transactions that were executed sequentially. Entries represent the most granular level of block data — below blocks and above individual transactions.
Monitoring entries is useful for:
- Building custom block parsers that need precise transaction ordering
- Reconstructing execution order within a block
- Low-latency detection of specific transactions within a slot
For most use cases, transaction or block subscriptions are simpler. Use entry monitoring when you need sub-block granularity or are building infrastructure that processes entries directly (e.g. a custom ledger parser).
Filter Parameters
Entry subscriptions do not support content-based filters — you receive all entries for the subscribed commitment level. The only configuration is the commitment level set on the subscription request.
Example: Stream All Entries
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 PING_INTERVAL_MS = 30_000;
const client = new Client(GRPC_URL, X_TOKEN, {
"grpc.max_receive_message_length": 1024 * 1024 * 1024, // 1 GiB
});
async function main() {
const stream = await client.subscribe();
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());
});
stream.on("data", (data) => {
if (data.entry) {
const { slot, index, numHashes, hash, transactions, startingTransactionIndex } = data.entry;
console.log(`Entry [slot ${slot}, index ${index}]:`);
console.log(` Hash: ${Buffer.from(hash).toString("base64")}`);
console.log(` Num hashes: ${numHashes}`);
console.log(` Transactions in entry: ${transactions?.length ?? 0}`);
console.log(` Starting tx index: ${startingTransactionIndex}`);
} else if (data.pong) {
console.log("Pong received");
}
});
const request: SubscribeRequest = {
entry: {
entryStream: {},
},
commitment: CommitmentLevel.PROCESSED,
accounts: {},
accountsDataSlice: [],
transactions: {},
transactionsStatus: {},
slots: {},
blocks: {},
blocksMeta: {},
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: {} }, () => {});
}, PING_INTERVAL_MS);
await streamClosed;
}
main();
Entry Data Fields
| Field | Type | Description |
|---|
slot | uint64 | The slot this entry belongs to |
index | uint64 | Position of this entry within the slot (0-indexed) |
numHashes | uint64 | Number of PoH hashes since the previous entry |
hash | bytes | PoH hash for this entry |
transactions | ConfirmedTransaction[] | Transactions included in this entry |
startingTransactionIndex | uint64 | Index of the first transaction in this entry within the block |
Combining Entries with Other Subscriptions
Entries are rarely subscribed to in isolation. A common pattern is to combine an entry subscription with a slot subscription to know when a slot is finalized and then process its entries:
const request: SubscribeRequest = {
entry: {
entryStream: {},
},
slots: {
slotUpdates: {
filterByCommitment: true,
},
},
commitment: CommitmentLevel.CONFIRMED,
accounts: {},
accountsDataSlice: [],
transactions: {},
transactionsStatus: {},
blocks: {},
blocksMeta: {},
};
This lets you buffer entries per slot and process them atomically once the slot reaches your target commitment level.