Solana gRPC和Vixen:使用Rust实时监控Jupiter限价订单
本文详细介绍了如何使用 Rust、Solana gRPC 和 Vixen 框架构建实时 Jupiter 限价订单监控器。通过 Vixen v0.6.1 的 IDL 代码生成宏,从 Jupiter 限价订单 IDL 自动生成类型安全解析器,并利用新的 TransactionPrefilter 过滤失败交易。文章涵盖了项目设置、IDL 获取与转换、处理器实现、运行时构建及运行步骤,最终实现了订单创建、成交和取消事件的实时日志输出,并展示了如何获取人类可读的代币符号和金额。
概述
本指南使用 Solana gRPC(兼容 Yellowstone 的 Geyser gRPC)和 Vixen(一个基于 Rust 的 Solana 数据解析工具包),构建一个实时的 Jupiter 限价单监控器。该应用流式传输已确认的限价单交易,并以人类可读的 Token 数量和符号记录订单的挂单、成交和取消。在此过程中,本指南展示了 Vixen v0.6.1 的三个特性:通过过程宏基于 IDL 的解析器代码生成、TransactionPrefilter 中的 failed 字段(用于过滤掉已失败交易),以及用于可观测性的结构化追踪。
TL;DR
- 使用 Solana gRPC 和 Vixen,在 Rust 中构建一个实时的 Jupiter 限价单 v2 监控器
- 使用 Vixen 的
include_vixen_parser!过程宏,直接从 Jupiter 限价单 IDL 生成类型安全的解析器 - 使用 Vixen v0.6.1 的
TransactionPrefilter,仅流式传输已确认(非失败)的交易 - 以人类可读的 Token 符号和数量记录订单的挂单、成交和取消
- 在 Quicknode 的 Solana gRPC(Solana 主网)上运行
你将做什么
- 使用 Vixen v0.6.1 依赖项设置一个 Rust 项目
- 获取 Jupiter 限价单 v2 IDL 并将其转换为 Codama 格式
- 使用 Vixen 的过程宏生成类型安全的指令解析器
- 实现一个处理器,将限价单事件以人类可读的输出记录下来
- 构建并运行连接到 Quicknode Solana gRPC 端点的 Vixen 运行时
你需要什么
- 系统上安装了 Rust 和 Cargo
- Node.js(v18+)用于 Codama CLI 工具链
- 一个拥有 Solana gRPC 访问权限的 Quicknode 账户(包含在 Scale 和 Business 计划中,或可通过 Build 和 Accelerate 计划的 Solana gRPC 附加组件 获取)
- Anchor CLI 用于获取链上 IDL
- 对 Rust 和 Solana 程序的基本了解
- 熟悉 Solana gRPC:使用 Solana gRPC(Rust)监控 Solana 程序为 Solana gRPC 流式传输的工作原理提供了坚实基础
- 了解 Codama:如何使用 Codama 创建 Anchor 程序客户端涵盖了格式和 CLI
本指南还使用了以下包:
| 依赖项 | 版本 |
|---|---|
| rustc | 1.85.0 |
| yellowstone-vixen | 0.6.1 |
| yellowstone-vixen-core | 0.6.1 |
| yellowstone-vixen-parser | 0.6.1 |
| yellowstone-vixen-proc-macro | 0.6.1 |
| yellowstone-vixen-yellowstone-grpc-source | 0.6.1 |
| borsh | 1 |
| bs58 | 0.5 |
| chrono | 0.4 |
| reqwest | 0.12 |
| serde | 1 |
| clap | 4 |
| rustls | 0.23 |
| toml | 0.8 |
| tracing | 0.1 |
| tracing-subscriber | 0.3 |
什么是 Solana gRPC?
Solana gRPC 是一种基于 Geyser 插件系统的高性能 Solana 数据流式传输解决方案。它不是轮询 RPC 端点以获取新数据或管理 WebSocket 重连,而是将账户更新、交易和 Slot 通知作为连续流推送到你的应用程序。与传统的轮询方式相比,这种方式提供了更低的延迟和更高的吞吐量。
Quicknode 提供了一个托管的 Solana gRPC 端点。它包含在 Scale 和 Business 计划中;在 Build 和 Accelerate 计划中,可通过 Solana gRPC 附加组件 获取。你可以从 Quicknode 仪表板 在任何 Solana 端点上启用它,该仪表板会为你的应用程序提供专用的 gRPC 端点和身份验证 Token。
关键的过滤功能包括:
- 按程序地址过滤,仅接收涉及特定程序的交易
- 过滤掉失败的交易,仅处理已确认的链上事件
- 过滤掉投票交易以减少噪音
什么是 Vixen?
Vixen 是一个开源的 Rust 框架,用于在 Solana gRPC 之上构建 Solana 数据管道。它自动处理 gRPC 订阅、交易过滤和反序列化,因此应用程序代码接收的是类型化的 Rust 结构体,而不是原始字节。
Vixen 使用 Parser + Handler 架构将数据提取与业务逻辑分离:
- Parser:将原始交易数据反序列化为类型化的 Rust 结构体
- Handler:接收解析后的数据并实现你的应用程序逻辑
- Pipeline:将解析器连接到一个或多个处理器
- Runtime:管理 gRPC 连接、流订阅和管道执行
根据 Vixen v0.6.1 发布说明,该版本引入了三个面向生产的改进,本指南将进行演示:
- 通过过程宏的基于 IDL 的代码生成:使用
include_vixen_parser!直接从程序的 IDL 生成类型安全的解析器,消除了手动反序列化代码 TransactionPrefilter中的failed字段:从流中排除失败的交易,只有已确认的链上事件到达你的处理器- 用于可观测性的追踪跨度:通过
tracingcrate 提供结构化、可过滤的日志输出,取代了普通的log/env_logger
Vixen vs Carbon
Vixen 和 Carbon 都是用于解析 Solana 程序数据的 Rust 框架。它们共享相同的核心方法:通过 Solana gRPC 流式传输交易,将指令数据解码为类型化的结构体,并在处理器中处理事件,但在如何生成解析器以及支持哪些数据源方面有所不同:
| 特性 | Vixen | Carbon |
|---|---|---|
| 解析器生成 | 编译时通过 include_vixen_parser! 过程宏进行 IDL 代码生成 |
预构建的解码器 crate + 从 IDL 进行 CLI 代码生成 |
| 数据源 | Solana gRPC | Solana gRPC、JITO Shredstream、JSON RPC |
| 内置程序支持 | 无——从任何 Anchor IDL 生成 | 60+ 个流行程序的预构建解码器 |
| 支持程序的设置 | 获取 IDL → 转换为 Codama → 宏调用 | 添加单个 Cargo crate 依赖 |
| 可观测性 | 具有结构化跨度的 tracing crate |
Prometheus 指标 + 日志 |
| 管道模型 | Runtime → Pipeline → Parser → Handler | Pipeline → Datasource → Decoder → Processor |
有关基于 Carbon 的指南,请参阅 Solana gRPC 和 Carbon:解析实时的 Solana 程序数据。
示例应用:Jupiter 限价单监控器
Jupiter 限价单是一个链上订单簿,允许交易者下达订单,当市场达到指定价格时自动执行。与即时兑换不同,限价单会留在链上,直到成交者(在程序账户中称为 taker)以优于或等于请求的价格成交。挂单者也可以随时取消未成交的订单以收回其 Token。
本指南选择 Jupiter 限价单有三个原因:
- 所有活动都通过单个 Anchor 程序(
j1o2qRpjcyUwEvwtcfhEQefh773ZgjxcVRry7LDqg5X)进行,这与 Vixen 的单管道模型清晰匹配。 - 该程序发布了链上 IDL,这使得我们可以端到端地演示 Vixen 的 IDL 代码生成功能。
- 该程序发出三种离散的、语义上不同的指令类型(每个生命周期事件一种),这使得处理器逻辑易于阅读和扩展。
本指南监控的三种指令是:
InitializeOrder:挂单者创建一个新的限价单,指定输入/输出 Token 和数量FillOrder:成交者以优于或等于请求的价格成交一个现有订单CancelOrder:挂单者取消其未成交的订单并收回 Token
对于每个事件,处理器从 Jupiter 的 Token API 解析人类可读的 Token 符号,并使用交易 Token 余额元数据中的小数精度格式化原始 Token 数量。
设置项目
创建项目目录和一个 idls 文件夹,用于存储 Jupiter 限价单 IDL:
cargo new jupiter-lo-vixen
cd jupiter-lo-vixen
mkdir idls
将 Cargo.toml 的内容替换为以下内容。所有 Vixen crate 都固定到 v0.6.1:
Cargo.toml
[package]
name = "jupiter-lo-vixen"
version = "0.1.0"
edition = "2021"
[dependencies]
yellowstone-vixen = "0.6.1"
yellowstone-vixen-core = "0.6.1"
yellowstone-vixen-parser = "0.6.1"
yellowstone-vixen-proc-macro = "0.6.1"
yellowstone-vixen-yellowstone-grpc-source = "0.6.1"
borsh = { version = "1", features = ["derive"] }
bs58 = "0.5"
chrono = { version = "0.4", features = ["clock"] }
reqwest = { version = "0.12", features = ["json", "rustls-tls"] }
serde = { version = "1", features = ["derive"] }
clap = { version = "4", features = ["derive"] }
rustls = { version = "0.23", features = ["ring"] }
toml = "0.8"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
请注意,yellowstone-vixen-core 必须是直接依赖项(而不仅仅是通过 yellowstone-vixen 传递),因为过程宏生成的代码会按 crate 名称引用它。
以下是关键依赖项的作用:
yellowstone-vixen/yellowstone-vixen-core:Vixen 运行时和核心 trait(Parser、Handler、Pipeline)yellowstone-vixen-proc-macro:用于基于 IDL 代码生成的include_vixen_parser!宏yellowstone-vixen-yellowstone-grpc-source:将 Vixen 运行时连接到 Solana gRPC 端点borsh:Solana 程序使用的序列化格式(生成的解析器需要)bs58:交易签名的 Base58 编码chrono:日志输出的时间戳格式化reqwest:用于从 Jupiter API 获取 Token 符号的 HTTP 客户端clap:用于--config标志的命令行参数解析rustls:gRPC 连接所需的 TLS 提供者tracing/tracing-subscriber:具有基于环境过滤的结构化日志记录
在项目根目录创建 Vixen.toml。这是应用唯一需要的配置文件:
Vixen.toml
[source]
endpoint = "YOUR_QN_ENDPOINT:10000"
x-token = "YOUR_X_TOKEN"
timeout = 120
commitment-level = "confirmed"
accept-compression = "zstd"
你可以在 Quicknode 仪表板 的 Solana 主网端点下的 Solana gRPC 设置中找到这些值。仪表板显示的端点 URL 格式为 https://your-endpoint-name.solana-mainnet.quiknode.pro/abc123。将 :10000 附加到主机名作为 endpoint 字段,并使用尾部 Token(abc123)作为你的 x-token。更多详情,请参阅 Solana gRPC 文档。
从限价单 IDL 生成解析器
与其为每条指令手动编写反序列化代码,不如以 Codama 格式提供程序的 IDL,include_vixen_parser! 宏将在编译时生成一个完整的类型安全解析器。本节将引导你完成整个流程,以便你能够将其应用于任何 Anchor 程序。
获取 Jupiter 限价单 v2 IDL:
Vixen 仓库已经在 tests/idls/lo_v2.json 中包含了一个即用的 Jupiter 限价单 v2 Codama IDL。你可以直接将其复制到你的 idls/ 文件夹,然后直接跳转到 调用过程宏。以下步骤展示了完整的获取和转换流程,以便你可以对任何没有现成 Codama IDL 的 Anchor 程序应用相同的过程。
使用 Anchor CLI 获取 Jupiter 限价单 v2 程序的链上 IDL。将 YOUR_QN_RPC_URL 替换为你的 Quicknode Solana 主网 RPC 端点:
anchor idl fetch j1o2qRpjcyUwEvwtcfhEQefh773ZgjxcVRry7LDqg5X \
--provider.cluster YOUR_QN_RPC_URL \
-o idls/lo_v2_raw.json
该 IDL 包含了完整的程序接口定义:指令名称、账户结构、参数类型以及程序地址。Vixen 使用这些信息来生成类型化的 Rust 代码。
Anchor IDL 的一个潜在问题
当你使用 anchor idl 命令下载 IDL 时,它会带有一个小问题,导致 Codama 的转换器出错。在 initialize_order 指令中,一个 PDA 种子引用了一个名为 unique_id 的参数,但在该 IDL 中,unique_id 实际上嵌套在一个 params 结构体中,而不是顶层的参数。Codama 会在顶层寻找它,找不到就会报错。
当你将 Codama 用于其他程序的 IDL 时,可能会遇到类似问题,这些 IDL 在技术上是有效的 Anchor 输出,但路径引用与实际参数结构不匹配。当使用 Vixen 时,如果 IDL 没有现成的 Codama 格式,你可能需要手动排查此类问题。
要解决此问题,打开 idls/lo_v2_raw.json,在 initialize_order 指令的账户种子中找到这一行(大约第 #621 行):
idls/lo_v2_raw.json
{
"kind": "arg",
"path": "unique_id"
}
将其更改为:
idls/lo_v2_raw.json
{
"kind": "arg",
"path": "params.unique_id"
}
转换为 Codama 格式:
将根项目文件夹初始化为 Node.js 项目,以便我们可以运行 Codama 命令:
npm init -y
npm install -g @codama/cli
npm install @codama/nodes-from-anchor
需要 @codama/nodes-from-anchor 来理解转换过程中 Anchor 的 IDL 格式。
将原始 Anchor IDL 转换为 Codama 格式:
codama convert idls/lo_v2_raw.json idls/lo_v2.json
这一一次性转换步骤是为任何 Anchor 程序解锁 Vixen 编译时代码生成的关键。
调用过程宏
创建 src/parser.rs,包含以下内容:
src/parser.rs
use yellowstone_vixen_proc_macro::include_vixen_parser;
include_vixen_parser!("idls/lo_v2.json");
这一个宏调用在编译时生成:
- 一个
limit_order2模块,包含所有生成的类型 - 一个类型化的
Instructions枚举,为 IDL 中的每条指令提供变体(InitializeOrder、FillOrder、CancelOrder等) - 每条指令的类型化账户和参数结构体
- 一个实现 Vixen parser trait 的
InstructionParser - 一个配置了三个过滤器的
TransactionPrefilter:程序地址(仅 Jupiter LO v2)、failed: Some(false)(v0.6.1 新增) 以排除失败交易,以及vote: Some(false)以排除投票交易
实现监控器
Vixen v0.6.1 使用 tracing crate 实现结构化、可过滤的日志输出,取代了 Vixen 早期版本中使用的旧 log/env_logger 模式。
tracing 订阅者会遵循 RUST_LOG 环境变量,因此你可以在运行时控制详细程度而无需重新编译:
RUST_LOG=info:显示处理器输出和 Vixen 生命周期事件RUST_LOG=debug:添加 gRPC 连接详情和流订阅活动RUST_LOG=warn:仅显示警告和错误
初始化在 main() 中的任何其他代码之前进行,以确保所有 Vixen 内部逻辑和你的处理器代码都能受益于结构化日志记录。
实现限价单处理器
创建 src/handlers.rs。处理器从生成的解析器接收解析后的指令,并记录三种订单事件:挂单、成交和取消。
处理器包含一些辅助函数,用于从 Jupiter 的 Token API 获取人类可读的 Token 符号并将其缓存以避免重复查询。它还从事务 Token 余额元数据中提取小数精度,以便以人类可读的形式显示数量(例如,1.5 USDC 而不是 1500000)。
src/handlers.rs
use std::{collections::HashMap, sync::Mutex};
use yellowstone_vixen::vixen_core::instruction::InstructionUpdate;
use crate::parser::limit_order2;
fn fmt_amount(raw: u64, decimals: u32) -> String {
if decimals == 0 {
return raw.to_string();
}
let s = format!("{:.prec$}", raw as f64 / 10f64.powi(decimals as i32), prec = decimals as usize);
s.trim_end_matches('0').trim_end_matches('.').to_string()
}
fn fmt_token(amount: &str, symbol: &str, mint: &str) -> String {
if symbol == mint {
format!("{amount} {mint}")
} else {
format!("{amount} {symbol} ({mint})")
}
}
async fn fetch_symbol(mint: &str) -> String {
#[derive(serde::Deserialize)]
struct Token {
id: String,
symbol: String,
}
let url = format!("https://api.jup.ag/tokens/v2/search?query={mint}");
let fallback = mint.to_string();
let Ok(resp) = reqwest::get(&url).await else {
return fallback;
};
let Ok(tokens) = resp.json::<Vec<Token>>().await else {
return fallback;
};
tokens
.into_iter()
.find(|t| t.id == mint)
.map(|t| t.symbol)
.unwrap_or(fallback)
}
#[derive(Debug, Default)]
pub struct LimitOrderHandler {
symbol_cache: Mutex<HashMap<String, String>>,
}
impl LimitOrderHandler {
async fn get_symbol(&self, mint: &str) -> String {
{
let cache = self.symbol_cache.lock().unwrap();
if let Some(sym) = cache.get(mint) {
return sym.clone();
}
}
let symbol = fetch_symbol(mint).await;
self.symbol_cache
.lock()
.unwrap()
.insert(mint.to_string(), symbol.clone());
symbol
}
}
impl yellowstone_vixen::Handler<limit_order2::Instructions, InstructionUpdate>
for LimitOrderHandler
{
async fn handle(
&self,
value: &limit_order2::Instructions,
raw: &InstructionUpdate,
) -> yellowstone_vixen::HandlerResult<()> {
use limit_order2::instruction::Instruction;
let sig = bs58::encode(&raw.shared.signature).into_string();
let ts = chrono::Utc::now().format("%Y-%m-%dT%H:%M:%S%.6fZ");
let pre = &raw.shared.pre_token_balances;
let post = &raw.shared.post_token_balances;
let find_decimals = |mint: &str| {
pre.iter()
.chain(post.iter())
.find(|b| b.mint == mint)
.and_then(|b| b.ui_token_amount.as_ref())
.map(|u| u.decimals)
};
match &value.instruction {
Instruction::InitializeOrder { accounts, args } => {
let input_mint_str = accounts.input_mint.to_string();
let output_mint_str = accounts.output_mint.to_string();
let input_symbol = self.get_symbol(&input_mint_str).await;
let output_symbol = self.get_symbol(&output_mint_str).await;
let making_amount = find_decimals(&input_mint_str)
.map(|d| fmt_amount(args.making_amount, d))
.unwrap_or_else(|| args.making_amount.to_string());
let taking_amount = find_decimals(&output_mint_str)
.map(|d| fmt_amount(args.taking_amount, d))
.unwrap_or_else(|| args.taking_amount.to_string());
let expired_at = args.expired_at
.and_then(|t| chrono::DateTime::from_timestamp(t, 0))
.map(|dt| dt.format("%Y-%m-%dT%H:%M:%SZ").to_string())
.unwrap_or_else(|| "None".to_string());
let making = fmt_token(&making_amount, &input_symbol, &input_mint_str);
let taking = fmt_token(&taking_amount, &output_symbol, &output_mint_str);
tracing::info!(
tx = %sig,
maker = %accounts.maker,
making_amount = %making,
taking_amount = %taking,
expired_at = %expired_at,
"New limit order placed - {ts}",
);
},
Instruction::FillOrder { accounts, args } => {
let input_mint_str = accounts.input_mint.to_string();
let output_mint_str = accounts.output_mint.to_string();
let input_symbol = self.get_symbol(&input_mint_str).await;
let input_amount = find_decimals(&input_mint_str)
.map(|d| fmt_amount(args.input_amount, d))
.unwrap_or_else(|| args.input_amount.to_string());
let input = fmt_token(&input_amount, &input_symbol, &input_mint_str);
tracing::info!(
tx = %sig,
taker = %accounts.taker,
output_mint = %output_mint_str,
input_amount = %input,
"Limit order filled - {ts}",
);
},
Instruction::CancelOrder { accounts, .. } => {
tracing::info!(
tx = %sig,
maker = %accounts.maker,
order = %accounts.order,
"Limit order cancelled - {ts}",
);
},
_ => {},
}
Ok(())
}
}
处理器为生成的 limit_order2::Instructions 类型实现了 Vixen 的 Handler trait。它对三种指令变体进行模式匹配:
InitializeOrder:记录挂单者地址、具有人类可读数量的输入/输出 Token 以及订单的过期时间戳FillOrder:记录成交者地址、输出铸币地址以及正在成交的输入数量CancelOrder:记录挂单者地址和被取消的订单账户_(通配符):静默忽略所有其他指令(费用更新、管理员操作等)
处理器还包含用户友好的格式化,例如 Token 数量的小数转换以及通过 Jupiter 的 Token API 进行的代码符号查找。对于超高并发的应用,这些出站 API 调用可能会影响性能。
构建 Vixen 运行时
将生成的 src/main.rs 替换为以下代码。这将所有内容整合在一起:tracing 初始化、TLS 加密提供者、配置加载以及带有指令管道的 Vixen 运行时。
src/main.rs
use std::path::PathBuf;
use clap::Parser as _;
use yellowstone_vixen::Pipeline;
use yellowstone_vixen_yellowstone_grpc_source::YellowstoneGrpcSource;
mod handlers;
mod parser;
use handlers::LimitOrderHandler;
use parser::limit_order2;
#[derive(clap::Parser)]
#[command(version, author, about = "Monitor Jupiter Limit Order v2 events via Yellowstone Vixen")]
struct Opts {
#[arg(long, short)]
config: PathBuf,
}
fn main() {
tracing_subscriber::fmt()
.with_env_filter(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info")),
)
.init();
rustls::crypto::ring::default_provider()
.install_default()
.expect("Failed to install rustls crypto provider");
let Opts { config } = Opts::parse();
let config = std::fs::read_to_string(config).expect("Error reading config file");
let config = toml::from_str(&config).expect("Error parsing config");
yellowstone_vixen::Runtime::<YellowstoneGrpcSource>::builder()
.instruction(Pipeline::new(limit_order2::InstructionParser, [LimitOrderHandler::default()]))
.build(config)
.run();
}
运行时设置执行四个步骤:
- Tracing:使用
EnvFilter初始化tracing-subscriber,以便RUST_LOG环境变量控制运行时日志详细程度而无需重新编译 - TLS:安装
rustls加密提供者,这是与 Quicknode 的 gRPC TLS 连接所必需的 - 配置:读取并解析
Vixen.toml,获取端点、Token和流设置 - 运行时:构建一个 Vixen
Runtime,其中包含一个将生成的InstructionParser连接到LimitOrderHandler的指令Pipeline,然后开始流式传输
Pipeline::new 调用将解析器连接到处理器。生成的 InstructionParser 处理订阅过滤(程序地址、失败交易、投票交易)和反序列化。你的处理器只接收针对 Jupiter 限价单 v2 程序的解析后的、类型化的指令数据。
构建并运行应用程序
构建项目:
cargo build
使用配置标志运行它。设置 RUST_LOG=info 以查看处理器输出和 Vixen 生命周期事件:
RUST_LOG=info cargo run -- --config ./Vixen.toml
预期输出
连接后,你将看到 Vixen 生命周期日志行,随后是在主网上发生时的限价单事件。以下是三种事件类型的示例输出:
2026-04-08T14:32:01.123456Z INFO jupiter_lo_vixen: New limit order placed - 2026-04-08T14:32:01.123456Z tx=5UjQ...abc maker=7xKm...def making_amount="100 USDC (EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v)" taking_amount="1.5 SOL (So11111111111111111111111111111111111111112)" expired_at=2026-04-15T00:00:00Z
2026-04-08T14:33:15.654321Z INFO jupiter_lo_vixen: Limit order filled - 2026-04-08T14:33:15.654321Z tx=3Kpx...ghi taker=9mRv...jkl output_mint=So11111111111111111111111111111111111111112 input_amount="100 USDC (EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v)"
2026-04-08T14:35:42.789012Z INFO jupiter_lo_vixen: Limit order cancelled - 2026-04-08T14:35:42.789012Z tx=8FnY...mno maker=7xKm...def order=4vTq...pqr
每个条目都包含一个 ISO 8601 时间戳、交易签名以及事件类型相关的账户和数量。Token 数量以人类可读的形式显示,带有从 Jupiter 的 Token API 解析的代码符号。
扩展你的监控器
你构建的架构是灵活的。以下是一些扩展方式:
- 监控不同的 Jupiter 程序:替换为 Jupiter DCA、Perps 或任何其他 Anchor 程序的 IDL。管道结构保持不变;只需更改 IDL 文件和处理器逻辑。
- 同时监控多个程序:Vixen v0.6.1 支持多个程序订阅。添加第二个指令管道,以便在同一个运行时中监控 Limit Order v1 和 v2。
- 添加账户监控:除了指令监控,还可以添加一个账户管道来跟踪特定限价单账户状态的变化。
- 将事件持久化到数据库:扩展处理器,将解析后的事件写入 PostgreSQL、ClickHouse 或其他数据存储,用于历史分析。
常见问题解答
什么是 Solana gRPC?它如何实现 Solana 上的实时监控?
Solana gRPC 是一种高性能 gRPC 接口,用于实时流式传输 Solana 区块链数据(包括交易和账户更新)。它允许 Rust 应用程序订阅特定程序(如 Jupiter),以获取低延迟的限价单数据流,无需轮询。
在 Solana 开发的上下文中,Vixen 是什么?
Vixen 是一个开源的 Rust 框架,用于在 Solana gRPC 之上构建类型化的数据管道。它自动处理 gRPC 连接、流过滤和指令反序列化,因此你可以针对类型化的 Rust 结构体编写业务逻辑,而不是原始字节。其核心抽象是 Parser(解码指令)、Handler(处理指令)和 Pipeline(在托管的 Runtime 内连接两者)。
我可以使用 Vixen 监控除 Jupiter 限价单以外的程序吗?
可以。Vixen 的过程宏适用于任何发布 IDL 的 Anchor 程序。使用 Anchor CLI 获取 IDL,将其转换为 Codama 格式,然后传递给 include_vixen_parser!。生成的解析器和管道结构保持不变。只需更改 IDL 文件和你的处理器逻辑。
为什么 TransactionPrefilter 要排除失败交易?
Vixen v0.6.1 在其 TransactionPrefilter 中引入了 failed 字段。将其设置为排除失败交易,可确保只有已确认、成功执行的交易到达你的处理器。对于限价单监控器,这可以防止记录从未在链上实际执行的失败尝试等事件。
我需要一个 Quicknode 账户才能使用 Vixen 吗?
Vixen 本身是一个开源框架。但是,它需要一个 Solana gRPC 端点来流式传输数据。Quicknode 提供 Solana gRPC(包含在 Scale 和 Business 计划中,或可通过 Build 和 Accelerate 计划的附加组件获取),你可以从仪表板在任何 Quicknode Solana 端点上启用它。
什么是 Codama?为什么需要转换步骤?
Codama 是一种 IDL 格式,它提供比 Anchor 原生 IDL 格式更丰富的类型信息。Vixen 的过程宏需要 Codama 格式才能生成准确的类型化结构体和枚举。codama convert 命令桥接了这两种格式。这一一次性转换步骤是为任何 Anchor 程序解锁 Vixen 编译时代码生成的关键。
结语
你已经构建了一个实时的 Jupiter 限价单监控器,它通过 Solana gRPC 流式传输已确认的交易,使用 Vixen 的 IDL 代码生成将其解析为类型化的 Rust 结构体,并记录订单的挂单、成交和取消,附带人类可读的 Token 输出。从这里开始,你可以通过替换其 IDL 将该模式应用于任何 Anchor 程序,使 Vixen 成为构建 Solana 数据管道的多功能基础。
资源
- 指南:使用 Solana gRPC(Rust)监控 Solana 程序
- 指南:Solana gRPC 和 Carbon:解析实时的 Solana 程序数据
- 指南:如何使用 Codama 创建 Anchor 程序客户端
- Yellowstone Vixen GitHub
- Codama IDL
- Quicknode Solana gRPC 文档
- Anchor 文档
- Jupiter Trigger Order API 概述
有问题?加入 Quicknode Discord 或关注 @quicknode 获取更新。
我们真诚欢迎反馈!
如有任何反馈或新主题请求,请告诉我们。我们期待你的意见。
- 原文链接: quicknode.com/guides/sol...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~