Documentation Index
Fetch the complete documentation index at: https://www.helius.dev/docs/llms.txt
Use this file to discover all available pages before exploring further.
公开测试版。 预处理交易是 Helius Shred Delivery 产品系列的一部分。对于任何专业版计划或更高级别的用户均可使用,以1 MB 20 个积分的速度计费。通常情况下,预处理交易比 processed 确认级别提前 ~8 毫秒 到达,但代价是执行元数据(参见下面的 权衡)。
预处理交易是通过 gRPC 接收 Solana 交易的最快方式。Helius 直接在验证器接收到分片时对其进行解码,并将生成的交易发送给您,让您比任何执行后订阅在数毫秒之前获取交易数据——平均比 processed 确认级别提前约 8 毫秒。
本指南解释了何时使用预处理交易、可用的数据以及如何使用 LaserStream SDK 订阅它们(该 SDK 也通过相同的 gRPC 连接提供预处理交易)。
预处理交易在生命周期中的位置
在 Solana 的架构中,交易在完全处理之前需要经过几个阶段:
- 分片接收 → 验证器接收交易分片(数据碎片)。← Helius 的 原始分片 (UDP) 在此处传递。
- 分片解码 → 分片被解码为原始交易。← 在此处可用的预处理交易。
- 交易执行 → 交易由运行时执行。
- 元数据生成 → 计算前后余额、日志和错误信息。
- 确认 → 交易达到处理/确认/最终状态。← LaserStream gRPC 和 LaserStream WebSocket 在此处传递。
执行后订阅在第5阶段传递数据——在完整执行和元数据生成之后。预处理订阅在第2阶段传递——解码shreds后立即执行,未完成时。
权衡: 你可以更早几毫秒获得交易数据,但没有执行元数据,如余额变化、日志或错误信息。
**这是一个仅限交易的数据流。**账户和程序状态更新要等到运行时执行交易(第4阶段)才能存在。如果你需要实时账户或程序更新——例如代币余额、绑定曲线状态、程序账户,或任何不只是原始交易的内容——请使用 LaserStream gRPC 在 processed 承诺中,这是接收账户/程序更改的最快方式。
尽力交付保证
预处理交易交付是尽力而为的,不是保证的。我们目标是99.99%的交付率,但一些交易可能会在以下情况下丢失:
- 基础设施更新和重新部署
- 网络问题或验证者连接问题
- 解码或处理shred的边缘情况
对于需要保证交付的关键应用,使用标准 交易订阅。
可用数据是什么?
预处理交易包含完整的交易消息,但缺乏执行元数据:
可用数据
- ✅ 交易签名 - 唯一的交易标识符
- ✅ 账户密钥 - 交易引用的所有账户
- ✅ 指令 - 完整的指令数据和程序调用
- ✅ 最近的区块哈希 - 交易到期参考
- ✅ 签名 - 所有交易签名
- ✅ 是否为投票交易 - 是否这是投票交易
- ✅ 槽位号码 - 包含此交易的槽位
缺失数据
- ❌ 交易元数据 - 代币余额变化、前/后余额、交易状态
- ❌ 交易错误 - 我们无法判断交易是否失败
- ❌ 内部指令 - 不包括跨程序调用(CPIs)
- ❌ 日志信息 - 程序在执行过程中生成的日志
- ❌ 消耗的计算单元 - 执行指标不可用
将预处理交易视为收到“提案”而没有“结果”。你可以看到用户试图做什么,但看不到实际发生的情况。
SDK支持和版本要求
所有LaserStream SDK都支持预处理交易订阅:
JavaScript/TypeScript
版本 0.2.8 或更高
实现示例
JavaScript/TypeScript
JavaScript SDK 提供了一个专用的 subscribePreprocessed 函数,具备自动重连功能:
import {
subscribePreprocessed,
CommitmentLevel,
LaserstreamConfig,
SubscribePreprocessedRequest,
SubscribePreprocessedUpdate
} from 'helius-laserstream';
import bs58 from 'bs58';
async function streamPreprocessedTransactions() {
const config: LaserstreamConfig = {
apiKey: 'YOUR_API_KEY',
endpoint: 'https://laserstream-mainnet-ewr.helius-rpc.com',
};
const request: SubscribePreprocessedRequest = {
transactions: {
"jupiter-swaps": {
vote: false,
accountInclude: ['JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4']
}
}
};
const stream = await subscribePreprocessed(
config,
request,
async (update: SubscribePreprocessedUpdate) => {
if (update.transaction) {
const tx = update.transaction;
const signature = bs58.encode(tx.transaction.signature);
console.log('⚡ Preprocessed transaction received:');
console.log(` Signature: ${signature}`);
console.log(` Slot: ${tx.slot}`);
console.log(` Is Vote: ${tx.transaction.isVote}`);
console.log(` Filters: ${update.filters.join(', ')}`);
console.log('---');
}
},
async (error) => {
console.error('Stream error:', error);
}
);
console.log(`✅ Preprocessed stream started (id: ${stream.id})`);
// Graceful shutdown
process.on('SIGINT', () => {
console.log('\n🛑 Shutting down stream...');
stream.cancel();
process.exit(0);
});
}
streamPreprocessedTransactions().catch(console.error);
完整示例: preprocessed-transaction-sub.ts
Rust
Rust SDK 提供原生性能:
use futures::StreamExt;
use helius_laserstream::{
grpc::{SubscribePreprocessedRequest, SubscribePreprocessedRequestFilterTransactions},
subscribe_preprocessed, LaserstreamConfig,
};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = LaserstreamConfig {
endpoint: "https://laserstream-mainnet-ewr.helius-rpc.com".to_string(),
api_key: "YOUR_API_KEY".to_string(),
..Default::default()
};
let mut request = SubscribePreprocessedRequest::default();
request.transactions.insert(
"jupiter-swaps".to_string(),
SubscribePreprocessedRequestFilterTransactions {
vote: Some(false),
account_include: vec![
"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4".to_string()
],
..Default::default()
},
);
let (stream, _handle) = subscribe_preprocessed(config, request);
tokio::pin!(stream);
println!("✅ Preprocessed stream started");
while let Some(result) = stream.next().await {
match result {
Ok(update) => {
if let Some(tx) = update.transaction {
println!("⚡ Preprocessed transaction:");
println!(" Slot: {}", tx.slot);
println!(" Is Vote: {}", tx.transaction.is_vote);
println!("---");
}
}
Err(e) => {
eprintln!("Stream error: {:?}", e);
break;
}
}
}
Ok(())
}
完整示例: preprocessed_transaction_sub.rs
Go SDK 提供惯用的 Go 接口:
package main
import (
"log"
"os"
"os/signal"
"syscall"
laserstream "github.com/helius-labs/laserstream-sdk/go"
pb "github.com/helius-labs/laserstream-sdk/go/proto"
)
func main() {
log.SetFlags(0)
clientConfig := laserstream.LaserstreamConfig{
Endpoint: "https://laserstream-mainnet-ewr.helius-rpc.com",
APIKey: "YOUR_API_KEY",
}
voteFilter := false
subscriptionRequest := &pb.SubscribePreprocessedRequest{
Transactions: map[string]*pb.SubscribePreprocessedRequestFilterTransactions{
"jupiter-swaps": {
Vote: &voteFilter,
AccountInclude: []string{
"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4",
},
},
},
}
client := laserstream.NewPreprocessedClient(clientConfig)
dataCallback := func(data *pb.SubscribePreprocessedUpdate) {
if data.Transaction != nil {
log.Println("⚡ Preprocessed transaction:")
log.Printf(" Slot: %d\n", data.Transaction.Slot)
log.Printf(" Is Vote: %t\n", data.Transaction.Transaction.IsVote)
log.Println("---")
}
}
errorCallback := func(err error) {
log.Printf("Error: %v", err)
}
err := client.Subscribe(subscriptionRequest, dataCallback, errorCallback)
if err != nil {
log.Fatalf("Failed to subscribe: %v", err)
}
log.Println("✅ Preprocessed stream started")
log.Println("Press Ctrl+C to exit")
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
<-sigChan
log.Println("\nShutting down...")
client.Close()
}
完整示例: preprocessed-transaction-sub.go
订阅结构和过滤
请求结构
预处理订阅请求遵循与标准订阅类似的结构,但具有一组集中的过滤器:
interface SubscribePreprocessedRequest {
transactions: {
[filterName: string]: SubscribePreprocessedRequestFilterTransactions
};
ping?: SubscribeRequestPing;
}
interface SubscribePreprocessedRequestFilterTransactions {
vote?: boolean; // Include/exclude vote transactions
signature?: string; // Filter by specific transaction signature
accountInclude?: string[]; // Include transactions touching these accounts
accountExclude?: string[]; // Exclude transactions touching these accounts
accountRequired?: string[]; // Require all these accounts to be present
}
响应结构
更新包含完整的交易消息和基本元数据:
interface SubscribePreprocessedUpdate {
filters: string[]; // Which filters matched
transaction?: SubscribePreprocessedTransaction; // The transaction data
ping?: SubscribeUpdatePing; // Keepalive ping
pong?: SubscribeUpdatePong; // Ping response
createdAt: Date; // When update was created
}
interface SubscribePreprocessedTransaction {
transaction: SubscribePreprocessedTransactionInfo;
slot: number; // Slot containing transaction
}
interface SubscribePreprocessedTransactionInfo {
signature: Uint8Array; // Transaction signature
isVote: boolean; // Is this a vote transaction
transaction: solana.storage.Transaction; // Full transaction message
}
transaction.transaction 字段包含完整的 Solana 交易结构,包括:
- 消息 - 帐户密钥、指令、最近的区块哈希
- 签名 - 所有交易签名
- 地址表查找 - 用于版本化交易
这与标准订阅中的交易结构相同,但没有meta字段包含执行结果。