From e32e7ee6ab1af20cee1cf754a702d44acac16624 Mon Sep 17 00:00:00 2001 From: Wood Date: Sat, 7 Mar 2026 11:45:33 +0800 Subject: [PATCH] docs: creator_vault/coin_creator from events; CODE_REVIEW three-repo check Made-with: Cursor --- README.md | 18 ++++++++++++++++++ README_CN.md | 18 ++++++++++++++++++ docs/CODE_REVIEW_REPORT.md | 35 ++++++++++++++++++++++++++++++++++- src/trading/core/params.rs | 17 ++++++++++++++++- 4 files changed, 86 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1a9f879..24b0b29 100644 --- a/README.md +++ b/README.md @@ -321,6 +321,24 @@ PumpFun and PumpSwap support **cashback** for eligible tokens: part of the tradi - The **pumpfun_copy_trading** and **pumpfun_sniper_trading** examples use sol-parser-sdk for gRPC subscription and pass `e.is_cashback_coin` when building params. - **Claim**: Use `client.claim_cashback_pumpfun()` and `client.claim_cashback_pumpswap(...)` to claim accumulated cashback. +#### PumpFun: Creator Rewards Sharing (creator_vault) + +Some PumpFun coins use **Creator Rewards Sharing**, so the on-chain `creator_vault` can differ from the default derivation. If you reuse cached params from a **buy** when **selling**, you may see program error **2006 (seeds constraint violated)**. To avoid this: + +- **From gRPC/events (no RPC needed)**: You can get both `creator` and `creator_vault` from parsed transaction events: + - **sol-parser-sdk**: Before pushing events, the pipeline calls `fill_trade_accounts`, which fills `creator_vault` from the buy/sell instruction accounts (buy index 9, sell index 8). `creator` comes from the TradeEvent log. Use `PumpFunParams::from_trade(..., e.creator, e.creator_vault, ...)` or `from_dev_trade(..., e.creator, e.creator_vault, ...)` with the event `e`. + - **solana-streamer**: Instruction parsers set `creator_vault` from accounts[9] (buy) or accounts[8] (sell); `creator` comes from the merged CPI TradeEvent log. Use the same `from_trade` / `from_dev_trade` with `e.creator` and `e.creator_vault`. +- **Override after RPC**: If you get params via `PumpFunParams::from_mint_by_rpc` but later receive a newer `creator_vault` from gRPC, call `.with_creator_vault(latest_creator_vault)` on the params before selling. + +The SDK does not fetch creator_vault from RPC on every sell (to avoid latency); pass the up-to-date vault from gRPC/events when available. + +#### PumpSwap: coin_creator_vault from events (no RPC) + +For **PumpSwap** (Pump AMM), `coin_creator_vault_ata` and `coin_creator_vault_authority` are required in buy/sell instructions. Both are available from parsed events without RPC: + +- **sol-parser-sdk**: Instruction parser sets them from accounts 17 and 18; the account filler also fills them when the event comes from logs. Use `PumpSwapParams::from_trade(..., e.coin_creator_vault_ata, e.coin_creator_vault_authority, ...)` with the buy/sell event `e`. +- **solana-streamer**: Instruction parser sets them from `accounts.get(17)` and `accounts.get(18)`. Use the same `from_trade` with the event’s `coin_creator_vault_ata` and `coin_creator_vault_authority`. + ## 🛡️ MEV Protection Services You can apply for a key through the official website: [Community Website](https://fnzero.dev/swqos) diff --git a/README_CN.md b/README_CN.md index 41a2820..e1039d2 100755 --- a/README_CN.md +++ b/README_CN.md @@ -320,6 +320,24 @@ PumpFun 与 PumpSwap 支持**返现(Cashback)**:部分手续费可返还 - **pumpfun_copy_trading**、**pumpfun_sniper_trading** 示例使用 sol-parser-sdk 订阅 gRPC 事件,并在构造参数时传入 `e.is_cashback_coin`。 - **领取返现**:使用 `client.claim_cashback_pumpfun()` 和 `client.claim_cashback_pumpswap(...)` 领取累计的返现。 +#### PumpFun:Creator Rewards Sharing(creator_vault) + +部分 PumpFun 代币启用了 **Creator Rewards Sharing**,链上 `creator_vault` 可能与默认推导结果不同。若在**卖出**时复用**买入**时缓存的 params,可能触发程序错误 **2006(seeds constraint violated)**。建议: + +- **来自 gRPC/事件(无需 RPC)**:`creator` 与 `creator_vault` 均可从解析后的事件中直接拿到: + - **sol-parser-sdk**:推送前会调用 `fill_trade_accounts`,从 buy/sell 指令账户补全 `creator_vault`(buy 索引 9,sell 索引 8);`creator` 来自 TradeEvent 日志。用 `PumpFunParams::from_trade(..., e.creator, e.creator_vault, ...)` 或 `from_dev_trade(..., e.creator, e.creator_vault, ...)` 即可。 + - **solana-streamer**:指令解析时从 accounts[9](buy)/ accounts[8](sell)写入 `creator_vault`;`creator` 来自合并后的 CPI TradeEvent 日志。同样用事件的 `e.creator`、`e.creator_vault` 调用 `from_trade` / `from_dev_trade`。 +- **RPC 后覆盖**:若通过 `PumpFunParams::from_mint_by_rpc` 得到 params,之后又从 gRPC 拿到更新的 `creator_vault`,在卖出前对 params 调用 `.with_creator_vault(latest_creator_vault)`。 + +SDK 不会在每次卖出时通过 RPC 拉取 creator_vault(以避免延迟);请从 gRPC/事件中传入最新 vault。 + +#### PumpSwap:从事件拿 coin_creator_vault(无需 RPC) + +**PumpSwap**(Pump AMM)的 buy/sell 指令需要 `coin_creator_vault_ata` 与 `coin_creator_vault_authority`,二者均可从解析事件中拿到,无需 RPC: + +- **sol-parser-sdk**:指令解析从账户 17、18 写入;若事件来自日志,账户填充器也会从指令补全。用 `PumpSwapParams::from_trade(..., e.coin_creator_vault_ata, e.coin_creator_vault_authority, ...)` 即可。 +- **solana-streamer**:指令解析从 `accounts.get(17)`、`accounts.get(18)` 写入。同样用事件的 `coin_creator_vault_ata`、`coin_creator_vault_authority` 调用 `from_trade`。 + ## 🛡️ MEV 保护服务 可以通过官网申请密钥:[社区官网](https://fnzero.dev/swqos) diff --git a/docs/CODE_REVIEW_REPORT.md b/docs/CODE_REVIEW_REPORT.md index b1e795e..a6f2981 100644 --- a/docs/CODE_REVIEW_REPORT.md +++ b/docs/CODE_REVIEW_REPORT.md @@ -111,7 +111,40 @@ --- -## 7. 汇总:必须改 vs 建议改 +## 7. 三库联动与超低延迟检查(sol-trade-sdk / sol-parser-sdk / solana-streamer) + +### 7.1 逻辑一致性(已确认) + +| 检查项 | sol-parser-sdk | solana-streamer | 说明 | +|--------|----------------|-----------------|------| +| Pump buy 账户数 | 16(fill 用 get(9) 填 creator_vault) | 16,accounts[9]=creator_vault | 与 idl/pumpfun.json 一致 | +| Pump sell 账户数 | 14(get(8)=creator_vault) | 14,accounts[8]=creator_vault | 一致 | +| PumpSwap buy 17/18 | 指令解析 + fill_buy_accounts get(17)/get(18) | 指令解析 accounts.get(17)/get(18) | coin_creator_vault_ata/authority 正确 | +| PumpSwap sell 17/18 | fill_sell_accounts 同左 | 同左 | 一致 | +| 填充顺序 | 先 parse(log 或 instruction)→ fill_accounts → push | 指令解析直接写 17/18;无单独 fill 步骤 | 两者均保证事件带齐 creator_vault / coin_creator_vault | +| find_instruction_invoke | 选「账户数最多」的 invoke,保证取到 outer buy/sell | N/A(按当前 instruction 解析) | 正确 | + +### 7.2 版本化交易账户解析 + +- **sol-parser-sdk**:`get_instruction_account_getter` 正确支持 versioned tx:先 `account_keys`,再 `loaded_writable_addresses`,再 `loaded_readonly_addresses`,与 Solana 约定一致。 +- **solana-streamer**:指令的 `accounts` 为索引,通过 `accounts.get(idx as usize).copied()` 从完整 `accounts: &[Pubkey]` 解析;调用方需传入已包含 loaded 的完整账户列表,否则高索引会得到 `default()`。 + +### 7.3 超低延迟相关 + +| 项目 | 状态 / 建议 | +|------|-------------| +| solana-streamer 热路径 | 已改为**顺序执行** inner 解析与 swap_data 提取,去掉 `thread::scope` + 双 spawn/join,减少 μs 级开销。 | +| sol-parser-sdk | log 与 instruction 并行(rayon::join);fill 仅在有 invoke 时做;`find_instruction_invoke` 为 O(invokes),单程序单 tx 下可接受。 | +| sol-trade-sdk | 见上文第 4 节;PumpSwap instruction 构建避免 `accounts.clone()` 已列为必须改。 | + +### 7.4 建议 + +- **solana-streamer**:若 gRPC 上游已提供完整 `accounts`(含 loaded),可避免对每笔 tx 做 `accounts.to_vec()`,仅在需要 resize 时克隆,进一步降低分配。 +- **三库**:保持 IDL 与账户索引注释同步(pumpfun.json / pump_amm.json),避免后续扩展时索引错位。 + +--- + +## 8. 汇总:必须改 vs 建议改 ### 必须改(优先处理) diff --git a/src/trading/core/params.rs b/src/trading/core/params.rs index 417d371..461dbb7 100755 --- a/src/trading/core/params.rs +++ b/src/trading/core/params.rs @@ -89,11 +89,18 @@ impl std::fmt::Debug for SwapParams { } /// PumpFun protocol specific parameters -/// Configuration parameters specific to PumpFun trading protocol +/// Configuration parameters specific to PumpFun trading protocol. +/// +/// **Creator Rewards Sharing**: Some coins use a dynamic `creator_vault` (fee-sharing config). +/// Always use the latest on-chain creator/vault when building params for **sell**; do not reuse +/// cached params from buy. Either fetch fresh data via RPC, or pass `creator_vault` from gRPC +/// using [`from_trade`](PumpFunParams::from_trade) / [`from_dev_trade`](PumpFunParams::from_dev_trade), +/// or override with [`with_creator_vault`](PumpFunParams::with_creator_vault). #[derive(Clone)] pub struct PumpFunParams { pub bonding_curve: Arc, pub associated_bonding_curve: Pubkey, + /// Creator vault PDA. For Creator Rewards Sharing coins this can change; pass latest from gRPC when selling. pub creator_vault: Pubkey, pub token_program: Pubkey, /// Whether to close token account when selling, only effective during sell operations @@ -222,6 +229,14 @@ impl PumpFunParams { token_program: mint_account.owner, }) } + + /// Override `creator_vault` with a value from gRPC/event (e.g. for Creator Rewards Sharing). + /// Use when selling so the instruction uses the latest on-chain vault and avoids "seeds constraint violated" (2006). + #[inline] + pub fn with_creator_vault(mut self, creator_vault: Pubkey) -> Self { + self.creator_vault = creator_vault; + self + } } /// PumpSwap Protocol Specific Parameters