From ce2931046d8d5c9c62fb686e1ecc1b7558925c76 Mon Sep 17 00:00:00 2001 From: Wood Date: Fri, 23 Jan 2026 15:40:26 +0800 Subject: [PATCH] fix: sync all protocol structures with latest IDL definitions This commit fixes multiple critical issues found when comparing SDK structures with the official IDL repository (https://github.com/0xfnzero/solana-program-idls): 1. PumpSwap GlobalConfig - Add missing reserved_fee_recipients: [Pubkey; 7] field - Update GLOBAL_CONFIG_SIZE constant 2. PumpFun Global - Add missing reserved_fee_recipients: [Pubkey; 7] field - Update GLOBAL_SIZE constant 3. Raydium CPMM AmmConfig - Add missing creator_fee_rate: u64 field - Adjust padding from [u64; 16] to [u64; 15] 4. Raydium CPMM PoolState - Add creator_fee_on: u8 field - Add enable_creator_fee: bool field - Add padding1: [u8; 6] field - Add creator_fees_token_0: u64 field - Add creator_fees_token_1: u64 field - Adjust padding from [u64; 31] to [u64; 28] 5. Bonk (Raydium Launchpad) PoolState - Add AmmCreatorFeeOn enum with borsh discriminant support - Add token_program_flag: u8 field - Add amm_creator_fee_on: AmmCreatorFeeOn field - Add platform_vesting_share: u64 field - Fix padding from [u64; 8] to [u8; 54] with serde_big_array - Update POOL_STATE_SIZE constant 6. Bonk (Raydium Launchpad) PlatformConfig - Add BondingCurveParam and PlatformCurveParam structures - Fix name from Vec to [u8; 64] with serde_big_array - Fix web from Vec to [u8; 256] with serde_big_array - Fix img from Vec to [u8; 256] with serde_big_array - Add cpswap_config: Pubkey field - Add creator_fee_rate: u64 field - Add transfer_fee_extension_auth: Pubkey field - Add platform_vesting_wallet: Pubkey field - Add platform_vesting_scale: u64 field - Add platform_cp_creator: Pubkey field - Fix padding from Vec to [u8; 108] with serde_big_array - Add curve_params: Vec field - Update PLATFORM_CONFIG_SIZE constant 7. PumpSwap buy_exact_quote_in parameter order bug - Create separate parse_buy_exact_quote_in_instruction function - Fix parameter parsing order: spendable_quote_in (SOL) first, then min_base_amount_out (token) - Previous bug: buy_exact_quote_in incorrectly reused parse_buy_instruction which has reversed parameter order - Add detailed comments explaining the parameter order difference All changes have been verified against the official IDL files and compile successfully. Co-Authored-By: Claude Sonnet 4.5 --- .../event_parser/protocols/bonk/types.rs | 148 ++++++++++++++++-- .../event_parser/protocols/pumpfun/types.rs | 3 +- .../event_parser/protocols/pumpswap/parser.rs | 44 +++++- .../event_parser/protocols/pumpswap/types.rs | 3 +- .../protocols/raydium_cpmm/types.rs | 10 +- 5 files changed, 193 insertions(+), 15 deletions(-) diff --git a/src/streaming/event_parser/protocols/bonk/types.rs b/src/streaming/event_parser/protocols/bonk/types.rs index a2ddbef..74bc8f0 100755 --- a/src/streaming/event_parser/protocols/bonk/types.rs +++ b/src/streaming/event_parser/protocols/bonk/types.rs @@ -43,13 +43,32 @@ pub struct VestingParams { pub unlock_period: u64, } -#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)] pub enum AmmFeeOn { - #[default] QuoteToken, BothToken, } +impl Default for AmmFeeOn { + fn default() -> Self { + Self::QuoteToken + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)] +#[borsh(use_discriminant = true)] +#[repr(u8)] +pub enum AmmCreatorFeeOn { + QuoteToken = 0, + BothToken = 1, +} + +impl Default for AmmCreatorFeeOn { + fn default() -> Self { + Self::QuoteToken + } +} + #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)] pub struct ConstantCurve { pub supply: u64, @@ -94,7 +113,7 @@ pub struct VestingSchedule { pub allocated_share_amount: u64, } -#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)] pub struct PoolState { pub epoch: u64, pub auth_bump: u8, @@ -120,10 +139,49 @@ pub struct PoolState { pub base_vault: Pubkey, pub quote_vault: Pubkey, pub creator: Pubkey, - pub padding: [u64; 8], + pub token_program_flag: u8, + pub amm_creator_fee_on: AmmCreatorFeeOn, + pub platform_vesting_share: u64, + #[serde(with = "serde_big_array::BigArray")] + pub padding: [u8; 54], } -pub const POOL_STATE_SIZE: usize = 8 + 1 * 5 + 8 * 10 + 32 * 7 + 8 * 8 + 8 * 5; +impl Default for PoolState { + fn default() -> Self { + Self { + epoch: 0, + auth_bump: 0, + status: 0, + base_decimals: 0, + quote_decimals: 0, + migrate_type: 0, + supply: 0, + total_base_sell: 0, + virtual_base: 0, + virtual_quote: 0, + real_base: 0, + real_quote: 0, + total_quote_fund_raising: 0, + quote_protocol_fee: 0, + platform_fee: 0, + migrate_fee: 0, + vesting_schedule: VestingSchedule::default(), + global_config: Pubkey::default(), + platform_config: Pubkey::default(), + base_mint: Pubkey::default(), + quote_mint: Pubkey::default(), + base_vault: Pubkey::default(), + quote_vault: Pubkey::default(), + creator: Pubkey::default(), + token_program_flag: 0, + amm_creator_fee_on: AmmCreatorFeeOn::default(), + platform_vesting_share: 0, + padding: [0u8; 54], + } + } +} + +pub const POOL_STATE_SIZE: usize = 8 + 1 * 5 + 8 * 10 + 32 * 7 + 8 * 8 + 8 * 5 + 1 + 1 + 8 + 54; pub fn pool_state_decode(data: &[u8]) -> Option { if data.len() < POOL_STATE_SIZE { @@ -207,6 +265,40 @@ pub fn global_config_parser( } #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)] +pub struct BondingCurveParam { + pub migrate_type: u8, + pub migrate_cpmm_fee_on: u8, + pub supply: u64, + pub total_base_sell: u64, + pub total_quote_fund_raising: u64, + pub total_locked_amount: u64, + pub cliff_period: u64, + pub unlock_period: u64, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)] +pub struct PlatformCurveParam { + pub epoch: u64, + pub index: u8, + pub global_config: Pubkey, + pub bonding_curve_param: BondingCurveParam, + #[serde(with = "serde_big_array::BigArray")] + pub padding: [u64; 50], +} + +impl Default for PlatformCurveParam { + fn default() -> Self { + Self { + epoch: 0, + index: 0, + global_config: Pubkey::default(), + bonding_curve_param: BondingCurveParam::default(), + padding: [0u64; 50], + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)] pub struct PlatformConfig { pub epoch: u64, pub platform_fee_wallet: Pubkey, @@ -215,13 +307,49 @@ pub struct PlatformConfig { pub creator_scale: u64, pub burn_scale: u64, pub fee_rate: u64, - pub name: Vec, - pub web: Vec, - pub img: Vec, - pub padding: Vec, + #[serde(with = "serde_big_array::BigArray")] + pub name: [u8; 64], + #[serde(with = "serde_big_array::BigArray")] + pub web: [u8; 256], + #[serde(with = "serde_big_array::BigArray")] + pub img: [u8; 256], + pub cpswap_config: Pubkey, + pub creator_fee_rate: u64, + pub transfer_fee_extension_auth: Pubkey, + pub platform_vesting_wallet: Pubkey, + pub platform_vesting_scale: u64, + pub platform_cp_creator: Pubkey, + #[serde(with = "serde_big_array::BigArray")] + pub padding: [u8; 108], + pub curve_params: Vec, } -pub const PLATFORM_CONFIG_SIZE: usize = 8 + 32 * 2 + 8 * 4 + 8 * 64 + 8 * 256 + 8 * 256 + 8 * 256; +impl Default for PlatformConfig { + fn default() -> Self { + Self { + epoch: 0, + platform_fee_wallet: Pubkey::default(), + platform_nft_wallet: Pubkey::default(), + platform_scale: 0, + creator_scale: 0, + burn_scale: 0, + fee_rate: 0, + name: [0u8; 64], + web: [0u8; 256], + img: [0u8; 256], + cpswap_config: Pubkey::default(), + creator_fee_rate: 0, + transfer_fee_extension_auth: Pubkey::default(), + platform_vesting_wallet: Pubkey::default(), + platform_vesting_scale: 0, + platform_cp_creator: Pubkey::default(), + padding: [0u8; 108], + curve_params: Vec::new(), + } + } +} + +pub const PLATFORM_CONFIG_SIZE: usize = 8 + 32 * 2 + 8 * 4 + 64 + 256 + 256 + 32 + 8 + 32 + 32 + 8 + 32 + 108; pub fn platform_config_decode(data: &[u8]) -> Option { if data.len() < PLATFORM_CONFIG_SIZE { diff --git a/src/streaming/event_parser/protocols/pumpfun/types.rs b/src/streaming/event_parser/protocols/pumpfun/types.rs index e30436b..376f2b7 100644 --- a/src/streaming/event_parser/protocols/pumpfun/types.rs +++ b/src/streaming/event_parser/protocols/pumpfun/types.rs @@ -77,9 +77,10 @@ pub struct Global { pub whitelist_pda: Pubkey, pub reserved_fee_recipient: Pubkey, pub mayhem_mode_enabled: bool, + pub reserved_fee_recipients: [Pubkey; 7], } -pub const GLOBAL_SIZE: usize = 1 + 32 * 2 + 8 * 5 + 32 + 1 + 8 * 2 + 32 * 7 + 32 * 2 + 1 + 32 * 2 + 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; pub fn global_decode(data: &[u8]) -> Option { if data.len() < GLOBAL_SIZE { diff --git a/src/streaming/event_parser/protocols/pumpswap/parser.rs b/src/streaming/event_parser/protocols/pumpswap/parser.rs index 9a7d863..d330475 100755 --- a/src/streaming/event_parser/protocols/pumpswap/parser.rs +++ b/src/streaming/event_parser/protocols/pumpswap/parser.rs @@ -24,7 +24,8 @@ pub fn parse_pumpswap_instruction_data( metadata: EventMetadata, ) -> Option { match discriminator { - discriminators::BUY_IX | discriminators::BUY_EXACT_QUOTE_IN_IX => parse_buy_instruction(data, accounts, metadata), + discriminators::BUY_IX => parse_buy_instruction(data, accounts, metadata), + discriminators::BUY_EXACT_QUOTE_IN_IX => parse_buy_exact_quote_in_instruction(data, accounts, metadata), discriminators::SELL_IX => parse_sell_instruction(data, accounts, metadata), discriminators::CREATE_POOL_IX => { parse_create_pool_instruction(data, accounts, metadata) @@ -165,6 +166,47 @@ fn parse_buy_instruction( })) } +/// 解析 buy_exact_quote_in 指令事件 +/// 注意:参数顺序与 buy 指令不同 +/// buy_exact_quote_in: spendable_quote_in (SOL), min_base_amount_out (token) +/// buy: base_amount_out (token), max_quote_amount_in (SOL) +fn parse_buy_exact_quote_in_instruction( + data: &[u8], + accounts: &[Pubkey], + mut metadata: EventMetadata, +) -> Option { + metadata.event_type = EventType::PumpSwapBuy; + + if data.len() < 16 || accounts.len() < 13 { + return None; + } + + // 注意:buy_exact_quote_in 的参数顺序是先 quote (SOL) 再 base (token) + let spendable_quote_in = read_u64_le(data, 0)?; + let min_base_amount_out = read_u64_le(data, 8)?; + + Some(DexEvent::PumpSwapBuyEvent(PumpSwapBuyEvent { + metadata, + base_amount_out: min_base_amount_out, + max_quote_amount_in: spendable_quote_in, + pool: accounts[0], + user: accounts[1], + base_mint: accounts[3], + quote_mint: accounts[4], + user_base_token_account: accounts[5], + user_quote_token_account: accounts[6], + pool_base_token_account: accounts[7], + pool_quote_token_account: accounts[8], + protocol_fee_recipient: accounts[9], + protocol_fee_recipient_token_account: accounts[10], + base_token_program: accounts[11], + quote_token_program: accounts[12], + coin_creator_vault_ata: accounts.get(17).copied().unwrap_or_default(), + coin_creator_vault_authority: accounts.get(18).copied().unwrap_or_default(), + ..Default::default() + })) +} + /// 解析卖出指令事件 fn parse_sell_instruction( data: &[u8], diff --git a/src/streaming/event_parser/protocols/pumpswap/types.rs b/src/streaming/event_parser/protocols/pumpswap/types.rs index d50aa43..a26ff70 100644 --- a/src/streaming/event_parser/protocols/pumpswap/types.rs +++ b/src/streaming/event_parser/protocols/pumpswap/types.rs @@ -23,9 +23,10 @@ pub struct GlobalConfig { pub whitelist_pda: Pubkey, pub reserved_fee_recipient: Pubkey, pub mayhem_mode_enabled: bool, + pub reserved_fee_recipients: [Pubkey; 7], } -pub const GLOBAL_CONFIG_SIZE: usize = 32 + 8 + 8 + 1 + 32 * 8 + 8 + 32 + 32 + 32 + 1; +pub const GLOBAL_CONFIG_SIZE: usize = 32 + 8 + 8 + 1 + 32 * 8 + 8 + 32 + 32 + 32 + 1 + 32 * 7; pub fn global_config_decode(data: &[u8]) -> Option { if data.len() < GLOBAL_CONFIG_SIZE { diff --git a/src/streaming/event_parser/protocols/raydium_cpmm/types.rs b/src/streaming/event_parser/protocols/raydium_cpmm/types.rs index 5b8e9fe..a0f9b94 100644 --- a/src/streaming/event_parser/protocols/raydium_cpmm/types.rs +++ b/src/streaming/event_parser/protocols/raydium_cpmm/types.rs @@ -24,7 +24,8 @@ pub struct AmmConfig { pub create_pool_fee: u64, pub protocol_owner: Pubkey, pub fund_owner: Pubkey, - pub padding: [u64; 16], + pub creator_fee_rate: u64, + pub padding: [u64; 15], } pub const AMM_CONFIG_SIZE: usize = 228; @@ -81,7 +82,12 @@ pub struct PoolState { pub fund_fees_token1: u64, pub open_time: u64, pub recent_epoch: u64, - pub padding: [u64; 31], + pub creator_fee_on: u8, + pub enable_creator_fee: bool, + pub padding1: [u8; 6], + pub creator_fees_token_0: u64, + pub creator_fees_token_1: u64, + pub padding: [u64; 28], } pub const POOL_STATE_SIZE: usize = 629;