release: prepare solana-streamer-sdk 3.0.17 with wrapped Pump provenance

This commit is contained in:
0xfnzero
2026-10-20 01:01:18 +08:00
parent 13e1a0fbcf
commit 4a3a09e8e2
8 changed files with 106 additions and 14 deletions
+2 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "solana-streamer-sdk"
version = "3.0.16"
version = "3.0.17"
edition = "2021"
rust-version = "1.91"
authors = ["William <byteblock6@gmail.com>", "sgxiang <sgxiang@gmail.com>", "wei <1415121722@qq.com>"]
@@ -22,7 +22,7 @@ sdk-perf-stats = ["sol-parser-sdk/perf-stats"]
sdk-ultra-perf = ["sol-parser-sdk/ultra-perf"]
[dependencies]
sol-parser-sdk = { path = "../sol-parser-sdk", version = "=0.7.18", default-features = false }
sol-parser-sdk = { path = "../sol-parser-sdk", version = "=0.7.19", default-features = false }
solana-sdk = "=4.0.1"
solana-client = "=4.2.2"
solana-transaction-status = { version = "=4.2.2", features = ["agave-unstable-api"] }
+10 -4
View File
@@ -1,5 +1,11 @@
<div align="center">
## v3.0.17 — Wrapped Pump.fun provenance and parser fixes
Updates sol-parser-sdk to 0.7.19 and preserves optional `third_program` provenance through the Pump.fun event bridge. Includes mint-scoped create-buy context and versioned IDL method-name fixes. Third-party rules remain explicit opt-in; the parsing hot path performs no RPC.
[Wrapped Pump.fun: offline rules, hot-path boundaries, and real-transaction replays](docs/wrapped-pumpfun.md)
## v3.0.16 — Shred metadata passthrough and ordering scope
Preserve and verify transaction position and receive time through raw instruction callbacks. Entries indexes are message-local, unlike Yellowstone node indexes; use signatures for cross-message deduplication. This release clarifies API documentation and adds regression protection without changing metadata fields or numbering behavior.
@@ -163,24 +169,24 @@ Add the dependency to your `Cargo.toml`:
```toml
# Add to your Cargo.toml
solana-streamer-sdk = { path = "./solana-streamer", version = "3.0.16" }
solana-streamer-sdk = { path = "./solana-streamer", version = "3.0.17" }
```
### Use crates.io
```toml
# Add to your Cargo.toml
solana-streamer-sdk = "3.0.16"
solana-streamer-sdk = "3.0.17"
```
Parser backend features:
```toml
# Default: sol-parser-sdk parse-borsh backend
solana-streamer-sdk = "3.0.16"
solana-streamer-sdk = "3.0.17"
# Zero-copy parser backend for latency-sensitive bots
solana-streamer-sdk = { version = "3.0.16", default-features = false, features = ["sdk-parse-zero-copy"] }
solana-streamer-sdk = { version = "3.0.17", default-features = false, features = ["sdk-parse-zero-copy"] }
```
If both `sdk-parse-borsh` and `sdk-parse-zero-copy` are enabled, `sol-parser-sdk 0.6.1+` uses the zero-copy backend.
+10 -4
View File
@@ -1,5 +1,11 @@
<div align="center">
## v3.0.17 — Pump.fun 第三方来源透传与解析修复
更新 sol-parser-sdk 至 0.7.19,Pump.fun 事件桥接保留可选的 `third_program` 第三方来源。同步按 mint 识别创建后买入及保留 IDL 版本化方法名的修复。第三方规则仍须显式启用,解析热路径不调用 RPC。
[第三方 Pump.fun 包装指令:离线规则、热路径边界与真实交易回放](docs/wrapped-pumpfun.md)
## v3.0.16 — Shred 元数据透传与顺序范围
保留并验证 `tx_index`、接收时间从解析入口传到原始指令回调。明确 Entries 消息内序号与 Yellowstone 节点 index 的区别:本地序号不是全 slot 索引;跨消息去重使用交易签名。此版本完善文档和回归保护,不改变现有字段或编号规则。
@@ -162,24 +168,24 @@ git clone https://github.com/0xfnzero/solana-streamer
```toml
# 添加到您的 Cargo.toml
solana-streamer-sdk = { path = "./solana-streamer", version = "3.0.16" }
solana-streamer-sdk = { path = "./solana-streamer", version = "3.0.17" }
```
### 使用 crates.io
```toml
# 添加到您的 Cargo.toml
solana-streamer-sdk = "3.0.16"
solana-streamer-sdk = "3.0.17"
```
解析后端 feature:
```toml
# 默认:sol-parser-sdk parse-borsh 后端
solana-streamer-sdk = "3.0.16"
solana-streamer-sdk = "3.0.17"
# 面向低延迟 Bot 的 zero-copy 解析后端
solana-streamer-sdk = { version = "3.0.16", default-features = false, features = ["sdk-parse-zero-copy"] }
solana-streamer-sdk = { version = "3.0.17", default-features = false, features = ["sdk-parse-zero-copy"] }
```
如果同时启用 `sdk-parse-borsh` 和 `sdk-parse-zero-copy`,`sol-parser-sdk 0.6.1+` 会优先使用 zero-copy 后端。
+57
View File
@@ -0,0 +1,57 @@
# Wrapped Pump.fun instructions (solana-streamer-sdk 3.0.17)
## Boundaries
The streaming hot path never calls RPC. gRPC uses the supplied execution metadata and actual CPI instructions. Shred sees only signed outer instructions and locally resolved ALT addresses. Never populate a missing ALT address with a default key.
Third-party reconstruction is **explicit opt-in**. Existing subscriptions keep RawInstruction as their default fallback. Load reviewed rules once at startup, then call the wrapper parser from a RawInstruction callback. Pass the subscription filter through; only deliver the restored event on a successful match. On a miss keep the raw event. Do not feed gRPC events through these rules.
A restored PumpFunTrade includes `third_program: { program_id, instruction_index, rule_id }`. `ix_name` uses the Pump.fun IDL method name. Input fields such as `spendable_quote_in` and `min_tokens_out` are available. `sol_amount`, `quote_amount`, and `token_amount` stay zero because Shred cannot establish executed amounts. Metadata, including tx_index, receive time, and recent_blockhash, is retained.
The initial rule engine supports fixed two-u64 v2 inputs. Multi-hop/CurveSwap, runtime-balance-dependent parameters, and unsupported layouts remain raw. Exact program, data length and guarded bytes, account count, canonical keys and aliases must match. A program upgrade can change semantics without changing this shape; rules are not a guarantee of future CPI behavior.
## Offline analysis
In the Rust sol-parser-sdk repository:
```sh
cargo run --example wrapped_pumpfun_rpc_analyzer
SOLANA_RPC_URL=https://YOUR_RPC cargo run --example wrapped_pumpfun_rpc_analyzer -- SIGNATURE
cargo run --example wrapped_pumpfun_replay
cargo test --test wrapped_pumpfun --test pump_execution
```
Only the analyzer example's signature-fetch branch accesses RPC. The library analyzer consumes an already fetched transaction. It matches actual Pump CPI instruction bytes (not executed TradeEvent amounts), infers account maps and exact byte guards, and emits a **candidate** rule. Missing, ambiguous or runtime-derived parameters produce an explicit rejection. Candidates never install themselves. Validate each candidate on independent signatures and retain separate rules for different byte patterns; do not wildcard unexplained bytes.
`wrapped_pumpfun_replay` uses signed saved mainnet wire data and compares the gRPC execution path with the Shred outer-only path. It accepts `--fixture FILE` and `--rules FILE`. Saved execution metadata is an offline oracle only, never an input to the hot wrapper parser.
## Verified transactions
Reported signatures:
- `5hKonDfdNj283yidV77Ptfe1oJsPB9XXBQ6mJPtpbXVe9b6UhwBedVkX6tAaTwschTHYMeqUwt7bXeiHAzRob62p`
- `2r4uXUJtSsphTBuJHJm2T5kKe9c215MCVL4bZ1mUZEZ6cT5698s3xFUfHb4TfU4ZfJ6cuFppQfYZUpMP5QL9pZ1Q`
- `3nMXPjA7xRFmCeq3jypeWaXe9bmP5Fkwkvy6u74bBZHe316qMXNaE5aDQwDB1HRVLSkmk3BjgRCKBNcPEMeXrGWj`
Additional mainnet captures, slots 458801397–458801420:
- `5wM2Jk6eDSc42mtaSWrVavAuo6rZDpR3LmWK4BeJMqrt6B5xiEE6YzpHv5qdHJsHt7crLEM1L8P5r53ndRBespvC`
- `2KBsKB7xmfFz3qLB6U3YMTuc2Z8W3WYrEJhxqvtNytqw2co2W3zQizTPzMtC1c37NnaCuKpaoaxiT6QT4pqmF5nW`
- `5rY6dXEjgr7m5KymyNUP1GQxegrFf8GsM9263y35b6AxQ4e5d1nmHDpj2QACqnE5eZoFPn5vNmz17nbXGXtDZR9w`
- `4dG9FmeZjtKDiH1asJgWy8PJZAfqd96hiZRyFfjNn1g2JosLG1iwgpMgi95bygVX9jUgNW3oRDwt61q4yrjKSe2B`
The original Rapid transaction has three reconstructable buys, with u64 arguments at outer offsets 1 and 33. Recent buys differ at guarded byte 13 (15 versus 255), so the examples keep two distinct rules. The recent rule is inferred from 5rY6… and tested against independent signature 4dG9…. Five total buys match their actual CPI data and ordered accounts. The older rule is demonstrated by three calls in one signature; it does not have independent-signature validation in this corpus.
J7's first instruction in 5hKon… performs token-account/transfer work, with no Pump trade CPI. Its later buy passes 3,551,960 quote units to Pump, a value absent from its outer data. This cannot be restored by replacing the discriminator; the analyzer returns no rule. The captured Rapid sells also fail parameter inference and remain raw.
gRPC regression tests cover the reported transactions, recent captures and existing multi-hop fixtures; full/missing/truncated logs, repeated trades, failed transactions, metadata and filtering. Fixed: create-buy context is derived from outer/CPI create instructions per mint, not a transaction-wide log hint; versioned instruction names are no longer overwritten by generic event-log names.
## Other repositories
- sol-shred-sdk: `cargo run --example wrapped_pumpfun_replay` verifies signed wires and exact CPI correspondence offline.
- TypeScript: `npx tsx examples/wrapped_pumpfun_replay.ts`; `npx tsx examples/pump_execution_replay.ts [SIGNATURE]`.
- Python: `python examples/wrapped_pumpfun_replay.py`; `python examples/pump_execution_replay.py [SIGNATURE]`.
- Go (repository root): `go run ./examples/wrapped_pumpfun_replay`; `go run ./examples/pump_execution_replay [SIGNATURE]`.
- solana-streamer: uses the corrected local parser and preserves `third_program` through its event bridge. It does not enable third-party rules automatically.
The Rust analyzer's JSON rules can be consumed by all four parser implementations. Tests and wrapper examples are offline by default. Passing a signature to an execution/analyzer tool enables an explicit offline RPC fetch, not a request from the parsing hot path.
+2 -3
View File
@@ -27,9 +27,8 @@ fn main() -> Result<()> {
let signature = wire["transaction"]["signatures"][0]
.as_str()
.ok_or_else(|| anyhow::anyhow!("missing signature in {name}"))?;
let slot = wire["slot"]
.as_u64()
.ok_or_else(|| anyhow::anyhow!("missing slot in {name}"))?;
let slot =
wire["slot"].as_u64().ok_or_else(|| anyhow::anyhow!("missing slot in {name}"))?;
let status = if wire["meta"]["err"].is_null() { "success" } else { "failed" };
println!("{name}: {signature}, slot={slot}, status={status}");
}
@@ -147,6 +147,9 @@ pub struct PumpFunCreateV2TokenEvent {
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
pub struct PumpFunTradeEvent {
#[borsh(skip)]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub third_program: Option<sol_parser_sdk::wrapped_pumpfun_parser::ThirdProgram>,
#[borsh(skip)]
#[serde(default)]
pub creator_fee_unclaimed: u64,
@@ -67,7 +67,6 @@ pub struct RaydiumCpmmSwapEvent {
#[borsh(skip)]
#[serde(default)]
pub creator_fee_on_input: bool,
}
/// 存款
@@ -716,6 +716,7 @@ pub(crate) fn pumpfun_trade_from_parser_with_event_type(
let pm = t.metadata.clone();
let meta = adapt_pm(pm, bt, recv_wall_us, ProtocolType::PumpFun, event_type, pump_program());
let st = PumpFunTradeEvent {
third_program: t.third_program,
metadata: meta,
mint: t.mint,
sol_amount: t.sol_amount,
@@ -924,6 +925,27 @@ mod tests {
}
}
#[test]
fn wrapped_pumpfun_provenance_survives_bridge() {
let source = sol_parser_sdk::wrapped_pumpfun_parser::ThirdProgram {
program_id: solana_sdk::pubkey!("rapidV5DAKtecYzetwCNoxXWHpezjeg4hGt9BGiVcGW"),
instruction_index: 3,
rule_id: "reviewed-layout".into(),
};
let ev = pumpfun_trade_from_parser_with_event_type(
sol_parser_sdk::core::events::PumpFunTradeEvent {
third_program: Some(source.clone()),
is_buy: true,
..Default::default()
},
None,
123456,
EventType::PumpFunBuy,
);
let DexEvent::PumpFunTradeEvent(trade) = ev else { panic!() };
assert_eq!(trade.third_program, Some(source));
}
#[test]
fn pumpfun_final_balances_are_preserved() {
let ev = pumpfun_trade_from_parser_with_event_type(