From e3249703a5dc4a996247ea20c1065bb10b050961 Mon Sep 17 00:00:00 2001 From: 0xfnzero <0xfnzero@users.noreply.github.com> Date: Fri, 12 Jun 2026 22:22:40 +0800 Subject: [PATCH] fix: add PumpFun quote case parser example --- Cargo.toml | 4 +- README.md | 15 ++- README_CN.md | 15 ++- examples/parse_pumpfun_quote_cases.rs | 146 ++++++++++++++++++++++++++ 4 files changed, 168 insertions(+), 12 deletions(-) create mode 100644 examples/parse_pumpfun_quote_cases.rs diff --git a/Cargo.toml b/Cargo.toml index 35d0507..52e72a1 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-streamer-sdk" -version = "1.5.14" +version = "1.5.15" edition = "2021" authors = ["William ", "sgxiang ", "wei <1415121722@qq.com>"] repository = "https://github.com/0xfnzero/solana-streamer" @@ -21,7 +21,7 @@ sdk-perf-stats = ["sol-parser-sdk/perf-stats"] sdk-ultra-perf = ["sol-parser-sdk/ultra-perf"] [dependencies] -sol-parser-sdk = { version = "0.5.14", default-features = false } +sol-parser-sdk = { git = "https://github.com/0xfnzero/sol-parser-sdk", rev = "4464880", version = "0.5.15", default-features = false } solana-sdk = "3.0.0" solana-client = "3.1.12" solana-transaction-status = "3.1.12" diff --git a/README.md b/README.md index 45f7f1c..a84cb44 100755 --- a/README.md +++ b/README.md @@ -124,30 +124,34 @@ Add the dependency to your `Cargo.toml`: ```toml # Add to your Cargo.toml -solana-streamer-sdk = { path = "./solana-streamer", version = "1.5.14" } +solana-streamer-sdk = { path = "./solana-streamer", version = "1.5.15" } ``` ### Use crates.io ```toml # Add to your Cargo.toml -solana-streamer-sdk = "1.5.14" +solana-streamer-sdk = "1.5.15" ``` Parser backend features: ```toml # Default: sol-parser-sdk parse-borsh backend -solana-streamer-sdk = "1.5.14" +solana-streamer-sdk = "1.5.15" # Zero-copy parser backend for latency-sensitive bots -solana-streamer-sdk = { version = "1.5.14", default-features = false, features = ["sdk-parse-zero-copy"] } +solana-streamer-sdk = { version = "1.5.15", default-features = false, features = ["sdk-parse-zero-copy"] } ``` -If both `sdk-parse-borsh` and `sdk-parse-zero-copy` are enabled, `sol-parser-sdk 0.5.14+` uses the zero-copy backend. +If both `sdk-parse-borsh` and `sdk-parse-zero-copy` are enabled, `sol-parser-sdk 0.5.15+` uses the zero-copy backend. ## 🔄 Migration Guide +### Upgrading to v1.5.15 + +Version 1.5.15 tracks `sol-parser-sdk 0.5.15` at GitHub rev `4464880`. Pump.fun `create_v2` now distinguishes 16-account SOL-sentinel creates from 19-account quote-pool creates, and account filling selects the actual create/create_v2 instruction before reading quote fields. + ### Upgrading to v1.5.14 Version 1.5.14 uses `sol-parser-sdk 0.5.14` from crates.io. Pump.fun canonical create events now expose `quote_mint`, `quote_vault`, and `quote_token_program` for `create_v2` quote pools, including USDC pools, across gRPC/RPC parser bridge and ShredStream-backed SDK output. @@ -350,6 +354,7 @@ grpc.subscribe_events_immediate( | Monitor transaction events using ShredStream | `cargo run --example shred_example` | [examples/shred_example.rs](examples/shred_example.rs) | | Parse Solana mainnet transaction data | `cargo run --example parse_tx_events` | [examples/parse_tx_events.rs](examples/parse_tx_events.rs) | | Parse PumpFun transaction from RPC (signature: `TX_SIGNATURE` or CLI arg) | `cargo run --example parse_pump_tx --release` | [examples/parse_pump_tx.rs](examples/parse_pump_tx.rs) | +| Parse PumpFun quote-mint cases from RPC | `TX_SIGNATURES= cargo run --example parse_pumpfun_quote_cases --release` | [examples/parse_pumpfun_quote_cases.rs](examples/parse_pumpfun_quote_cases.rs) | | Parse PumpSwap transaction from RPC | `cargo run --example parse_pumpswap_tx --release` | [examples/parse_pumpswap_tx.rs](examples/parse_pumpswap_tx.rs) | | Parse Meteora DAMM v2 transaction from RPC | `TX_SIGNATURE= cargo run --example parse_meteora_damm_tx --release` | [examples/parse_meteora_damm_tx.rs](examples/parse_meteora_damm_tx.rs) | | Debug PumpFun transaction (fetch, print meta/logs, parse) | `TX_SIGNATURE= cargo run --example debug_pump_tx --release` | [examples/debug_pump_tx.rs](examples/debug_pump_tx.rs) | diff --git a/README_CN.md b/README_CN.md index 0117695..4139a1c 100644 --- a/README_CN.md +++ b/README_CN.md @@ -123,30 +123,34 @@ git clone https://github.com/0xfnzero/solana-streamer ```toml # 添加到您的 Cargo.toml -solana-streamer-sdk = { path = "./solana-streamer", version = "1.5.14" } +solana-streamer-sdk = { path = "./solana-streamer", version = "1.5.15" } ``` ### 使用 crates.io ```toml # 添加到您的 Cargo.toml -solana-streamer-sdk = "1.5.14" +solana-streamer-sdk = "1.5.15" ``` 解析后端 feature: ```toml # 默认:sol-parser-sdk parse-borsh 后端 -solana-streamer-sdk = "1.5.14" +solana-streamer-sdk = "1.5.15" # 面向低延迟 Bot 的 zero-copy 解析后端 -solana-streamer-sdk = { version = "1.5.14", default-features = false, features = ["sdk-parse-zero-copy"] } +solana-streamer-sdk = { version = "1.5.15", default-features = false, features = ["sdk-parse-zero-copy"] } ``` -如果同时启用 `sdk-parse-borsh` 和 `sdk-parse-zero-copy`,`sol-parser-sdk 0.5.14+` 会优先使用 zero-copy 后端。 +如果同时启用 `sdk-parse-borsh` 和 `sdk-parse-zero-copy`,`sol-parser-sdk 0.5.15+` 会优先使用 zero-copy 后端。 ## 🔄 迁移指南 +### 升级到 v1.5.15 + +v1.5.15 跟随 GitHub rev `4464880` 上的 `sol-parser-sdk 0.5.15`。Pump.fun `create_v2` 现在会区分 16 账户 SOL sentinel 创建和 19 账户 quote-pool 创建;填充账户时会先选择实际的 create/create_v2 指令,再读取 quote 字段。 + ### 升级到 v1.5.14 v1.5.14 使用 crates.io 上的 `sol-parser-sdk 0.5.14`。Pump.fun canonical create 事件现在会为 `create_v2` quote 池暴露 `quote_mint`、`quote_vault`、`quote_token_program`,包括 USDC 池;gRPC/RPC parser bridge 和 ShredStream-backed SDK 输出都会保留这些字段。 @@ -349,6 +353,7 @@ grpc.subscribe_events_immediate( | 使用 ShredStream 监控交易事件 | `cargo run --example shred_example` | [examples/shred_example.rs](examples/shred_example.rs) | | 解析 Solana 主网交易数据 | `cargo run --example parse_tx_events` | [examples/parse_tx_events.rs](examples/parse_tx_events.rs) | | 从 RPC 解析 PumpFun 交易(签名:环境变量 `TX_SIGNATURE` 或 CLI 参数) | `cargo run --example parse_pump_tx --release` | [examples/parse_pump_tx.rs](examples/parse_pump_tx.rs) | +| 从 RPC 解析 PumpFun quote_mint 边界案例 | `TX_SIGNATURES= cargo run --example parse_pumpfun_quote_cases --release` | [examples/parse_pumpfun_quote_cases.rs](examples/parse_pumpfun_quote_cases.rs) | | 从 RPC 解析 PumpSwap 交易 | `cargo run --example parse_pumpswap_tx --release` | [examples/parse_pumpswap_tx.rs](examples/parse_pumpswap_tx.rs) | | 从 RPC 解析 Meteora DAMM v2 交易 | `TX_SIGNATURE= cargo run --example parse_meteora_damm_tx --release` | [examples/parse_meteora_damm_tx.rs](examples/parse_meteora_damm_tx.rs) | | 调试 PumpFun 交易(拉取、打印 meta/logs、解析) | `TX_SIGNATURE= cargo run --example debug_pump_tx --release` | [examples/debug_pump_tx.rs](examples/debug_pump_tx.rs) | diff --git a/examples/parse_pumpfun_quote_cases.rs b/examples/parse_pumpfun_quote_cases.rs new file mode 100644 index 0000000..5164cb8 --- /dev/null +++ b/examples/parse_pumpfun_quote_cases.rs @@ -0,0 +1,146 @@ +//! Parse Pump.fun quote-mint edge cases through solana-streamer's parser SDK re-export. +//! +//! Defaults cover: +//! - 19-account `create_v2` with explicit WSOL quote mint. +//! - 16-account `create_v2` with no quote mint account. +//! +//! Usage: +//! ```bash +//! cargo run --example parse_pumpfun_quote_cases --release +//! SOLANA_RPC_URL=https://api.mainnet-beta.solana.com cargo run --example parse_pumpfun_quote_cases --release +//! TX_SIGNATURES=sig1,sig2 cargo run --example parse_pumpfun_quote_cases --release +//! ``` + +use solana_client::rpc_client::RpcClient; +use solana_sdk::pubkey::Pubkey; +use solana_sdk::signature::Signature; +use solana_streamer_sdk::parser_sdk::core::events::{ + DexEvent, PUMPFUN_SOLSCAN_SOL_QUOTE_MINT, PUMPFUN_WSOL_QUOTE_MINT, +}; +use solana_streamer_sdk::parser_sdk::parse_transaction_from_rpc; +use std::str::FromStr; + +struct Case { + name: &'static str, + signature: &'static str, + expected_create_v2_quote: Option, +} + +fn default_cases() -> Vec { + vec![ + Case { + name: "19-account create_v2 explicit WSOL quote", + signature: "4GCVgY2FnT1s4q5zemnPL4mzSbuhUTgQo9mc9jewhLZzsCXKe8ehz6xD4QDJE853CLrF6doJbf4JNwJVeEYLA4De", + expected_create_v2_quote: Some(PUMPFUN_WSOL_QUOTE_MINT), + }, + Case { + name: "16-account create_v2 no quote account", + signature: "H6azwLqtRtrnVNC5iwcjYM9idU3e9SRyLZXTwjfJGJxA4X7dZL7vyhFAJNvQy7bb6bmQNmFHUt1KkkPPmhdge3G", + expected_create_v2_quote: Some(PUMPFUN_SOLSCAN_SOL_QUOTE_MINT), + }, + ] +} + +fn cases_from_env() -> Option> { + let raw = std::env::var("TX_SIGNATURES").ok()?; + let cases: Vec = raw + .split(',') + .map(str::trim) + .filter(|s| !s.is_empty()) + .map(|signature| Case { + name: "custom signature", + signature: Box::leak(signature.to_string().into_boxed_str()), + expected_create_v2_quote: None, + }) + .collect(); + + if cases.is_empty() { + None + } else { + Some(cases) + } +} + +fn print_event_summary(index: usize, event: &DexEvent) -> Option { + match event { + DexEvent::PumpFunCreate(e) => { + println!( + " #{index} PumpFunCreate ix={} mint={} quote_mint={} token_program={} user={}", + e.ix_name, e.mint, e.quote_mint, e.token_program, e.user + ); + if e.ix_name == "create_v2" { + Some(e.quote_mint) + } else { + None + } + } + DexEvent::PumpFunCreateV2(e) => { + println!( + " #{index} PumpFunCreateV2 ix={} mint={} quote_mint={} token_program={} user={}", + e.ix_name, e.mint, e.quote_mint, e.token_program, e.user + ); + Some(e.quote_mint) + } + DexEvent::PumpFunBuy(e) + | DexEvent::PumpFunBuyExactSolIn(e) + | DexEvent::PumpFunSell(e) + | DexEvent::PumpFunTrade(e) => { + println!( + " #{index} PumpFunTrade ix={} mint={} quote_mint={} quote_amount={} sol_amount={} token_amount={}", + e.ix_name, e.mint, e.quote_mint, e.quote_amount, e.sol_amount, e.token_amount + ); + None + } + _ => None, + } +} + +fn main() { + let rpc_url = std::env::var("SOLANA_RPC_URL") + .unwrap_or_else(|_| "https://api.mainnet-beta.solana.com".to_string()); + let client = RpcClient::new(rpc_url.clone()); + let cases = cases_from_env().unwrap_or_else(default_cases); + let mut failed = false; + + println!("RPC: {rpc_url}"); + println!("Cases: {}\n", cases.len()); + + for case in cases { + println!("=== {} ===", case.name); + println!("signature={}", case.signature); + + let signature = Signature::from_str(case.signature).expect("invalid signature"); + let events = match parse_transaction_from_rpc(&client, &signature, None) { + Ok(events) => events, + Err(err) => { + eprintln!(" ERROR: {err}"); + failed = true; + println!(); + continue; + } + }; + + println!(" events={}", events.len()); + let mut create_v2_quotes = Vec::new(); + for (i, event) in events.iter().enumerate() { + if let Some(quote) = print_event_summary(i + 1, event) { + create_v2_quotes.push(quote); + } + } + + if let Some(expected) = case.expected_create_v2_quote { + let ok = create_v2_quotes.contains(&expected); + println!( + " expected_create_v2_quote={} result={}", + expected, + if ok { "ok" } else { "FAIL" } + ); + failed |= !ok; + } + println!(); + } + + if failed { + std::process::exit(1); + } +}