Files
solana-streamer/docs/SHREDSTREAM_LIMITATIONS.md
T
Wood 7270372604 feat(shred): tx_index, Migrate without CPI, merge fix, doc limits
- Add tx_index to TransactionWithSlot (entry index), pass to parser
- Emit PumpFun Migrate even when no CPI (shred gets instruction-only)
- Merge: only overwrite Create/CreateV2 with CPI when value non-default
- Rename transaction_index -> tx_index across codebase
- Add docs/SHREDSTREAM_LIMITATIONS.md (ALT/CPI limits, field completeness)
- Comment shred path: static_account_keys only, no inner_instructions

Made-with: Cursor
2026-03-07 12:35:57 +08:00

102 lines
8.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Shredstream 解析限制与差异说明
与 gRPC 订阅相比,shredstream 路径存在以下限制和解析差异,使用时请注意。
## 1. 数据源差异
| 数据 | gRPC | Shredstream |
|------|------|-------------|
| 账户列表 | 完整 resolved 列表(static + loaded_addresses | 仅 `static_account_keys()` |
| Inner instructions (CPI) | 有(来自区块执行结果) | **无**Entry 仅含原始交易) |
| block_time | 有 | **无**(恒为 0 |
| tx_index | slot 内交易索引 | entry 内交易索引(best-effort |
## 2. 解析问题与遗漏
**漏掉事件小结**:Shred 会**整笔漏掉**「仅通过 CPI 触发的」所有协议事件(例如经 Jupiter 等聚合器路由的 PumpFun/PumpSwap 等),因为 shred 不解析 inner instructions。详见 2.3。
### 2.1 使用 Address Lookup Tables (ALT) 的交易
- **现象**:指令中的账户索引指向「static + loaded」的完整列表,shred 只传入 static,越界索引会被填成 `Pubkey::default()`11111...)。
- **影响协议/指令**:所有依赖「按索引取账户」的指令,在交易使用 ALT 时都可能出现错误或默认账户。
- **典型表现**
- **PumpFun**Create / CreateV2 的 token_program、global、event_authority 等为 11111...Buy/Sell 的 creator_vault、token_program 等可能错误。
- **PumpSwap / Bonk / Raydium / Meteora**:依赖高索引账户的指令同样可能得到错误或 default 账户。
- **建议**:若需完整且正确的账户字段,请使用 gRPC 订阅。
### 2.2 无 Inner Instructions → 无 CPI 合并
- **原因**Shred/Entry 只包含原始 `VersionedTransaction`inner instructions 是执行阶段产物,不在 shred 载荷中。
- **影响**
- **PumpFun**
- Create / CreateV2:无 CPI 合并 → `timestamp``virtual_*_reserves``real_*_reserves``token_total_supply``token_program`(来自 log)等多为 0 或默认。
- Trade:无 CPI 合并 → 无 log 中的成交额、reserves、fee 等明细,仅保留指令层数据。
- Migrate:此前因「必须带 CPI」被直接跳过,**现已改为** shred 下仍发出仅含指令数据的 Migrate 事件(user/mint 等来自指令账户;mint_amount、sol_amount、timestamp、pool 等来自 CPI 的字段为 0/默认)。
- **PumpSwap**buy/sell/deposit/withdraw/create_pool 无 CPI 合并,无 log 中的金额、reserves 等;**swap_data** 依赖后续指令解析,inner 为空时恒为空。
- **Bonk**trade、pool_create 无 CPI 合并,缺少 log 明细。
- **Meteora Damm V2**swap、initialize_pool 无 CPI 合并。
- **Raydium**:依赖 inner 的解析/合并与 gRPC 一致缺失。
### 2.3 漏掉的事件:仅通过 CPI 触发的调用
- **原因**:Shred 路径只遍历并解析**外层指令**(`transaction.message.instructions()`)。内层指令(inner instructions)只有在传入非空的 `inner_instructions` 时才会被解析;shred 传入的为 `&[]`,因此**从不**解析任何 inner。
- **结果**:当协议**仅作为 CPI 被调用**时(例如用户通过 Jupiter/Raydium 聚合器等路由,外层指令是聚合器,PumpFun/PumpSwap 等只在 inner 中出现),gRPC 会解析该 inner 并发出对应事件,**shred 则整笔交易都不会产生该协议的任何事件**。
- **影响**:所有协议(PumpFun、PumpSwap、Bonk、Raydium、Meteora 等)在「仅 CPI 调用」场景下,shred 都会**漏掉整笔事件**,不是字段缺失,而是事件本身不会出现。
- **建议**:若需要统计或处理通过聚合器/路由产生的交易,必须使用 gRPC 订阅;shred 只适合「用户直接与协议交互」的链路。
### 2.4 其他明确「漏掉」或弱化的解析
- **PumpFun Migrate**shred 下**会**发出事件,但仅包含指令解析出的账户与部分字段(如 user、mint);mint_amount、sol_amount、pool_migration_fee、timestamp、pool 等来自 CPI 的字段为 0/默认。
- **所有协议的 CPI 维度的数据**:shred 路径一律缺失(无 inner instructions 即无 CPI 解析与 merge)。
## 3. 使用建议
- 需要**完整、正确**的账户与 log 字段(reserves、timestamp、amounts、swap_data 等)时,使用 **gRPC 订阅**
- Shredstream 更适合:对延迟更敏感、可接受「仅指令层 + 部分字段缺失/默认」的场景,或确认交易**未使用 ALT** 时的账户解析。
## 4. 各事件 Shred 路径字段完整性
以下为「直接外层调用」场景下,shred 能拿到的字段 vs 仅 CPI 合并才有的字段(shred 下为 0/默认)。若交易使用 ALT,标注为「指令」的账户类字段也可能错误或为 default。
**元数据(所有事件)**
- Shred 有:signature, slot, recv_us, program_id, outer_index, tx_indexentry 内索引), event_type, protocol
- Shred 缺失:**block_time / block_time_ms**(恒为 0),**swap_data**(恒为 None,依赖 inner 后续指令解析)
### 4.1 PumpFun
| 事件 | 指令解析有(Shred 有) | 仅 CPI 合并有(Shred 缺失) |
|------|------------------------|-----------------------------|
| **CreateToken** | name, symbol, uri, creator, mint, 各账户(0..13) | timestamp, virtual_*_reserves, real_*_reserves, token_total_supply, token_program(来自 log), is_mayhem_mode, is_cashback_enabled |
| **CreateV2Token** | name, symbol, uri, creator, mint, 各账户(0..15) | timestamp, virtual_*_reserves, real_*_reserves, token_total_supply, token_program(来自 log), is_mayhem_mode, is_cashback_enabled |
| **Trade** (Buy/Sell) | is_buy, amount/max_sol_cost/min_sol_output, 各账户(含 user, mint, creator_vault 等) | sol_amount, token_amount, timestamp, virtual_*_reserves, real_*_reserves, fee_recipient, fee_basis_points, fee, creator, creator_fee_*, track_volume, total_unclaimed/claimed_tokens, current_sol_volume, last_update_timestamp, ix_name, mayhem_mode, cashback_* |
| **Migrate** | user, mint, bonding_curve, 全部 24 个账户 | mint_amount, sol_amount, pool_migration_fee, timestamp, poolCPI 的 pool |
### 4.2 PumpSwap
| 事件 | 指令解析有(Shred 有) | 仅 CPI 合并有(Shred 缺失) |
|------|------------------------|-----------------------------|
| **Buy** | base_amount_out, max_quote_amount_in, pool, user, base_mint, quote_mint, 各 token account / fee recipient / programcoin_creator_vault_ata/authority(若 accounts≥19) | timestamp, 实际 quote_amount_in, user/pool *_reserves, lp_fee, protocol_fee, coin_creator_fee_*, track_volume, total_unclaimed/claimed_tokens, current_sol_volume, last_update_timestamp |
| **Sell** | base_amount_in, min_quote_amount_out, pool, user, base_mint, quote_mint, 各账户,coin_creator_vault_* | timestamp, 实际 quote_amount_out, *_reserves, 各项 fee, coin_creator_fee_* |
| **CreatePool** | index, base_amount_in, quote_amount_in, coin_creator(若 data≥50), pool, creator, base/quote_mint, lp_mint, 各 token account | timestamp, base_mint_decimals, quote_mint_decimals, pool_base/quote_amount, minimum/initial_liquidity, lp_token_amount_out, pool_bump |
| **Deposit** | lp_token_amount_out, max_base/quote_amount_in, pool, user, 各 mint / token account | timestamp, user/pool *_reserves, base_amount_in, quote_amount_in, lp_mint_supply 等 |
| **Withdraw** | lp_token_amount_in, min_base/quote_amount_out, pool, user, 各账户 | timestamp, *_reserves, base/quote_amount_out, lp_mint_supply 等 |
### 4.3 Bonk
| 事件 | 指令解析有(Shred 有) | 仅 CPI 合并有(Shred 缺失) |
|------|------------------------|-----------------------------|
| **Trade** | amount_in/out, minimum/maximum_*, share_fee_rate, payer, pool_state, 各 vault/mint/program 账户, trade_direction | pool_state(来自 log), total_base_sell, virtual_base/quote, real_*_before/after, amount_in/out(实际成交), protocol_fee, platform_fee, creator_fee, share_fee, pool_status, exact_in |
| **PoolCreate** | payer, creator, global_config, platform_config, pool_state, base/quote_mint, base/quote_vault, base_mint_param, curve_param, vesting_param(, amm_fee_on for V2) | config, base_mint_param/curve_param/vesting_param(来自 log 的完整值), amm_fee_on(来自 log) |
| **MigrateToAmm / MigrateToCpswap** | 指令侧账户与参数 | base_lot_size, quote_lot_size, market_vault_signer_nonceCPI 才有) |
### 4.4 Raydium / Meteora Damm V2
- **Raydium CLMM/CPMM/AMM**:指令解析会填账户与指令内参数(如 amount、min_out 等);实际成交额、reserves、fee 等来自 log 的字段在 shred 下均为 0/默认。
- **Meteora Damm V2**Swap / InitializePool 等同上,指令层有账户与部分参数,CPI 的 timestamp、reserves、实际 amount 等 shred 缺失。
## 5. 代码位置参考
- Shred 入口:`streaming/common/event_processor.rs``process_shred_transaction`
- 账户与 inner 传入:`accounts = tx.message.static_account_keys()``inner_instructions: &[]`
- 合并逻辑(CPI 覆盖/补充字段):`streaming/event_parser/core/merger_event.rs``merge()`