From 108b9a616fb0e8d9dc9f74864a0c7e622aebfe93 Mon Sep 17 00:00:00 2001 From: 0xfnzero <0xfnzero@users.noreply.github.com> Date: Mon, 25 May 2026 03:38:28 +0800 Subject: [PATCH] Release solana-streamer-sdk v1.4.11 --- Cargo.toml | 6 +- README.md | 14 +- README_CN.md | 14 +- src/streaming/event_parser/common/filter.rs | 13 ++ src/streaming/event_parser/common/types.rs | 8 + .../event_parser/core/merger_event.rs | 55 ++++++- src/streaming/event_parser/core/traits.rs | 8 + .../event_parser/protocols/pumpfun/events.rs | 97 ++++++++++++ .../event_parser/protocols/pumpfun/types.rs | 29 +++- src/streaming/parser_sdk_bridge/accounts.rs | 40 ++++- src/streaming/parser_sdk_bridge/convert.rs | 56 ++++++- src/streaming/parser_sdk_bridge/filter.rs | 6 +- src/streaming/parser_sdk_bridge/mod.rs | 17 +++ .../parser_sdk_bridge/pump_pumpswap.rs | 138 +++++++++++++++++- 14 files changed, 455 insertions(+), 46 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index de511ba..8d42745 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-streamer-sdk" -version = "1.4.10" +version = "1.4.11" edition = "2021" authors = ["William ", "sgxiang ", "wei <1415121722@qq.com>"] repository = "https://github.com/0xfnzero/solana-streamer" @@ -15,13 +15,13 @@ crate-type = ["cdylib", "rlib"] [features] default = ["sdk-parse-borsh"] sdk-parse-borsh = ["sol-parser-sdk/parse-borsh"] -# If both parser backend features are enabled, sol-parser-sdk 0.4.15+ uses zero-copy. +# If both parser backend features are enabled, sol-parser-sdk 0.4.16+ uses zero-copy. sdk-parse-zero-copy = ["sol-parser-sdk/parse-zero-copy"] sdk-perf-stats = ["sol-parser-sdk/perf-stats"] sdk-ultra-perf = ["sol-parser-sdk/ultra-perf"] [dependencies] -sol-parser-sdk = { version = "0.4.15", default-features = false } +sol-parser-sdk = { path = "../sol-parser-sdk", version = "0.4.16", 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 fe4ada7..85dfa4a 100755 --- a/README.md +++ b/README.md @@ -123,33 +123,33 @@ Add the dependency to your `Cargo.toml`: ```toml # Add to your Cargo.toml -solana-streamer-sdk = { path = "./solana-streamer", version = "1.4.10" } +solana-streamer-sdk = { path = "./solana-streamer", version = "1.4.11" } ``` ### Use crates.io ```toml # Add to your Cargo.toml -solana-streamer-sdk = "1.4.10" +solana-streamer-sdk = "1.4.11" ``` Parser backend features: ```toml # Default: sol-parser-sdk parse-borsh backend -solana-streamer-sdk = "1.4.10" +solana-streamer-sdk = "1.4.11" # Zero-copy parser backend for latency-sensitive bots -solana-streamer-sdk = { version = "1.4.10", default-features = false, features = ["sdk-parse-zero-copy"] } +solana-streamer-sdk = { version = "1.4.11", default-features = false, features = ["sdk-parse-zero-copy"] } ``` -If both `sdk-parse-borsh` and `sdk-parse-zero-copy` are enabled, `sol-parser-sdk 0.4.11+` uses the zero-copy backend. +If both `sdk-parse-borsh` and `sdk-parse-zero-copy` are enabled, `sol-parser-sdk 0.4.16+` uses the zero-copy backend. ## 🔄 Migration Guide -### Upgrading to v1.4.10 +### Upgrading to v1.4.11 -Version 1.4.10 uses `sol-parser-sdk 0.4.15` from crates.io and preserves the SDK-compatible Yellowstone gRPC ordering modes in the streamer facade while adding the latest PumpFun USDC/v2 parser fields. +Version 1.4.11 uses `sol-parser-sdk 0.4.16` and preserves the SDK-compatible Yellowstone gRPC ordering modes in the streamer facade while aligning PumpFun USDC/v2 create, trade, global, bonding-curve, and volume account fields with the parser SDK. `buy_exact_quote_in(_v2)` is delivered as `PumpFunBuy` with the exact quote fields preserved. New optional capabilities: diff --git a/README_CN.md b/README_CN.md index 643ce1d..13b2075 100644 --- a/README_CN.md +++ b/README_CN.md @@ -122,33 +122,33 @@ git clone https://github.com/0xfnzero/solana-streamer ```toml # 添加到您的 Cargo.toml -solana-streamer-sdk = { path = "./solana-streamer", version = "1.4.10" } +solana-streamer-sdk = { path = "./solana-streamer", version = "1.4.11" } ``` ### 使用 crates.io ```toml # 添加到您的 Cargo.toml -solana-streamer-sdk = "1.4.10" +solana-streamer-sdk = "1.4.11" ``` 解析后端 feature: ```toml # 默认:sol-parser-sdk parse-borsh 后端 -solana-streamer-sdk = "1.4.10" +solana-streamer-sdk = "1.4.11" # 面向低延迟 Bot 的 zero-copy 解析后端 -solana-streamer-sdk = { version = "1.4.10", default-features = false, features = ["sdk-parse-zero-copy"] } +solana-streamer-sdk = { version = "1.4.11", default-features = false, features = ["sdk-parse-zero-copy"] } ``` -如果同时启用 `sdk-parse-borsh` 和 `sdk-parse-zero-copy`,`sol-parser-sdk 0.4.11+` 会优先使用 zero-copy 后端。 +如果同时启用 `sdk-parse-borsh` 和 `sdk-parse-zero-copy`,`sol-parser-sdk 0.4.16+` 会优先使用 zero-copy 后端。 ## 🔄 迁移指南 -### 升级到 v1.4.10 +### 升级到 v1.4.11 -v1.4.10 使用 crates.io 上的 `sol-parser-sdk 0.4.15`,保留 streamer facade 中与 SDK 兼容的 Yellowstone gRPC 输出顺序模式,并补齐最新 PumpFun USDC/v2 解析字段。 +v1.4.11 使用 `sol-parser-sdk 0.4.16`,保留 streamer facade 中与 SDK 兼容的 Yellowstone gRPC 输出顺序模式,并将 PumpFun USDC/v2 的 create、trade、global、bonding-curve 和 volume account 字段与 parser SDK 对齐。`buy_exact_quote_in(_v2)` 会作为 `PumpFunBuy` 投递,同时保留 exact quote 相关字段。 新增可选能力: diff --git a/src/streaming/event_parser/common/filter.rs b/src/streaming/event_parser/common/filter.rs index 120b36d..9f991f8 100644 --- a/src/streaming/event_parser/common/filter.rs +++ b/src/streaming/event_parser/common/filter.rs @@ -254,6 +254,14 @@ fn push_streamer_event_sdk_grpc_types(t: &EventType, out: &mut Vec out.push(Sdk::NonceAccount), St::AccountPumpFunGlobal => out.push(Sdk::AccountPumpFunGlobal), St::AccountPumpFunBondingCurve => out.push(Sdk::AccountPumpFunBondingCurve), + St::AccountPumpFunFeeConfig => out.push(Sdk::AccountPumpFunFeeConfig), + St::AccountPumpFunSharingConfig => out.push(Sdk::AccountPumpFunSharingConfig), + St::AccountPumpFunGlobalVolumeAccumulator => { + out.push(Sdk::AccountPumpFunGlobalVolumeAccumulator) + } + St::AccountPumpFunUserVolumeAccumulator => { + out.push(Sdk::AccountPumpFunUserVolumeAccumulator) + } St::AccountPumpSwapGlobalConfig => out.push(Sdk::AccountPumpSwapGlobalConfig), St::AccountPumpSwapPool => out.push(Sdk::AccountPumpSwapPool), _ => return false, @@ -641,6 +649,11 @@ mod tests { EventType::TokenInfo, EventType::NonceAccount, EventType::AccountPumpFunGlobal, + EventType::AccountPumpFunBondingCurve, + EventType::AccountPumpFunFeeConfig, + EventType::AccountPumpFunSharingConfig, + EventType::AccountPumpFunGlobalVolumeAccumulator, + EventType::AccountPumpFunUserVolumeAccumulator, EventType::AccountPumpSwapGlobalConfig, EventType::AccountPumpSwapPool, ]; diff --git a/src/streaming/event_parser/common/types.rs b/src/streaming/event_parser/common/types.rs index 9cf3b01..9ff8ac2 100755 --- a/src/streaming/event_parser/common/types.rs +++ b/src/streaming/event_parser/common/types.rs @@ -178,6 +178,10 @@ pub enum EventType { AccountBonkVestingRecord, AccountPumpFunBondingCurve, AccountPumpFunGlobal, + AccountPumpFunFeeConfig, + AccountPumpFunSharingConfig, + AccountPumpFunGlobalVolumeAccumulator, + AccountPumpFunUserVolumeAccumulator, AccountRaydiumClmmAmmConfig, AccountRaydiumClmmPoolState, AccountRaydiumClmmTickArrayState, @@ -206,6 +210,10 @@ pub const ACCOUNT_EVENT_TYPES: &[EventType] = &[ EventType::AccountBonkVestingRecord, EventType::AccountPumpFunBondingCurve, EventType::AccountPumpFunGlobal, + EventType::AccountPumpFunFeeConfig, + EventType::AccountPumpFunSharingConfig, + EventType::AccountPumpFunGlobalVolumeAccumulator, + EventType::AccountPumpFunUserVolumeAccumulator, EventType::AccountRaydiumClmmAmmConfig, EventType::AccountRaydiumClmmPoolState, EventType::AccountRaydiumClmmTickArrayState, diff --git a/src/streaming/event_parser/core/merger_event.rs b/src/streaming/event_parser/core/merger_event.rs index 6d20534..88f3ad8 100644 --- a/src/streaming/event_parser/core/merger_event.rs +++ b/src/streaming/event_parser/core/merger_event.rs @@ -15,6 +15,20 @@ fn fill_u64(to: &mut u64, from: u64) { } } +#[inline] +fn fill_i64(to: &mut i64, from: i64) { + if *to == 0 && from != 0 { + *to = from; + } +} + +#[inline] +fn fill_string(to: &mut String, from: String) { + if to.is_empty() && !from.is_empty() { + *to = from; + } +} + pub fn merge(instruction_event: &mut DexEvent, cpi_log_event: DexEvent) { match instruction_event { // PumpFun events @@ -108,6 +122,9 @@ pub fn merge(instruction_event: &mut DexEvent, cpi_log_event: DexEvent) { }, DexEvent::PumpFunCreateTokenEvent(e) => match cpi_log_event { DexEvent::PumpFunCreateV2TokenEvent(cpie) => { + fill_string(&mut e.name, cpie.name); + fill_string(&mut e.symbol, cpie.symbol); + fill_string(&mut e.uri, cpie.uri); if cpie.mint != solana_sdk::pubkey::Pubkey::default() { e.mint = cpie.mint; } @@ -138,13 +155,37 @@ pub fn merge(instruction_event: &mut DexEvent, cpi_log_event: DexEvent) { if cpie.token_program != solana_sdk::pubkey::Pubkey::default() { e.token_program = cpie.token_program; } - e.is_mayhem_mode = cpie.is_mayhem_mode; - e.is_cashback_enabled = cpie.is_cashback_enabled; + fill_pubkey(&mut e.quote_mint, cpie.quote_mint); + fill_u64(&mut e.virtual_quote_reserves, cpie.virtual_quote_reserves); + e.is_mayhem_mode |= cpie.is_mayhem_mode; + e.is_cashback_enabled |= cpie.is_cashback_enabled; } _ => {} }, DexEvent::PumpFunCreateV2TokenEvent(e) => match cpi_log_event { + DexEvent::PumpFunCreateTokenEvent(cpie) => { + fill_string(&mut e.name, cpie.name); + fill_string(&mut e.symbol, cpie.symbol); + fill_string(&mut e.uri, cpie.uri); + fill_pubkey(&mut e.mint, cpie.mint); + fill_pubkey(&mut e.bonding_curve, cpie.bonding_curve); + fill_pubkey(&mut e.user, cpie.user); + fill_pubkey(&mut e.creator, cpie.creator); + fill_i64(&mut e.timestamp, cpie.timestamp); + fill_u64(&mut e.virtual_token_reserves, cpie.virtual_token_reserves); + fill_u64(&mut e.virtual_sol_reserves, cpie.virtual_sol_reserves); + fill_u64(&mut e.real_token_reserves, cpie.real_token_reserves); + fill_u64(&mut e.token_total_supply, cpie.token_total_supply); + fill_pubkey(&mut e.token_program, cpie.token_program); + fill_pubkey(&mut e.quote_mint, cpie.quote_mint); + fill_u64(&mut e.virtual_quote_reserves, cpie.virtual_quote_reserves); + e.is_mayhem_mode |= cpie.is_mayhem_mode; + e.is_cashback_enabled |= cpie.is_cashback_enabled; + } DexEvent::PumpFunCreateV2TokenEvent(cpie) => { + fill_string(&mut e.name, cpie.name); + fill_string(&mut e.symbol, cpie.symbol); + fill_string(&mut e.uri, cpie.uri); if cpie.mint != solana_sdk::pubkey::Pubkey::default() { e.mint = cpie.mint; } @@ -175,8 +216,10 @@ pub fn merge(instruction_event: &mut DexEvent, cpi_log_event: DexEvent) { if cpie.token_program != solana_sdk::pubkey::Pubkey::default() { e.token_program = cpie.token_program; } - e.is_mayhem_mode = cpie.is_mayhem_mode; - e.is_cashback_enabled = cpie.is_cashback_enabled; + fill_pubkey(&mut e.quote_mint, cpie.quote_mint); + fill_u64(&mut e.virtual_quote_reserves, cpie.virtual_quote_reserves); + e.is_mayhem_mode |= cpie.is_mayhem_mode; + e.is_cashback_enabled |= cpie.is_cashback_enabled; } _ => {} }, @@ -795,7 +838,7 @@ mod tests { let shareholder = Pubkey::new_unique(); let mut instruction_event = DexEvent::PumpFunTradeEvent(PumpFunTradeEvent { - ix_name: "buy_exact_quote_in_v2".to_string(), + ix_name: "buy_exact_quote_in".to_string(), quote_mint, spendable_quote_in: 500, min_tokens_out: 600, @@ -823,7 +866,7 @@ mod tests { DexEvent::PumpFunTradeEvent(t) => { assert_eq!(t.sol_amount, 500); assert_eq!(t.token_amount, 600); - assert_eq!(t.ix_name, "buy_exact_quote_in_v2"); + assert_eq!(t.ix_name, "buy_exact_quote_in"); assert_eq!(t.quote_mint, quote_mint); assert_eq!(t.spendable_quote_in, 500); assert_eq!(t.min_tokens_out, 600); diff --git a/src/streaming/event_parser/core/traits.rs b/src/streaming/event_parser/core/traits.rs index c015bec..ff765ba 100755 --- a/src/streaming/event_parser/core/traits.rs +++ b/src/streaming/event_parser/core/traits.rs @@ -55,6 +55,10 @@ pub enum DexEvent { PumpFunMigrateBondingCurveCreatorEvent(PumpFunMigrateBondingCurveCreatorEvent), PumpFunBondingCurveAccountEvent(PumpFunBondingCurveAccountEvent), PumpFunGlobalAccountEvent(PumpFunGlobalAccountEvent), + PumpFunFeeConfigAccountEvent(PumpFunFeeConfigAccountEvent), + PumpFunSharingConfigAccountEvent(PumpFunSharingConfigAccountEvent), + PumpFunGlobalVolumeAccumulatorAccountEvent(PumpFunGlobalVolumeAccumulatorAccountEvent), + PumpFunUserVolumeAccumulatorAccountEvent(PumpFunUserVolumeAccumulatorAccountEvent), // PumpSwap events PumpSwapBuyEvent(PumpSwapBuyEvent), @@ -185,6 +189,10 @@ impl_dex_event_metadata!( PumpFunMigrateBondingCurveCreatorEvent, PumpFunBondingCurveAccountEvent, PumpFunGlobalAccountEvent, + PumpFunFeeConfigAccountEvent, + PumpFunSharingConfigAccountEvent, + PumpFunGlobalVolumeAccumulatorAccountEvent, + PumpFunUserVolumeAccumulatorAccountEvent, // PumpSwap events PumpSwapBuyEvent, PumpSwapSellEvent, diff --git a/src/streaming/event_parser/protocols/pumpfun/events.rs b/src/streaming/event_parser/protocols/pumpfun/events.rs index bef187c..2a7ae61 100755 --- a/src/streaming/event_parser/protocols/pumpfun/events.rs +++ b/src/streaming/event_parser/protocols/pumpfun/events.rs @@ -29,6 +29,10 @@ pub struct PumpFunCreateTokenEvent { #[borsh(skip)] pub is_cashback_enabled: bool, #[borsh(skip)] + pub quote_mint: Pubkey, + #[borsh(skip)] + pub virtual_quote_reserves: u64, + #[borsh(skip)] pub mint_authority: Pubkey, #[borsh(skip)] pub associated_bonding_curve: Pubkey, @@ -72,6 +76,10 @@ pub struct PumpFunCreateV2TokenEvent { /// Whether cashback is enabled (IDL CreateEvent.is_cashback_enabled) pub is_cashback_enabled: bool, #[borsh(skip)] + pub quote_mint: Pubkey, + #[borsh(skip)] + pub virtual_quote_reserves: u64, + #[borsh(skip)] pub mint_authority: Pubkey, #[borsh(skip)] pub associated_bonding_curve: Pubkey, @@ -448,6 +456,95 @@ pub struct PumpFunGlobalAccountEvent { pub global: Global, } +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PumpFunFeeConfigAccountEvent { + pub metadata: EventMetadata, + pub pubkey: Pubkey, + pub executable: bool, + pub lamports: u64, + pub owner: Pubkey, + pub rent_epoch: u64, + pub fee_config: PumpFunFeeConfig, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PumpFunFeeConfig { + pub bump: u8, + pub admin: Pubkey, + pub flat_fees: PumpFeesFees, + pub fee_tiers: Vec, + pub stable_fee_tiers: Vec, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PumpFunSharingConfigAccountEvent { + pub metadata: EventMetadata, + pub pubkey: Pubkey, + pub executable: bool, + pub lamports: u64, + pub owner: Pubkey, + pub rent_epoch: u64, + pub sharing_config: PumpFunSharingConfig, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PumpFunSharingConfig { + pub bump: u8, + pub version: u8, + pub status: PumpFeesConfigStatus, + pub mint: Pubkey, + pub admin: Pubkey, + pub admin_revoked: bool, + pub shareholders: Vec, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PumpFunGlobalVolumeAccumulatorAccountEvent { + pub metadata: EventMetadata, + pub pubkey: Pubkey, + pub executable: bool, + pub lamports: u64, + pub owner: Pubkey, + pub rent_epoch: u64, + pub global_volume_accumulator: PumpFunGlobalVolumeAccumulator, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PumpFunGlobalVolumeAccumulator { + pub start_time: i64, + pub end_time: i64, + pub seconds_in_a_day: i64, + pub mint: Pubkey, + pub total_token_supply: [u64; 30], + pub sol_volumes: [u64; 30], +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PumpFunUserVolumeAccumulatorAccountEvent { + pub metadata: EventMetadata, + pub pubkey: Pubkey, + pub executable: bool, + pub lamports: u64, + pub owner: Pubkey, + pub rent_epoch: u64, + pub user_volume_accumulator: PumpFunUserVolumeAccumulator, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PumpFunUserVolumeAccumulator { + pub user: Pubkey, + pub needs_claim: bool, + pub total_unclaimed_tokens: u64, + pub total_claimed_tokens: u64, + pub current_sol_volume: u64, + pub last_update_timestamp: i64, + pub has_total_claimed_tokens: bool, + pub cashback_earned: u64, + pub total_cashback_claimed: u64, + pub stable_cashback_earned: u64, + pub total_stable_cashback_claimed: u64, +} + /// Event discriminator constants pub mod discriminators { // Event discriminators diff --git a/src/streaming/event_parser/protocols/pumpfun/types.rs b/src/streaming/event_parser/protocols/pumpfun/types.rs index 1f6000e..301a247 100644 --- a/src/streaming/event_parser/protocols/pumpfun/types.rs +++ b/src/streaming/event_parser/protocols/pumpfun/types.rs @@ -5,17 +5,18 @@ use solana_sdk::pubkey::Pubkey; #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)] pub struct BondingCurve { pub virtual_token_reserves: u64, - pub virtual_sol_reserves: u64, + pub virtual_quote_reserves: u64, pub real_token_reserves: u64, - pub real_sol_reserves: u64, + pub real_quote_reserves: u64, pub token_total_supply: u64, pub complete: bool, pub creator: Pubkey, pub is_mayhem_mode: bool, pub is_cashback_coin: bool, + pub quote_mint: Pubkey, } -pub const BONDING_CURVE_SIZE: usize = 8 * 5 + 1 + 32 + 1 + 1; +pub const BONDING_CURVE_SIZE: usize = 8 * 5 + 1 + 32 + 1 + 1 + 32; #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)] pub struct Global { @@ -40,7 +41,25 @@ pub struct Global { pub mayhem_mode_enabled: bool, pub reserved_fee_recipients: [Pubkey; 7], pub is_cashback_enabled: bool, + pub buyback_fee_recipients: [Pubkey; 8], + pub buyback_basis_points: u64, + pub initial_virtual_quote_reserves: u64, + pub whitelisted_quote_mints: [Pubkey; 1], } -pub const GLOBAL_SIZE: usize = - 1 + 32 * 2 + 8 * 5 + 32 + 1 + 8 * 2 + 32 * 7 + 32 * 2 + 1 + 32 * 2 + 1 + 32 * 7 + 1; +pub const GLOBAL_SIZE: usize = 1 + + 32 * 2 + + 8 * 5 + + 32 + + 1 + + 8 * 2 + + 32 * 7 + + 32 * 2 + + 1 + + 32 * 2 + + 1 + + 32 * 7 + + 1 + + 32 * 8 + + 8 * 2 + + 32; diff --git a/src/streaming/parser_sdk_bridge/accounts.rs b/src/streaming/parser_sdk_bridge/accounts.rs index ca62c71..9d65435 100644 --- a/src/streaming/parser_sdk_bridge/accounts.rs +++ b/src/streaming/parser_sdk_bridge/accounts.rs @@ -16,9 +16,6 @@ pub(crate) enum AccountParseResult { Unsupported, } -// SDK account event omits the streamer wrapper fields and this byte; recover them from raw data. -const PUMPFUN_GLOBAL_CASHBACK_OFFSET: usize = 8 + 764; - pub(crate) fn parse_account_event( account: &AccountPretty, protocols: &[Protocol], @@ -104,6 +101,16 @@ fn streamer_account_event_to_sdk_types(t: &EventType) -> Vec { EventType::AccountPumpFunBondingCurve => { vec![SdkGrpcEventType::AccountPumpFunBondingCurve] } + EventType::AccountPumpFunFeeConfig => vec![SdkGrpcEventType::AccountPumpFunFeeConfig], + EventType::AccountPumpFunSharingConfig => { + vec![SdkGrpcEventType::AccountPumpFunSharingConfig] + } + EventType::AccountPumpFunGlobalVolumeAccumulator => { + vec![SdkGrpcEventType::AccountPumpFunGlobalVolumeAccumulator] + } + EventType::AccountPumpFunUserVolumeAccumulator => { + vec![SdkGrpcEventType::AccountPumpFunUserVolumeAccumulator] + } EventType::AccountPumpSwapGlobalConfig => { vec![SdkGrpcEventType::AccountPumpSwapGlobalConfig] } @@ -130,9 +137,6 @@ fn normalize_account_event(event: &mut DexEvent, account: &AccountPretty) { e.lamports = account.lamports; e.owner = account.owner; e.rent_epoch = account.rent_epoch; - if let Some(flag) = account.data.get(PUMPFUN_GLOBAL_CASHBACK_OFFSET) { - e.global.is_cashback_enabled = *flag != 0; - } } DexEvent::PumpFunBondingCurveAccountEvent(e) => { e.executable = account.executable; @@ -140,6 +144,30 @@ fn normalize_account_event(event: &mut DexEvent, account: &AccountPretty) { e.owner = account.owner; e.rent_epoch = account.rent_epoch; } + DexEvent::PumpFunFeeConfigAccountEvent(e) => { + e.executable = account.executable; + e.lamports = account.lamports; + e.owner = account.owner; + e.rent_epoch = account.rent_epoch; + } + DexEvent::PumpFunSharingConfigAccountEvent(e) => { + e.executable = account.executable; + e.lamports = account.lamports; + e.owner = account.owner; + e.rent_epoch = account.rent_epoch; + } + DexEvent::PumpFunGlobalVolumeAccumulatorAccountEvent(e) => { + e.executable = account.executable; + e.lamports = account.lamports; + e.owner = account.owner; + e.rent_epoch = account.rent_epoch; + } + DexEvent::PumpFunUserVolumeAccumulatorAccountEvent(e) => { + e.executable = account.executable; + e.lamports = account.lamports; + e.owner = account.owner; + e.rent_epoch = account.rent_epoch; + } _ => {} } } diff --git a/src/streaming/parser_sdk_bridge/convert.rs b/src/streaming/parser_sdk_bridge/convert.rs index fbdec4a..4df7dcf 100644 --- a/src/streaming/parser_sdk_bridge/convert.rs +++ b/src/streaming/parser_sdk_bridge/convert.rs @@ -228,10 +228,58 @@ pub(crate) fn convert_parser_event( pumpfun_bonding_curve_account_from_parser(e, meta), )) } - PbDexEvent::PumpFunFeeConfigAccount(_) - | PbDexEvent::PumpFunSharingConfigAccount(_) - | PbDexEvent::PumpFunGlobalVolumeAccumulatorAccount(_) - | PbDexEvent::PumpFunUserVolumeAccumulatorAccount(_) => None, + PbDexEvent::PumpFunFeeConfigAccount(e) => { + let meta = adapt_pm( + e.metadata.clone(), + bt, + recv_wall_us, + ProtocolType::PumpFun, + EventType::AccountPumpFunFeeConfig, + pump_program(), + ); + Some(DexEvent::PumpFunFeeConfigAccountEvent(pumpfun_fee_config_account_from_parser( + e, meta, + ))) + } + PbDexEvent::PumpFunSharingConfigAccount(e) => { + let meta = adapt_pm( + e.metadata.clone(), + bt, + recv_wall_us, + ProtocolType::PumpFun, + EventType::AccountPumpFunSharingConfig, + pump_program(), + ); + Some(DexEvent::PumpFunSharingConfigAccountEvent( + pumpfun_sharing_config_account_from_parser(e, meta), + )) + } + PbDexEvent::PumpFunGlobalVolumeAccumulatorAccount(e) => { + let meta = adapt_pm( + e.metadata.clone(), + bt, + recv_wall_us, + ProtocolType::PumpFun, + EventType::AccountPumpFunGlobalVolumeAccumulator, + pump_program(), + ); + Some(DexEvent::PumpFunGlobalVolumeAccumulatorAccountEvent( + pumpfun_global_volume_account_from_parser(e, meta), + )) + } + PbDexEvent::PumpFunUserVolumeAccumulatorAccount(e) => { + let meta = adapt_pm( + e.metadata.clone(), + bt, + recv_wall_us, + ProtocolType::PumpFun, + EventType::AccountPumpFunUserVolumeAccumulator, + pump_program(), + ); + Some(DexEvent::PumpFunUserVolumeAccumulatorAccountEvent( + pumpfun_user_volume_account_from_parser(e, meta), + )) + } PbDexEvent::PumpSwapTrade(t) => pumpswap_trade_from_parser(t, bt, recv_wall_us), PbDexEvent::PumpSwapBuy(b) => { diff --git a/src/streaming/parser_sdk_bridge/filter.rs b/src/streaming/parser_sdk_bridge/filter.rs index 562d319..256d2f5 100644 --- a/src/streaming/parser_sdk_bridge/filter.rs +++ b/src/streaming/parser_sdk_bridge/filter.rs @@ -42,7 +42,11 @@ fn protocol_matches_event(p: &Protocol, ev: &DexEvent) -> bool { | (Protocol::PumpFun, DexEvent::PumpFeesUpsertFeeTiersEvent(_)) | (Protocol::PumpFun, DexEvent::PumpFunMigrateBondingCurveCreatorEvent(_)) | (Protocol::PumpFun, DexEvent::PumpFunBondingCurveAccountEvent(_)) - | (Protocol::PumpFun, DexEvent::PumpFunGlobalAccountEvent(_)) => true, + | (Protocol::PumpFun, DexEvent::PumpFunGlobalAccountEvent(_)) + | (Protocol::PumpFun, DexEvent::PumpFunFeeConfigAccountEvent(_)) + | (Protocol::PumpFun, DexEvent::PumpFunSharingConfigAccountEvent(_)) + | (Protocol::PumpFun, DexEvent::PumpFunGlobalVolumeAccumulatorAccountEvent(_)) + | (Protocol::PumpFun, DexEvent::PumpFunUserVolumeAccumulatorAccountEvent(_)) => true, (Protocol::PumpFees, DexEvent::PumpFeesCreateFeeSharingConfigEvent(_)) | (Protocol::PumpFees, DexEvent::PumpFeesInitializeFeeConfigEvent(_)) | (Protocol::PumpFees, DexEvent::PumpFeesResetFeeSharingConfigEvent(_)) diff --git a/src/streaming/parser_sdk_bridge/mod.rs b/src/streaming/parser_sdk_bridge/mod.rs index eafe01d..49c48ab 100644 --- a/src/streaming/parser_sdk_bridge/mod.rs +++ b/src/streaming/parser_sdk_bridge/mod.rs @@ -141,6 +141,23 @@ mod tests { } } + #[test] + fn converts_pumpfun_buy_exact_quote_in_as_buy_event() { + let mut t = PbPumpTrade::default(); + t.metadata = EventMetadata::default(); + t.is_buy = true; + t.ix_name = "buy_exact_quote_in".to_string(); + + let ev = convert_parser_event(PbDexEvent::PumpFunBuy(t), None, 999).expect("convert"); + match ev { + DexEvent::PumpFunTradeEvent(st) => { + assert_eq!(st.metadata.event_type, EventType::PumpFunBuy); + assert_eq!(st.ix_name, "buy_exact_quote_in"); + } + _ => panic!("expected PumpFunTradeEvent"), + } + } + #[test] fn protocol_filter_keeps_pumpfun_only_when_requested() { let mut t = PbPumpTrade::default(); diff --git a/src/streaming/parser_sdk_bridge/pump_pumpswap.rs b/src/streaming/parser_sdk_bridge/pump_pumpswap.rs index 09a3608..b90c291 100644 --- a/src/streaming/parser_sdk_bridge/pump_pumpswap.rs +++ b/src/streaming/parser_sdk_bridge/pump_pumpswap.rs @@ -8,8 +8,11 @@ use crate::streaming::event_parser::protocols::pumpfun::events::{ PumpFeesTransferFeeSharingAuthorityEvent, PumpFeesUpdateAdminEvent, PumpFeesUpdateFeeConfigEvent, PumpFeesUpdateFeeSharesEvent, PumpFeesUpsertFeeTiersEvent, PumpFunBondingCurveAccountEvent, PumpFunCreateTokenEvent, PumpFunCreateV2TokenEvent, - PumpFunGlobalAccountEvent, PumpFunMigrateBondingCurveCreatorEvent, PumpFunMigrateEvent, - PumpFunTradeEvent, + PumpFunFeeConfig, PumpFunFeeConfigAccountEvent, PumpFunGlobalAccountEvent, + PumpFunGlobalVolumeAccumulator, PumpFunGlobalVolumeAccumulatorAccountEvent, + PumpFunMigrateBondingCurveCreatorEvent, PumpFunMigrateEvent, PumpFunSharingConfig, + PumpFunSharingConfigAccountEvent, PumpFunTradeEvent, PumpFunUserVolumeAccumulator, + PumpFunUserVolumeAccumulatorAccountEvent, }; use crate::streaming::event_parser::protocols::pumpfun::types::{BondingCurve, Global}; use crate::streaming::event_parser::protocols::pumpswap::events::{ @@ -44,6 +47,8 @@ pub(crate) fn pumpfun_create_token_from_parser( token_program: c.token_program, is_mayhem_mode: c.is_mayhem_mode, is_cashback_enabled: c.is_cashback_enabled, + quote_mint: c.quote_mint, + virtual_quote_reserves: c.virtual_quote_reserves, ..Default::default() } } @@ -69,6 +74,8 @@ pub(crate) fn pumpfun_create_v2_from_parser( token_program: c.token_program, is_mayhem_mode: c.is_mayhem_mode, is_cashback_enabled: c.is_cashback_enabled, + quote_mint: c.quote_mint, + virtual_quote_reserves: c.virtual_quote_reserves, mint_authority: c.mint_authority, associated_bonding_curve: c.associated_bonding_curve, global: c.global, @@ -328,7 +335,11 @@ pub(crate) fn pumpfun_global_account_from_parser( reserved_fee_recipient: e.global.reserved_fee_recipient, mayhem_mode_enabled: e.global.mayhem_mode_enabled, reserved_fee_recipients: e.global.reserved_fee_recipients, - is_cashback_enabled: false, + is_cashback_enabled: e.global.is_cashback_enabled, + buyback_fee_recipients: e.global.buyback_fee_recipients, + buyback_basis_points: e.global.buyback_basis_points, + initial_virtual_quote_reserves: e.global.initial_virtual_quote_reserves, + whitelisted_quote_mints: e.global.whitelisted_quote_mints, }, } } @@ -346,15 +357,118 @@ pub(crate) fn pumpfun_bonding_curve_account_from_parser( rent_epoch: 0, bonding_curve: BondingCurve { virtual_token_reserves: e.bonding_curve.virtual_token_reserves, - // Streamer keeps the historical SOL field names; SDK v0.4.15 uses quote reserves. - virtual_sol_reserves: e.bonding_curve.virtual_quote_reserves, + virtual_quote_reserves: e.bonding_curve.virtual_quote_reserves, real_token_reserves: e.bonding_curve.real_token_reserves, - real_sol_reserves: e.bonding_curve.real_quote_reserves, + real_quote_reserves: e.bonding_curve.real_quote_reserves, token_total_supply: e.bonding_curve.token_total_supply, complete: e.bonding_curve.complete, creator: e.bonding_curve.creator, is_mayhem_mode: e.bonding_curve.is_mayhem_mode, is_cashback_coin: e.bonding_curve.is_cashback_coin, + quote_mint: e.bonding_curve.quote_mint, + }, + } +} + +pub(crate) fn pumpfun_fee_config_account_from_parser( + e: sol_parser_sdk::core::events::PumpFunFeeConfigAccountEvent, + meta: EventMetadata, +) -> PumpFunFeeConfigAccountEvent { + PumpFunFeeConfigAccountEvent { + metadata: meta, + pubkey: e.pubkey, + executable: false, + lamports: 0, + owner: pump_program(), + rent_epoch: 0, + fee_config: PumpFunFeeConfig { + bump: e.fee_config.bump, + admin: e.fee_config.admin, + flat_fees: pump_fees_fees_from_parser(e.fee_config.flat_fees), + fee_tiers: e.fee_config.fee_tiers.into_iter().map(pump_fees_tier_from_parser).collect(), + stable_fee_tiers: e + .fee_config + .stable_fee_tiers + .into_iter() + .map(pump_fees_tier_from_parser) + .collect(), + }, + } +} + +pub(crate) fn pumpfun_sharing_config_account_from_parser( + e: sol_parser_sdk::core::events::PumpFunSharingConfigAccountEvent, + meta: EventMetadata, +) -> PumpFunSharingConfigAccountEvent { + PumpFunSharingConfigAccountEvent { + metadata: meta, + pubkey: e.pubkey, + executable: false, + lamports: 0, + owner: pump_program(), + rent_epoch: 0, + sharing_config: PumpFunSharingConfig { + bump: e.sharing_config.bump, + version: e.sharing_config.version, + status: pump_fees_status_from_parser(e.sharing_config.status), + mint: e.sharing_config.mint, + admin: e.sharing_config.admin, + admin_revoked: e.sharing_config.admin_revoked, + shareholders: e + .sharing_config + .shareholders + .into_iter() + .map(pump_fees_shareholder_from_parser) + .collect(), + }, + } +} + +pub(crate) fn pumpfun_global_volume_account_from_parser( + e: sol_parser_sdk::core::events::PumpFunGlobalVolumeAccumulatorAccountEvent, + meta: EventMetadata, +) -> PumpFunGlobalVolumeAccumulatorAccountEvent { + PumpFunGlobalVolumeAccumulatorAccountEvent { + metadata: meta, + pubkey: e.pubkey, + executable: false, + lamports: 0, + owner: pump_program(), + rent_epoch: 0, + global_volume_accumulator: PumpFunGlobalVolumeAccumulator { + start_time: e.global_volume_accumulator.start_time, + end_time: e.global_volume_accumulator.end_time, + seconds_in_a_day: e.global_volume_accumulator.seconds_in_a_day, + mint: e.global_volume_accumulator.mint, + total_token_supply: e.global_volume_accumulator.total_token_supply, + sol_volumes: e.global_volume_accumulator.sol_volumes, + }, + } +} + +pub(crate) fn pumpfun_user_volume_account_from_parser( + e: sol_parser_sdk::core::events::PumpFunUserVolumeAccumulatorAccountEvent, + meta: EventMetadata, +) -> PumpFunUserVolumeAccumulatorAccountEvent { + PumpFunUserVolumeAccumulatorAccountEvent { + metadata: meta, + pubkey: e.pubkey, + executable: false, + lamports: 0, + owner: pump_program(), + rent_epoch: 0, + user_volume_accumulator: PumpFunUserVolumeAccumulator { + user: e.user_volume_accumulator.user, + needs_claim: e.user_volume_accumulator.needs_claim, + total_unclaimed_tokens: e.user_volume_accumulator.total_unclaimed_tokens, + total_claimed_tokens: e.user_volume_accumulator.total_claimed_tokens, + current_sol_volume: e.user_volume_accumulator.current_sol_volume, + last_update_timestamp: e.user_volume_accumulator.last_update_timestamp, + has_total_claimed_tokens: e.user_volume_accumulator.has_total_claimed_tokens, + cashback_earned: e.user_volume_accumulator.cashback_earned, + total_cashback_claimed: e.user_volume_accumulator.total_cashback_claimed, + stable_cashback_earned: e.user_volume_accumulator.stable_cashback_earned, + total_stable_cashback_claimed: e.user_volume_accumulator.total_stable_cashback_claimed, }, } } @@ -544,10 +658,20 @@ pub(crate) fn pumpfun_trade_from_parser( bt: Option<&Timestamp>, recv_wall_us: i64, ) -> DexEvent { - let event_type = if t.is_buy { EventType::PumpFunBuy } else { EventType::PumpFunSell }; + let event_type = pumpfun_trade_event_type(&t); pumpfun_trade_from_parser_with_event_type(t, bt, recv_wall_us, event_type) } +pub(crate) fn pumpfun_trade_event_type( + t: &sol_parser_sdk::core::events::PumpFunTradeEvent, +) -> EventType { + match t.ix_name.as_str() { + "buy_exact_sol_in" => EventType::PumpFunBuyExactSolIn, + _ if t.is_buy => EventType::PumpFunBuy, + _ => EventType::PumpFunSell, + } +} + pub(crate) fn pumpfun_trade_from_parser_with_event_type( t: sol_parser_sdk::core::events::PumpFunTradeEvent, bt: Option<&Timestamp>,