mirror of
https://github.com/0xfnzero/solana-streamer.git
synced 2026-11-02 07:16:31 +00:00
release: prepare solana-streamer-sdk 3.0.3
This commit is contained in:
+2
-2
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-streamer-sdk"
|
||||
version = "3.0.2"
|
||||
version = "3.0.3"
|
||||
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.2", default-features = false }
|
||||
sol-parser-sdk = { path = "../sol-parser-sdk", version = "0.7.3", default-features = false }
|
||||
solana-sdk = "=4.0.1"
|
||||
solana-client = "=4.2.2"
|
||||
solana-transaction-status = { version = "=4.2.2", features = ["agave-unstable-api"] }
|
||||
|
||||
@@ -129,30 +129,34 @@ Add the dependency to your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
# Add to your Cargo.toml
|
||||
solana-streamer-sdk = { path = "./solana-streamer", version = "3.0.2" }
|
||||
solana-streamer-sdk = { path = "./solana-streamer", version = "3.0.3" }
|
||||
```
|
||||
|
||||
### Use crates.io
|
||||
|
||||
```toml
|
||||
# Add to your Cargo.toml
|
||||
solana-streamer-sdk = "3.0.2"
|
||||
solana-streamer-sdk = "3.0.3"
|
||||
```
|
||||
|
||||
Parser backend features:
|
||||
|
||||
```toml
|
||||
# Default: sol-parser-sdk parse-borsh backend
|
||||
solana-streamer-sdk = "3.0.2"
|
||||
solana-streamer-sdk = "3.0.3"
|
||||
|
||||
# Zero-copy parser backend for latency-sensitive bots
|
||||
solana-streamer-sdk = { version = "3.0.2", default-features = false, features = ["sdk-parse-zero-copy"] }
|
||||
solana-streamer-sdk = { version = "3.0.3", 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.
|
||||
|
||||
## 🔄 Migration Guide
|
||||
|
||||
### Upgrading to v3.0.3
|
||||
|
||||
Version 3.0.3 uses `sol-parser-sdk 0.7.3` and synchronizes the current PumpFun, PumpSwap, and Pump Fees protocol schemas. PumpFun create/trade events and PumpSwap buy/sell/create-pool events now preserve creator-fee and holder-reward fields through the streamer bridge. PumpFun bonding-curve and PumpSwap pool account events also expose the new creator-fee editability and holder-reward state while retaining defaults for legacy accounts and JSON payloads. The parser rejects truncated current PumpSwap trade and CreatePool tails instead of returning partially populated events.
|
||||
|
||||
### Upgrading to v3.0.2
|
||||
|
||||
Version 3.0.2 uses `sol-parser-sdk 0.7.2` and forwards the new Meteora DAMM v2 events: `UpdateDelegatePermission`, `WithdrawDeadLiquidityReward`, `CreateConfig`, and `CreateDynamicConfig`. Transaction-cost events preserve all four Solana V1 config requests, and Yellowstone V1 messages are distinguished from V0 through `Message.config`. V1 ingestion requires a Yellowstone geyser server plugin version of at least `15.1.1`; older servers silently discard the config while downgrading V1 to V0.
|
||||
|
||||
+8
-4
@@ -128,30 +128,34 @@ git clone https://github.com/0xfnzero/solana-streamer
|
||||
|
||||
```toml
|
||||
# 添加到您的 Cargo.toml
|
||||
solana-streamer-sdk = { path = "./solana-streamer", version = "3.0.2" }
|
||||
solana-streamer-sdk = { path = "./solana-streamer", version = "3.0.3" }
|
||||
```
|
||||
|
||||
### 使用 crates.io
|
||||
|
||||
```toml
|
||||
# 添加到您的 Cargo.toml
|
||||
solana-streamer-sdk = "3.0.2"
|
||||
solana-streamer-sdk = "3.0.3"
|
||||
```
|
||||
|
||||
解析后端 feature:
|
||||
|
||||
```toml
|
||||
# 默认:sol-parser-sdk parse-borsh 后端
|
||||
solana-streamer-sdk = "3.0.2"
|
||||
solana-streamer-sdk = "3.0.3"
|
||||
|
||||
# 面向低延迟 Bot 的 zero-copy 解析后端
|
||||
solana-streamer-sdk = { version = "3.0.2", default-features = false, features = ["sdk-parse-zero-copy"] }
|
||||
solana-streamer-sdk = { version = "3.0.3", default-features = false, features = ["sdk-parse-zero-copy"] }
|
||||
```
|
||||
|
||||
如果同时启用 `sdk-parse-borsh` 和 `sdk-parse-zero-copy`,`sol-parser-sdk 0.6.1+` 会优先使用 zero-copy 后端。
|
||||
|
||||
## 🔄 迁移指南
|
||||
|
||||
### 升级到 v3.0.3
|
||||
|
||||
v3.0.3 使用 `sol-parser-sdk 0.7.3`,同步当前 PumpFun、PumpSwap 和 Pump Fees 协议 schema。PumpFun create/trade 事件以及 PumpSwap buy/sell/create-pool 事件现在会通过 streamer bridge 完整保留 creator fee 与 holder reward 字段;PumpFun bonding curve 和 PumpSwap pool 账户事件也会暴露新的 creator fee 可编辑状态与 holder reward 状态,同时继续为旧账户布局和旧 JSON 数据提供默认值。解析器会拒绝截断的当前 PumpSwap trade 和 CreatePool 尾部,不再返回字段不完整的事件。
|
||||
|
||||
### 升级到 v3.0.2
|
||||
|
||||
v3.0.2 使用 `sol-parser-sdk 0.7.2`,并转发新增的 Meteora DAMM v2 事件:`UpdateDelegatePermission`、`WithdrawDeadLiquidityReward`、`CreateConfig`、`CreateDynamicConfig`。TransactionCost 事件会完整保留 Solana V1 的四项 config 请求,并通过 `Message.config` 区分 V1 与 V0。接入 V1 要求 Yellowstone geyser 服务端插件至少为 `15.1.1`;更旧的服务端会静默丢弃 config 并把 V1 降级成 V0。
|
||||
|
||||
@@ -104,6 +104,8 @@ pub fn merge(instruction_event: &mut DexEvent, cpi_log_event: DexEvent) {
|
||||
fill_u64(&mut e.quote_amount, cpie.quote_amount);
|
||||
fill_u64(&mut e.virtual_quote_reserves, cpie.virtual_quote_reserves);
|
||||
fill_u64(&mut e.real_quote_reserves, cpie.real_quote_reserves);
|
||||
overwrite_u64_if_present(&mut e.holder_rewards_bps, cpie.holder_rewards_bps);
|
||||
overwrite_u64_if_present(&mut e.holder_rewards, cpie.holder_rewards);
|
||||
e.is_cashback_coin |= cpie.is_cashback_coin;
|
||||
e.is_created_buy |= cpie.is_created_buy;
|
||||
fill_u64(&mut e.amount, cpie.amount);
|
||||
@@ -193,8 +195,10 @@ pub fn merge(instruction_event: &mut DexEvent, cpi_log_event: DexEvent) {
|
||||
fill_pubkey(&mut e.quote_vault, cpie.quote_vault);
|
||||
fill_pubkey(&mut e.quote_token_program, cpie.quote_token_program);
|
||||
fill_u64(&mut e.virtual_quote_reserves, cpie.virtual_quote_reserves);
|
||||
overwrite_u64_if_present(&mut e.creator_fee_bps, cpie.creator_fee_bps);
|
||||
e.is_mayhem_mode |= cpie.is_mayhem_mode;
|
||||
e.is_cashback_enabled |= cpie.is_cashback_enabled;
|
||||
e.is_holder_reward |= cpie.is_holder_reward;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
@@ -217,8 +221,10 @@ pub fn merge(instruction_event: &mut DexEvent, cpi_log_event: DexEvent) {
|
||||
fill_pubkey(&mut e.quote_vault, cpie.quote_vault);
|
||||
fill_pubkey(&mut e.quote_token_program, cpie.quote_token_program);
|
||||
fill_u64(&mut e.virtual_quote_reserves, cpie.virtual_quote_reserves);
|
||||
overwrite_u64_if_present(&mut e.creator_fee_bps, cpie.creator_fee_bps);
|
||||
e.is_mayhem_mode |= cpie.is_mayhem_mode;
|
||||
e.is_cashback_enabled |= cpie.is_cashback_enabled;
|
||||
e.is_holder_reward |= cpie.is_holder_reward;
|
||||
}
|
||||
DexEvent::PumpFunCreateV2TokenEvent(cpie) => {
|
||||
fill_string(&mut e.name, cpie.name);
|
||||
@@ -258,8 +264,10 @@ pub fn merge(instruction_event: &mut DexEvent, cpi_log_event: DexEvent) {
|
||||
fill_pubkey(&mut e.quote_vault, cpie.quote_vault);
|
||||
fill_pubkey(&mut e.quote_token_program, cpie.quote_token_program);
|
||||
fill_u64(&mut e.virtual_quote_reserves, cpie.virtual_quote_reserves);
|
||||
overwrite_u64_if_present(&mut e.creator_fee_bps, cpie.creator_fee_bps);
|
||||
e.is_mayhem_mode |= cpie.is_mayhem_mode;
|
||||
e.is_cashback_enabled |= cpie.is_cashback_enabled;
|
||||
e.is_holder_reward |= cpie.is_holder_reward;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
@@ -392,6 +400,8 @@ pub fn merge(instruction_event: &mut DexEvent, cpi_log_event: DexEvent) {
|
||||
);
|
||||
e.can_boost |= cpie.can_boost;
|
||||
overwrite_u64_if_present(&mut e.base_supply, cpie.base_supply);
|
||||
overwrite_u64_if_present(&mut e.holder_rewards_bps, cpie.holder_rewards_bps);
|
||||
overwrite_u64_if_present(&mut e.holder_rewards, cpie.holder_rewards);
|
||||
e.is_pump_pool |= cpie.is_pump_pool;
|
||||
fill_pubkey(&mut e.base_mint, cpie.base_mint);
|
||||
fill_pubkey(&mut e.quote_mint, cpie.quote_mint);
|
||||
@@ -451,6 +461,8 @@ pub fn merge(instruction_event: &mut DexEvent, cpi_log_event: DexEvent) {
|
||||
);
|
||||
e.can_boost |= cpie.can_boost;
|
||||
overwrite_u64_if_present(&mut e.base_supply, cpie.base_supply);
|
||||
overwrite_u64_if_present(&mut e.holder_rewards_bps, cpie.holder_rewards_bps);
|
||||
overwrite_u64_if_present(&mut e.holder_rewards, cpie.holder_rewards);
|
||||
e.is_pump_pool |= cpie.is_pump_pool;
|
||||
fill_pubkey(&mut e.base_mint, cpie.base_mint);
|
||||
fill_pubkey(&mut e.quote_mint, cpie.quote_mint);
|
||||
@@ -493,6 +505,9 @@ pub fn merge(instruction_event: &mut DexEvent, cpi_log_event: DexEvent) {
|
||||
e.coin_creator = cpie.coin_creator;
|
||||
e.is_mayhem_mode = cpie.is_mayhem_mode;
|
||||
e.is_cashback_coin |= cpie.is_cashback_coin;
|
||||
overwrite_u64_if_present(&mut e.creator_fee_bps, cpie.creator_fee_bps);
|
||||
e.can_edit_creator_fee |= cpie.can_edit_creator_fee;
|
||||
e.is_holder_reward |= cpie.is_holder_reward;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
@@ -934,9 +949,11 @@ mod tests {
|
||||
MeteoraDammV2Swap2Event, MeteoraDammV2SwapEvent,
|
||||
};
|
||||
use crate::streaming::event_parser::protocols::pumpfun::events::{
|
||||
PumpFeesShareholder, PumpFunTradeEvent,
|
||||
PumpFeesShareholder, PumpFunCreateTokenEvent, PumpFunCreateV2TokenEvent, PumpFunTradeEvent,
|
||||
};
|
||||
use crate::streaming::event_parser::protocols::pumpswap::events::{
|
||||
PumpSwapCreatePoolEvent, PumpSwapSellEvent,
|
||||
};
|
||||
use crate::streaming::event_parser::protocols::pumpswap::events::PumpSwapSellEvent;
|
||||
|
||||
#[test]
|
||||
fn damm_v2_merge_copies_all_current_fee_fields() {
|
||||
@@ -1004,6 +1021,8 @@ mod tests {
|
||||
quote_amount: 500,
|
||||
virtual_quote_reserves: 700,
|
||||
real_quote_reserves: 800,
|
||||
holder_rewards_bps: 250,
|
||||
holder_rewards: 125,
|
||||
is_created_buy: true,
|
||||
..Default::default()
|
||||
});
|
||||
@@ -1026,6 +1045,8 @@ mod tests {
|
||||
assert_eq!(t.quote_amount, 500);
|
||||
assert_eq!(t.virtual_quote_reserves, 700);
|
||||
assert_eq!(t.real_quote_reserves, 800);
|
||||
assert_eq!(t.holder_rewards_bps, 250);
|
||||
assert_eq!(t.holder_rewards, 125);
|
||||
assert!(t.is_created_buy);
|
||||
}
|
||||
_ => panic!("expected PumpFunTradeEvent"),
|
||||
@@ -1072,6 +1093,8 @@ mod tests {
|
||||
buyback_fee: 22,
|
||||
can_boost: true,
|
||||
base_supply: 33,
|
||||
holder_rewards_bps: 44,
|
||||
holder_rewards: 55,
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
@@ -1085,6 +1108,8 @@ mod tests {
|
||||
assert_eq!(event.buyback_fee, 22);
|
||||
assert!(event.can_boost);
|
||||
assert_eq!(event.base_supply, 33);
|
||||
assert_eq!(event.holder_rewards_bps, 44);
|
||||
assert_eq!(event.holder_rewards, 55);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1097,6 +1122,8 @@ mod tests {
|
||||
virtual_quote_reserves: -500,
|
||||
can_boost: true,
|
||||
base_supply: 60,
|
||||
holder_rewards_bps: 70,
|
||||
holder_rewards: 80,
|
||||
is_pump_pool: true,
|
||||
..Default::default()
|
||||
});
|
||||
@@ -1113,9 +1140,54 @@ mod tests {
|
||||
assert_eq!(event.virtual_quote_reserves, -500);
|
||||
assert!(event.can_boost);
|
||||
assert_eq!(event.base_supply, 60);
|
||||
assert_eq!(event.holder_rewards_bps, 70);
|
||||
assert_eq!(event.holder_rewards, 80);
|
||||
assert!(event.is_pump_pool);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pumpfun_create_merge_preserves_holder_reward_fields() {
|
||||
let mut instruction_event = DexEvent::PumpFunCreateTokenEvent(PumpFunCreateTokenEvent {
|
||||
creator_fee_bps: 125,
|
||||
..Default::default()
|
||||
});
|
||||
let log_event = DexEvent::PumpFunCreateV2TokenEvent(PumpFunCreateV2TokenEvent {
|
||||
creator_fee_bps: 250,
|
||||
is_holder_reward: true,
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
merge(&mut instruction_event, log_event);
|
||||
|
||||
let DexEvent::PumpFunCreateTokenEvent(event) = instruction_event else {
|
||||
panic!("expected PumpFunCreateTokenEvent");
|
||||
};
|
||||
assert_eq!(event.creator_fee_bps, 250);
|
||||
assert!(event.is_holder_reward);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pumpswap_create_pool_merge_preserves_holder_reward_fields() {
|
||||
let mut instruction_event = DexEvent::PumpSwapCreatePoolEvent(PumpSwapCreatePoolEvent {
|
||||
creator_fee_bps: 125,
|
||||
can_edit_creator_fee: true,
|
||||
is_holder_reward: true,
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
merge(
|
||||
&mut instruction_event,
|
||||
DexEvent::PumpSwapCreatePoolEvent(PumpSwapCreatePoolEvent::default()),
|
||||
);
|
||||
|
||||
let DexEvent::PumpSwapCreatePoolEvent(event) = instruction_event else {
|
||||
panic!("expected PumpSwapCreatePoolEvent");
|
||||
};
|
||||
assert_eq!(event.creator_fee_bps, 125);
|
||||
assert!(event.can_edit_creator_fee);
|
||||
assert!(event.is_holder_reward);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pumpfun_merge_replaces_sol_quote_sentinel_with_real_quote_mint() {
|
||||
let quote_mint = Pubkey::new_unique();
|
||||
|
||||
@@ -36,6 +36,14 @@ pub struct PumpFunCreateTokenEvent {
|
||||
pub quote_token_program: Pubkey,
|
||||
#[borsh(skip)]
|
||||
pub virtual_quote_reserves: u64,
|
||||
/// Coin-specific creator fee rate. Zero uses the standard fee schedule.
|
||||
#[borsh(skip)]
|
||||
#[serde(default)]
|
||||
pub creator_fee_bps: u64,
|
||||
/// Whether creator fees are distributed to holders.
|
||||
#[borsh(skip)]
|
||||
#[serde(default)]
|
||||
pub is_holder_reward: bool,
|
||||
/// Original PumpFun instruction name: "create" or "create_v2".
|
||||
#[borsh(skip)]
|
||||
pub ix_name: String,
|
||||
@@ -103,6 +111,12 @@ pub struct PumpFunCreateV2TokenEvent {
|
||||
#[borsh(skip)]
|
||||
pub virtual_quote_reserves: u64,
|
||||
#[borsh(skip)]
|
||||
#[serde(default)]
|
||||
pub creator_fee_bps: u64,
|
||||
#[borsh(skip)]
|
||||
#[serde(default)]
|
||||
pub is_holder_reward: bool,
|
||||
#[borsh(skip)]
|
||||
pub mint_authority: Pubkey,
|
||||
#[borsh(skip)]
|
||||
pub associated_bonding_curve: Pubkey,
|
||||
@@ -226,6 +240,14 @@ pub struct PumpFunTradeEvent {
|
||||
pub virtual_quote_reserves: u64,
|
||||
#[borsh(skip)]
|
||||
pub real_quote_reserves: u64,
|
||||
/// Holder rewards fee rate. Zero for regular coins and legacy events.
|
||||
#[borsh(skip)]
|
||||
#[serde(default)]
|
||||
pub holder_rewards_bps: u64,
|
||||
/// Holder rewards amount. Zero for regular coins and legacy events.
|
||||
#[borsh(skip)]
|
||||
#[serde(default)]
|
||||
pub holder_rewards: u64,
|
||||
/// Whether this is a cashback coin (cashback_fee_basis_points > 0)
|
||||
#[borsh(skip)]
|
||||
pub is_cashback_coin: bool,
|
||||
|
||||
@@ -14,9 +14,17 @@ pub struct BondingCurve {
|
||||
pub is_mayhem_mode: bool,
|
||||
pub is_cashback_coin: bool,
|
||||
pub quote_mint: Pubkey,
|
||||
#[serde(default)]
|
||||
pub creator_fee_bps: u64,
|
||||
#[serde(default)]
|
||||
pub can_edit_creator_fee: bool,
|
||||
#[serde(default)]
|
||||
pub is_holder_reward: bool,
|
||||
}
|
||||
|
||||
pub const BONDING_CURVE_SIZE: usize = 8 * 5 + 1 + 32 + 1 + 1 + 32;
|
||||
pub const BONDING_CURVE_LEGACY_SIZE: usize = 8 * 5 + 1 + 32 + 1 + 1 + 32;
|
||||
pub const BONDING_CURVE_CREATOR_FEE_SIZE: usize = BONDING_CURVE_LEGACY_SIZE + 8 + 1;
|
||||
pub const BONDING_CURVE_SIZE: usize = BONDING_CURVE_CREATOR_FEE_SIZE + 1;
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
|
||||
pub struct Global {
|
||||
|
||||
@@ -54,6 +54,10 @@ pub struct PumpSwapBuyEvent {
|
||||
pub can_boost: bool,
|
||||
#[serde(default)]
|
||||
pub base_supply: u64,
|
||||
#[serde(default)]
|
||||
pub holder_rewards_bps: u64,
|
||||
#[serde(default)]
|
||||
pub holder_rewards: u64,
|
||||
#[borsh(skip)]
|
||||
pub is_pump_pool: bool,
|
||||
#[borsh(skip)]
|
||||
@@ -125,6 +129,10 @@ pub struct PumpSwapSellEvent {
|
||||
pub can_boost: bool,
|
||||
#[serde(default)]
|
||||
pub base_supply: u64,
|
||||
#[serde(default)]
|
||||
pub holder_rewards_bps: u64,
|
||||
#[serde(default)]
|
||||
pub holder_rewards: u64,
|
||||
#[borsh(skip)]
|
||||
pub is_pump_pool: bool,
|
||||
#[borsh(skip)]
|
||||
@@ -185,6 +193,18 @@ pub struct PumpSwapCreatePoolEvent {
|
||||
#[borsh(skip)]
|
||||
#[serde(default)]
|
||||
pub is_cashback_coin: bool,
|
||||
/// Coin-specific creator fee rate carried over from the bonding curve.
|
||||
#[borsh(skip)]
|
||||
#[serde(default)]
|
||||
pub creator_fee_bps: u64,
|
||||
/// Reserved by the program; currently false.
|
||||
#[borsh(skip)]
|
||||
#[serde(default)]
|
||||
pub can_edit_creator_fee: bool,
|
||||
/// Whether creator fees are distributed to holders.
|
||||
#[borsh(skip)]
|
||||
#[serde(default)]
|
||||
pub is_holder_reward: bool,
|
||||
#[borsh(skip)]
|
||||
pub user_pool_token_account: Pubkey,
|
||||
#[borsh(skip)]
|
||||
@@ -284,6 +304,8 @@ mod tests {
|
||||
object.remove("virtual_quote_reserves");
|
||||
object.remove("can_boost");
|
||||
object.remove("base_supply");
|
||||
object.remove("holder_rewards_bps");
|
||||
object.remove("holder_rewards");
|
||||
|
||||
let decoded: PumpSwapSellEvent = serde_json::from_value(value).unwrap();
|
||||
assert_eq!(decoded.buyback_fee_basis_points, 0);
|
||||
@@ -291,6 +313,8 @@ mod tests {
|
||||
assert_eq!(decoded.virtual_quote_reserves, 0);
|
||||
assert!(!decoded.can_boost);
|
||||
assert_eq!(decoded.base_supply, 0);
|
||||
assert_eq!(decoded.holder_rewards_bps, 0);
|
||||
assert_eq!(decoded.holder_rewards, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,11 +35,21 @@ pub struct Pool {
|
||||
pub is_cashback_coin: bool,
|
||||
#[serde(default)]
|
||||
pub virtual_quote_reserves: i128,
|
||||
#[serde(default)]
|
||||
pub creator_fee_bps: u64,
|
||||
#[serde(default)]
|
||||
pub can_edit_creator_fee: bool,
|
||||
#[serde(default)]
|
||||
pub is_holder_reward: bool,
|
||||
}
|
||||
|
||||
/// Legacy allocated pool account body.
|
||||
pub const POOL_BODY_LEGACY: usize = 244;
|
||||
/// Current serialized pool account body including signed virtual quote reserves.
|
||||
pub const POOL_BODY: usize = 253;
|
||||
/// Boost-era body including signed virtual quote reserves.
|
||||
pub const POOL_BODY_BOOST: usize = 253;
|
||||
/// Creator-fee body before the holder-reward flag was added.
|
||||
pub const POOL_BODY_CREATOR_FEE: usize = 262;
|
||||
/// Current serialized pool account body.
|
||||
pub const POOL_BODY: usize = 263;
|
||||
|
||||
pub const POOL_SIZE: usize = POOL_BODY;
|
||||
|
||||
@@ -202,6 +202,9 @@ pub(crate) fn pumpswap_pool_from_pb(p: sol_parser_sdk::core::events::PumpSwapPoo
|
||||
is_mayhem_mode: p.is_mayhem_mode,
|
||||
is_cashback_coin: p.is_cashback_coin,
|
||||
virtual_quote_reserves: p.virtual_quote_reserves,
|
||||
creator_fee_bps: p.creator_fee_bps,
|
||||
can_edit_creator_fee: p.can_edit_creator_fee,
|
||||
is_holder_reward: p.is_holder_reward,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -92,6 +92,8 @@ mod tests {
|
||||
t.quote_amount = 14;
|
||||
t.virtual_quote_reserves = 15;
|
||||
t.real_quote_reserves = 16;
|
||||
t.holder_rewards_bps = 17;
|
||||
t.holder_rewards = 18;
|
||||
t.is_buy = true;
|
||||
|
||||
let ev = convert_parser_event(PbDexEvent::PumpFunTrade(t), None, 999).expect("convert");
|
||||
@@ -126,6 +128,8 @@ mod tests {
|
||||
assert_eq!(st.quote_amount, 14);
|
||||
assert_eq!(st.virtual_quote_reserves, 15);
|
||||
assert_eq!(st.real_quote_reserves, 16);
|
||||
assert_eq!(st.holder_rewards_bps, 17);
|
||||
assert_eq!(st.holder_rewards, 18);
|
||||
assert!(st.is_buy);
|
||||
}
|
||||
_ => panic!("expected PumpFunTradeEvent"),
|
||||
@@ -260,6 +264,8 @@ mod tests {
|
||||
quote_token_program,
|
||||
is_mayhem_mode: true,
|
||||
is_cashback_enabled: true,
|
||||
creator_fee_bps: 250,
|
||||
is_holder_reward: true,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
@@ -276,6 +282,8 @@ mod tests {
|
||||
assert_eq!(st.ix_name, "create_v2");
|
||||
assert!(st.is_mayhem_mode);
|
||||
assert!(st.is_cashback_enabled);
|
||||
assert_eq!(st.creator_fee_bps, 250);
|
||||
assert!(st.is_holder_reward);
|
||||
}
|
||||
other => panic!("expected canonical PumpFunCreateTokenEvent, got {other:?}"),
|
||||
}
|
||||
@@ -314,6 +322,9 @@ mod tests {
|
||||
quote_mint: Pubkey::new_unique(),
|
||||
is_mayhem_mode: true,
|
||||
is_cashback_coin: true,
|
||||
creator_fee_bps: 250,
|
||||
can_edit_creator_fee: true,
|
||||
is_holder_reward: true,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
@@ -324,6 +335,9 @@ mod tests {
|
||||
assert_eq!(st.metadata.event_type, EventType::PumpSwapCreatePool);
|
||||
assert!(st.is_mayhem_mode);
|
||||
assert!(st.is_cashback_coin);
|
||||
assert_eq!(st.creator_fee_bps, 250);
|
||||
assert!(st.can_edit_creator_fee);
|
||||
assert!(st.is_holder_reward);
|
||||
}
|
||||
_ => panic!("expected PumpSwapCreatePoolEvent"),
|
||||
}
|
||||
@@ -337,6 +351,8 @@ mod tests {
|
||||
buyback_fee: 22,
|
||||
can_boost: true,
|
||||
base_supply: 33,
|
||||
holder_rewards_bps: 44,
|
||||
holder_rewards: 55,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
@@ -350,6 +366,8 @@ mod tests {
|
||||
assert_eq!(converted.buyback_fee, 22);
|
||||
assert!(converted.can_boost);
|
||||
assert_eq!(converted.base_supply, 33);
|
||||
assert_eq!(converted.holder_rewards_bps, 44);
|
||||
assert_eq!(converted.holder_rewards, 55);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -360,6 +378,8 @@ mod tests {
|
||||
buyback_fee: 55,
|
||||
can_boost: true,
|
||||
base_supply: 66,
|
||||
holder_rewards_bps: 77,
|
||||
holder_rewards: 88,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
@@ -373,6 +393,8 @@ mod tests {
|
||||
assert_eq!(converted.buyback_fee, 55);
|
||||
assert!(converted.can_boost);
|
||||
assert_eq!(converted.base_supply, 66);
|
||||
assert_eq!(converted.holder_rewards_bps, 77);
|
||||
assert_eq!(converted.holder_rewards, 88);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -406,6 +428,9 @@ mod tests {
|
||||
is_mayhem_mode: true,
|
||||
is_cashback_coin: true,
|
||||
virtual_quote_reserves: -777,
|
||||
creator_fee_bps: 250,
|
||||
can_edit_creator_fee: true,
|
||||
is_holder_reward: true,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -418,6 +443,9 @@ mod tests {
|
||||
assert!(st.pool.is_mayhem_mode);
|
||||
assert!(st.pool.is_cashback_coin);
|
||||
assert_eq!(st.pool.virtual_quote_reserves, -777);
|
||||
assert_eq!(st.pool.creator_fee_bps, 250);
|
||||
assert!(st.pool.can_edit_creator_fee);
|
||||
assert!(st.pool.is_holder_reward);
|
||||
}
|
||||
_ => panic!("expected PumpSwapPoolAccountEvent"),
|
||||
}
|
||||
|
||||
@@ -56,6 +56,8 @@ pub(crate) fn pumpfun_create_token_from_parser(
|
||||
quote_vault: c.quote_vault,
|
||||
quote_token_program: c.quote_token_program,
|
||||
virtual_quote_reserves: c.virtual_quote_reserves,
|
||||
creator_fee_bps: c.creator_fee_bps,
|
||||
is_holder_reward: c.is_holder_reward,
|
||||
ix_name: "create".to_string(),
|
||||
..Default::default()
|
||||
}
|
||||
@@ -86,6 +88,8 @@ pub(crate) fn pumpfun_create_token_from_parser_v2(
|
||||
quote_vault: c.quote_vault,
|
||||
quote_token_program: c.quote_token_program,
|
||||
virtual_quote_reserves: c.virtual_quote_reserves,
|
||||
creator_fee_bps: c.creator_fee_bps,
|
||||
is_holder_reward: c.is_holder_reward,
|
||||
ix_name: "create_v2".to_string(),
|
||||
mint_authority: c.mint_authority,
|
||||
associated_bonding_curve: c.associated_bonding_curve,
|
||||
@@ -378,6 +382,9 @@ pub(crate) fn pumpfun_bonding_curve_account_from_parser(
|
||||
is_mayhem_mode: e.bonding_curve.is_mayhem_mode,
|
||||
is_cashback_coin: e.bonding_curve.is_cashback_coin,
|
||||
quote_mint: normalize_pumpfun_quote_mint(e.bonding_curve.quote_mint),
|
||||
creator_fee_bps: e.bonding_curve.creator_fee_bps,
|
||||
can_edit_creator_fee: e.bonding_curve.can_edit_creator_fee,
|
||||
is_holder_reward: e.bonding_curve.is_holder_reward,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -528,6 +535,8 @@ pub(crate) fn pumpswap_buy_full_from_parser(
|
||||
virtual_quote_reserves: b.virtual_quote_reserves,
|
||||
can_boost: b.can_boost,
|
||||
base_supply: b.base_supply,
|
||||
holder_rewards_bps: b.holder_rewards_bps,
|
||||
holder_rewards: b.holder_rewards,
|
||||
is_pump_pool: b.is_pump_pool,
|
||||
base_mint: b.base_mint,
|
||||
quote_mint: b.quote_mint,
|
||||
@@ -579,6 +588,8 @@ pub(crate) fn pumpswap_sell_full_from_parser(
|
||||
virtual_quote_reserves: s.virtual_quote_reserves,
|
||||
can_boost: s.can_boost,
|
||||
base_supply: s.base_supply,
|
||||
holder_rewards_bps: s.holder_rewards_bps,
|
||||
holder_rewards: s.holder_rewards,
|
||||
is_pump_pool: s.is_pump_pool,
|
||||
base_mint: s.base_mint,
|
||||
quote_mint: s.quote_mint,
|
||||
@@ -622,6 +633,9 @@ pub(crate) fn pumpswap_create_pool_from_parser(
|
||||
coin_creator: c.coin_creator,
|
||||
is_mayhem_mode: c.is_mayhem_mode,
|
||||
is_cashback_coin: c.is_cashback_coin,
|
||||
creator_fee_bps: c.creator_fee_bps,
|
||||
can_edit_creator_fee: c.can_edit_creator_fee,
|
||||
is_holder_reward: c.is_holder_reward,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
@@ -744,6 +758,8 @@ pub(crate) fn pumpfun_trade_from_parser_with_event_type(
|
||||
quote_amount: t.quote_amount,
|
||||
virtual_quote_reserves: t.virtual_quote_reserves,
|
||||
real_quote_reserves: t.real_quote_reserves,
|
||||
holder_rewards_bps: t.holder_rewards_bps,
|
||||
holder_rewards: t.holder_rewards,
|
||||
is_cashback_coin: t.is_cashback_coin,
|
||||
is_created_buy: t.is_created_buy,
|
||||
is_dev_create_token_trade: t.is_created_buy,
|
||||
@@ -912,4 +928,26 @@ mod tests {
|
||||
_ => panic!("expected PumpFunTradeEvent"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pumpfun_holder_rewards_are_preserved() {
|
||||
let ev = pumpfun_trade_from_parser_with_event_type(
|
||||
sol_parser_sdk::core::events::PumpFunTradeEvent {
|
||||
holder_rewards_bps: 250,
|
||||
holder_rewards: 1_234,
|
||||
..Default::default()
|
||||
},
|
||||
None,
|
||||
0,
|
||||
EventType::PumpFunBuy,
|
||||
);
|
||||
|
||||
match ev {
|
||||
DexEvent::PumpFunTradeEvent(t) => {
|
||||
assert_eq!(t.holder_rewards_bps, 250);
|
||||
assert_eq!(t.holder_rewards, 1_234);
|
||||
}
|
||||
_ => panic!("expected PumpFunTradeEvent"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,73 @@ fn rpc_client() -> RpcClient {
|
||||
// These transactions were captured from current mainnet traffic in August/September 2026.
|
||||
// Run with: RUN_MAINNET_TESTS=1 SOLANA_RPC_URL=<optional archive RPC> cargo test --test current_mainnet_transactions
|
||||
|
||||
#[test]
|
||||
fn current_pumpfun_holder_reward_trade_preserves_reward_tail() {
|
||||
if !run_mainnet_tests() {
|
||||
return;
|
||||
}
|
||||
const SIGNATURE: &str =
|
||||
"4xesV2yysHa1S2t6wkW8gJKTJ23LbtnKqchbtuRAmeSGqVWXTuZ755nQRzg4xZNYUmyeZr8Hpq8UW4iVe24YLrf2";
|
||||
let signature = Signature::from_str(SIGNATURE).expect("valid fixture signature");
|
||||
let filter = EventTypeFilter::include_only([EventType::PumpFunBuy]);
|
||||
let events = fetch_rpc_transaction_as_streamer_events(
|
||||
&rpc_client(),
|
||||
&signature,
|
||||
0,
|
||||
&[Protocol::PumpFun],
|
||||
Some(&filter),
|
||||
)
|
||||
.expect("parse current PumpFun holder-reward trade");
|
||||
|
||||
assert_eq!(events.len(), 1);
|
||||
let DexEvent::PumpFunTradeEvent(trade) = &events[0] else {
|
||||
panic!("expected PumpFun trade");
|
||||
};
|
||||
assert_eq!(trade.metadata.signature, signature);
|
||||
assert_eq!(trade.metadata.slot, 446_704_267);
|
||||
assert_eq!(trade.token_amount, 33_183_665_172);
|
||||
assert_eq!(trade.quote_amount, 111_485_301);
|
||||
assert_eq!(trade.creator_fee_basis_points, 300);
|
||||
assert_eq!(trade.creator_fee, 3_344_560);
|
||||
assert_eq!(trade.holder_rewards_bps, 300);
|
||||
assert_eq!(trade.holder_rewards, 3_344_560);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn current_pumpswap_buy_preserves_complete_upgrade_tail() {
|
||||
if !run_mainnet_tests() {
|
||||
return;
|
||||
}
|
||||
const SIGNATURE: &str =
|
||||
"4tCWJ41mFmJNU6KPk1dQpbVH9noysGKcuTTMcb7NQsE2U4Kr2x9rNNipCKLRuxA38j7TJPPYzC9eNA7piNyTPat1";
|
||||
let signature = Signature::from_str(SIGNATURE).expect("valid fixture signature");
|
||||
let filter = EventTypeFilter::include_only([EventType::PumpSwapBuy]);
|
||||
let events = fetch_rpc_transaction_as_streamer_events(
|
||||
&rpc_client(),
|
||||
&signature,
|
||||
0,
|
||||
&[Protocol::PumpSwap],
|
||||
Some(&filter),
|
||||
)
|
||||
.expect("parse current PumpSwap buy");
|
||||
|
||||
assert_eq!(events.len(), 1);
|
||||
let DexEvent::PumpSwapBuyEvent(buy) = &events[0] else {
|
||||
panic!("expected PumpSwap buy");
|
||||
};
|
||||
assert_eq!(buy.metadata.signature, signature);
|
||||
assert_eq!(buy.metadata.slot, 446_704_571);
|
||||
assert_eq!(buy.base_amount_out, 88_002_570_945);
|
||||
assert_eq!(buy.quote_amount_in, 99_000_000);
|
||||
assert_eq!(buy.buyback_fee_basis_points, 5_000);
|
||||
assert_eq!(buy.buyback_fee, 24_457);
|
||||
assert_eq!(buy.virtual_quote_reserves, 17_584_505_355);
|
||||
assert!(buy.can_boost);
|
||||
assert_eq!(buy.base_supply, 976_275_975_409_128);
|
||||
assert_eq!(buy.holder_rewards_bps, 0);
|
||||
assert_eq!(buy.holder_rewards, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn current_meteora_damm_v2_swap_passes_exact_filter() {
|
||||
if !run_mainnet_tests() {
|
||||
|
||||
Reference in New Issue
Block a user