helius-laserstream SDK 实时跟踪插槽进展、区块最终化和网络性能指标。
先决条件: 本指南假设您已完成 LaserStream gRPC 快速入门 并拥有 API 密钥。
监控类型
- 插槽更新
- 区块数据
- 区块元数据
跟踪网络共识进展监控不同承诺级别的插槽进展:插槽数据包括: 插槽编号、父插槽、承诺状态 (
import { subscribe, CommitmentLevel, LaserstreamConfig, SubscribeRequest } from 'helius-laserstream';
const subscriptionRequest: SubscribeRequest = {
slots: {
slotSubscribe: {
filterByCommitment: false // Receive all commitment levels
}
},
commitment: CommitmentLevel.CONFIRMED,
accounts: {}, transactions: {}, transactionsStatus: {},
blocks: {}, blocksMeta: {}, entry: {}, accountsDataSlice: [],
};
processed / confirmed / finalized) 以及领导者信息。最佳用途: 网络健康监控、插槽计时分析、共识跟踪。
监控完整区块信息流播含事务和账户更新的完整区块:区块数据包括: 区块元数据、事务、账户更新、区块计时。
const subscriptionRequest: SubscribeRequest = {
blocks: {
blockSubscribe: {
accountInclude: [], // All accounts
includeTransactions: true,
includeAccounts: true,
includeEntries: false
}
},
commitment: CommitmentLevel.CONFIRMED,
accounts: {}, transactions: {}, transactionsStatus: {},
slots: {}, blocksMeta: {}, entry: {}, accountsDataSlice: [],
};
高流量: 完整区块流会生成大量数据。使用
accountInclude 过滤器减少流量。轻量级区块信息获取不含事务细节的区块元数据:元数据包括: 区块哈希、父哈希、插槽、高度、事务数、奖励。
const subscriptionRequest: SubscribeRequest = {
blocksMeta: {
blockMetaSubscribe: {}
},
commitment: CommitmentLevel.CONFIRMED,
accounts: {}, transactions: {}, transactionsStatus: {},
slots: {}, blocks: {}, entry: {}, accountsDataSlice: [],
};
高效: 相比整个区块流量更低的带宽。
实用示例
示例 1:网络健康监控器
跟踪插槽进展并识别网络问题:import { subscribe, CommitmentLevel, LaserstreamConfig, SubscribeRequest } from 'helius-laserstream';
let lastSlot = 0;
let lastTimestamp = Date.now();
const slotTimes: number[] = [];
// CommitmentLevel only ships the forward (name → number) mapping, so we keep
// a small reverse lookup for the numeric status the SDK returns on slot updates.
const STATUS_NAMES = ['PROCESSED', 'CONFIRMED', 'FINALIZED'] as const;
async function monitorNetworkHealth() {
const subscriptionRequest: SubscribeRequest = {
slots: {
slotSubscribe: {
filterByCommitment: true // Track processed commitment levels
}
},
commitment: CommitmentLevel.PROCESSED,
accounts: {}, transactions: {}, transactionsStatus: {},
blocks: {}, blocksMeta: {}, entry: {}, accountsDataSlice: [],
};
const config: LaserstreamConfig = {
apiKey: 'YOUR_API_KEY',
endpoint: 'https://laserstream-mainnet-ewr.helius-rpc.com',
};
await subscribe(config, subscriptionRequest, async (data) => {
if (!data.slot) return;
const slot = data.slot;
// The SDK returns u64 fields as strings to preserve precision.
const currentSlot = Number(slot.slot);
const currentTime = Date.now();
console.log(`\n📊 Slot Update:`);
console.log(` Slot: ${currentSlot}`);
console.log(` Parent: ${slot.parent}`);
// slot.status is a numeric enum (0=processed, 1=confirmed, 2=finalized).
console.log(` Status: ${STATUS_NAMES[slot.status] ?? slot.status}`);
if (lastSlot > 0) {
const slotDiff = currentSlot - lastSlot;
const timeDiff = currentTime - lastTimestamp;
if (slotDiff === 1) {
slotTimes.push(timeDiff);
if (slotTimes.length > 100) slotTimes.shift();
const avg = slotTimes.reduce((a, b) => a + b, 0) / slotTimes.length;
console.log(` Slot Time: ${timeDiff}ms`);
console.log(` Avg Slot Time: ${avg.toFixed(1)}ms`);
if (timeDiff > 800) {
console.log(` ⚠️ SLOW SLOT: ${timeDiff}ms (normal ~400ms)`);
}
} else if (slotDiff > 1) {
console.log(` ⚠️ SKIPPED ${slotDiff - 1} SLOTS`);
}
}
lastSlot = currentSlot;
lastTimestamp = currentTime;
}, async (error) => {
console.error('Stream error:', error);
});
}
monitorNetworkHealth().catch(console.error);
示例 2:区块生产监控器
跟踪区块生产和交易量:async function monitorBlockProduction() {
const subscriptionRequest: SubscribeRequest = {
blocksMeta: {
blockMetaSubscribe: {}
},
commitment: CommitmentLevel.CONFIRMED,
accounts: {}, transactions: {}, transactionsStatus: {},
slots: {}, blocks: {}, entry: {}, accountsDataSlice: [],
};
const config: LaserstreamConfig = {
apiKey: 'YOUR_API_KEY',
endpoint: 'https://laserstream-mainnet-ewr.helius-rpc.com',
};
await subscribe(config, subscriptionRequest, async (data) => {
if (!data.blockMeta) return;
const blockMeta = data.blockMeta;
console.log(`\n🧱 Block Produced:`);
console.log(` Slot: ${blockMeta.slot}`);
// blockHeight is a wrapper object: { blockHeight: '397657352' }
console.log(` Block Height: ${blockMeta.blockHeight?.blockHeight}`);
console.log(` Block Hash: ${blockMeta.blockhash}`);
console.log(` Parent Slot: ${blockMeta.parentSlot}`);
console.log(` Parent Hash: ${blockMeta.parentBlockhash}`);
console.log(` Transactions: ${blockMeta.executedTransactionCount}`);
console.log(` Entries: ${blockMeta.entriesCount}`);
if (blockMeta.blockTime?.timestamp) {
// blockTime.timestamp is a u64 as a string (Unix seconds).
console.log(` Block Time: ${new Date(Number(blockMeta.blockTime.timestamp) * 1000).toISOString()}`);
}
// rewards is a wrapper object: { rewards: [...], numPartitions: number | null }
if (blockMeta.rewards?.rewards?.length > 0) {
console.log(` Rewards:`);
blockMeta.rewards.rewards.forEach((r: any) => {
console.log(` ${r.pubkey}: ${r.lamports} lamports (${r.rewardType})`);
});
}
if (Number(blockMeta.executedTransactionCount) > 3000) {
console.log(` 🔥 HIGH ACTIVITY: ${blockMeta.executedTransactionCount} transactions`);
}
}, async (error) => {
console.error('Stream error:', error);
});
}
示例 3:过滤区块监控器
监控包含特定程序活动的区块:async function monitorDEXBlocks() {
const subscriptionRequest: SubscribeRequest = {
blocks: {
blockSubscribe: {
accountInclude: [
"675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8", // Raydium
"CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK", // Raydium CLMM
"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4" // Jupiter
],
includeTransactions: true,
includeAccounts: false,
includeEntries: false
}
},
commitment: CommitmentLevel.CONFIRMED,
accounts: {}, transactions: {}, transactionsStatus: {},
slots: {}, blocksMeta: {}, entry: {}, accountsDataSlice: [],
};
const config: LaserstreamConfig = {
apiKey: 'YOUR_API_KEY',
endpoint: 'https://laserstream-mainnet-ewr.helius-rpc.com',
};
await subscribe(config, subscriptionRequest, async (data) => {
if (!data.block) return;
const block = data.block;
let successfulDexTx = 0;
let totalFees = 0; // lamports
block.transactions?.forEach((tx: any) => {
if (tx.meta && !tx.meta.err) {
successfulDexTx++;
// tx.meta.fee is a u64 string — coerce before adding.
totalFees += Number(tx.meta.fee ?? 0);
}
});
console.log(`\n🔄 DEX Activity Block:`);
console.log(` Slot: ${block.slot}`);
console.log(` Block Height: ${block.blockHeight?.blockHeight}`);
console.log(` Block Hash: ${block.blockhash}`);
console.log(` Total transactions in block: ${block.executedTransactionCount}`);
console.log(` Matched DEX transactions: ${block.transactions?.length ?? 0}`);
console.log(` Successful DEX transactions: ${successfulDexTx}`);
if (successfulDexTx > 0) {
console.log(` Total Fees: ${(totalFees / 1e9).toFixed(4)} SOL`);
console.log(` Avg Fee: ${(totalFees / successfulDexTx / 1e9).toFixed(6)} SOL`);
}
}, async (error) => {
console.error('Stream error:', error);
});
}
数据结构
插槽数据结构
插槽数据结构
{
slot: string; // Current slot number (u64 as string)
parent: string; // Parent slot number (u64 as string)
status: number; // CommitmentLevel enum: 0 = processed, 1 = confirmed, 2 = finalized
}
processed (初始)、confirmed (超多数投票)、finalized (不可逆)。u64 字段(插槽、父级)以字符串形式传送,以在
Number.MAX_SAFE_INTEGER 之后保持精度。需要进行算术运算时请使用 Number(slot.slot) 进行转换。status 是一个数字枚举 — 使用 CommitmentLevel[slot.status] 获取可读名称。区块元数据结构
区块元数据结构
{
slot: string; // u64 as string
blockhash: string;
rewards: {
rewards: Array<{
pubkey: string;
lamports: string; // u64 as string
rewardType: string; // "fee" | "rent" | "voting" | "staking"
}>;
numPartitions: number | null;
};
blockTime: { timestamp: string }; // Unix seconds as a u64 string
blockHeight: { blockHeight: string }; // u64 as string, wrapped
parentSlot: string; // u64 as string
parentBlockhash: string;
executedTransactionCount: string; // u64 as string
entriesCount: string; // u64 as string
}
数字字段(slot, parentSlot, executedTransactionCount, entriesCount,
blockHeight 和 blockTime 内的值)都以字符串形式输出,因为它们在底层 proto 中是 u64。对于算术或比较,请将它们包装在 Number(...) 中。完整区块结构
完整区块结构
{
slot: string; // u64 as string
blockhash: string;
rewards: {
rewards: Array<{
pubkey: string;
lamports: string; // u64 as string
rewardType: string;
}>;
numPartitions: number | null;
};
blockTime: { timestamp: string }; // Unix seconds (u64 string)
blockHeight: { blockHeight: string }; // u64 as string, wrapped
parentSlot: string; // u64 as string
parentBlockhash: string;
executedTransactionCount: string; // total executed tx in the block (u64 as string)
updatedAccountCount: string; // total account updates in the block (u64 as string)
entriesCount: string; // u64 as string
transactions: Array<{
signature: Buffer; // base58-encode for display
isVote: boolean;
transaction: TransactionMessage; // full transaction payload
meta: TransactionMeta; // execution metadata (fee, err, balances, …)
index: string; // u64 as string
}>;
accounts: AccountUpdate[]; // populated when includeAccounts: true
entries: Entry[]; // populated when includeEntries: true
}
Number(...) 以进行算术。每个事务中,meta.fee、meta.preBalances、meta.postBalances 等也是字符串。性能考虑
插槽监控
轻量级:带宽极低,处理开销最小。适用于监控仪表板。
区块元数据
平衡:中等带宽,区块级洞察,无完整数据。适合分析。
完整区块
高流量:完整交易数据,需要强大的处理能力。始终与过滤器配对使用。
过滤区块
优化:使用
accountInclude,禁用不需要的 includeAccounts/includeEntries。使用案例
- 网络监控
- 分析与指标
- 应用同步
跟踪网络健康和性能 —— 插槽计时、拥堵、共识。
const targetSlotTime = 400; // ms
const tolerance = 200; // ms
if (Math.abs(slotTime - targetSlotTime) > tolerance) {
console.log(`Network performance issue detected`);
}
收集区块链分析数据 —— 交易量、费用分析、区块大小、活动模式。
const dailyStats = {
date: new Date().toDateString(),
totalTransactions: 0,
totalFees: 0,
blockCount: 0
};
使应用与网络保持同步 —— 以插槽为基础的更新、区块确认。
if (data.slot && data.slot.status === 'finalized') {
updateApplicationState(data.slot.slot);
}
错误处理
缺失插槽
缺失插槽
高流量
高流量
症状: 区块数据过多。解决方案: 使用区块元数据代替完整区块;应用账户过滤器;禁用不必要的包含项(条目、账户);异步处理。
计时问题
计时问题
症状: 插槽计时不一致。分析: 计算移动平均值;跟踪偏差;监控网络健康指标;与验证器性能相关联。
最佳实践
生产指南:
- 从元数据开始 — 在订阅完整区块之前使用区块元数据
- 应用过滤器 — 使用
accountInclude删除不相关数据 - 监控计时 — 将插槽进展作为网络健康指示灯
- 处理空缺 — 结合 历史重播,使在重新连接时自动回填缺失插槽
- 异步处理 — 不要通过繁重的计算阻塞流处理
- 匹配承诺与需求 —
processed用于低延迟接口,confirmed/finalized用于状态写入