From 372bb765afdf90ebcef87c39e39067d66d3f3410 Mon Sep 17 00:00:00 2001 From: ysq Date: Tue, 19 Aug 2025 21:13:22 +0800 Subject: [PATCH] feat: enhance protocol event parsers and bump to v0.3.5 - Add creator_fee support to Bonk events - Improve PumpFun/PumpSwap event parsing - Update documentation and common types --- Cargo.toml | 2 +- README.md | 4 +- README_CN.md | 4 +- src/streaming/event_parser/common/types.rs | 2 + .../event_parser/protocols/bonk/events.rs | 33 ++++++++++- .../event_parser/protocols/bonk/parser.rs | 59 +++++++++++++++++-- .../event_parser/protocols/bonk/types.rs | 11 +++- .../event_parser/protocols/pumpfun/events.rs | 27 +++++++++ .../event_parser/protocols/pumpfun/parser.rs | 10 ++-- .../event_parser/protocols/pumpswap/events.rs | 45 ++++++++++++++ .../event_parser/protocols/pumpswap/parser.rs | 41 +++++-------- 11 files changed, 193 insertions(+), 45 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f71b1bc..790aa5d 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-streamer-sdk" -version = "0.3.4" +version = "0.3.5" edition = "2021" authors = ["William ", "sgxiang ", "wei <1415121722@qq.com>"] repository = "https://github.com/0xfnzero/solana-streamer" diff --git a/README.md b/README.md index 125a6d9..f3db4bf 100755 --- a/README.md +++ b/README.md @@ -44,14 +44,14 @@ Add the dependency to your `Cargo.toml`: ```toml # Add to your Cargo.toml -solana-streamer-sdk = { path = "./solana-streamer", version = "0.3.4" } +solana-streamer-sdk = { path = "./solana-streamer", version = "0.3.5" } ``` ### Use crates.io ```toml # Add to your Cargo.toml -solana-streamer-sdk = "0.3.4" +solana-streamer-sdk = "0.3.5" ``` ## Usage Examples diff --git a/README_CN.md b/README_CN.md index 5299d75..21fefa4 100644 --- a/README_CN.md +++ b/README_CN.md @@ -44,14 +44,14 @@ git clone https://github.com/0xfnzero/solana-streamer ```toml # 添加到您的 Cargo.toml -solana-streamer-sdk = { path = "./solana-streamer", version = "0.3.4" } +solana-streamer-sdk = { path = "./solana-streamer", version = "0.3.5" } ``` ### 使用 crates.io ```toml # 添加到您的 Cargo.toml -solana-streamer-sdk = "0.3.4" +solana-streamer-sdk = "0.3.5" ``` ## 使用示例 diff --git a/src/streaming/event_parser/common/types.rs b/src/streaming/event_parser/common/types.rs index 03098b4..9794cc8 100755 --- a/src/streaming/event_parser/common/types.rs +++ b/src/streaming/event_parser/common/types.rs @@ -131,6 +131,7 @@ pub enum EventType { BonkSellExactIn, BonkSellExactOut, BonkInitialize, + BonkInitializeV2, BonkMigrateToAmm, BonkMigrateToCpswap, @@ -216,6 +217,7 @@ impl EventType { EventType::BonkSellExactIn => "BonkSellExactIn".to_string(), EventType::BonkSellExactOut => "BonkSellExactOut".to_string(), EventType::BonkInitialize => "BonkInitialize".to_string(), + EventType::BonkInitializeV2 => "BonkInitializeV2".to_string(), EventType::BonkMigrateToAmm => "BonkMigrateToAmm".to_string(), EventType::BonkMigrateToCpswap => "BonkMigrateToCpswap".to_string(), EventType::AccountPumpFunBondingCurve => "AccountPumpFunBondingCurve".to_string(), diff --git a/src/streaming/event_parser/protocols/bonk/events.rs b/src/streaming/event_parser/protocols/bonk/events.rs index eaf6b05..7f25067 100755 --- a/src/streaming/event_parser/protocols/bonk/events.rs +++ b/src/streaming/event_parser/protocols/bonk/events.rs @@ -3,7 +3,9 @@ use crate::streaming::event_parser::common::EventMetadata; use crate::streaming::event_parser::protocols::bonk::types::{ CurveParams, MintParams, PoolStatus, TradeDirection, VestingParams, }; -use crate::streaming::event_parser::protocols::bonk::{GlobalConfig, PlatformConfig, PoolState}; +use crate::streaming::event_parser::protocols::bonk::{ + AmmFeeOn, GlobalConfig, PlatformConfig, PoolState, +}; use borsh::BorshDeserialize; use serde::{Deserialize, Serialize}; use solana_sdk::pubkey::Pubkey; @@ -25,9 +27,11 @@ pub struct BonkTradeEvent { pub amount_out: u64, pub protocol_fee: u64, pub platform_fee: u64, + pub creator_fee: u64, pub share_fee: u64, pub trade_direction: TradeDirection, pub pool_status: PoolStatus, + pub exact_in: bool, #[borsh(skip)] pub minimum_amount_out: u64, #[borsh(skip)] @@ -58,6 +62,15 @@ pub struct BonkTradeEvent { pub is_bot: bool, } +pub const BONK_TRADE_EVENT_LOG_SIZE: usize = 32 + 8 * 13 + 1 + 1 + 1; + +pub fn bonk_trade_event_log_decode(data: &[u8]) -> Option { + if data.len() < BONK_TRADE_EVENT_LOG_SIZE { + return None; + } + borsh::from_slice::(&data[..BONK_TRADE_EVENT_LOG_SIZE]).ok() +} + // Macro to generate UnifiedEvent implementation, specifying the fields to be merged impl_unified_event!( BonkTradeEvent, @@ -73,9 +86,11 @@ impl_unified_event!( amount_out, protocol_fee, platform_fee, + creator_fee, share_fee, trade_direction, - pool_status + pool_status, + exact_in ); /// Create pool event @@ -89,6 +104,7 @@ pub struct BonkPoolCreateEvent { pub base_mint_param: MintParams, pub curve_param: CurveParams, pub vesting_param: VestingParams, + pub amm_fee_on: Option, #[borsh(skip)] pub payer: Pubkey, #[borsh(skip)] @@ -105,6 +121,15 @@ pub struct BonkPoolCreateEvent { pub platform_config: Pubkey, } +pub const BONK_POOL_CREATE_EVENT_LOG_SIZE: usize = 256; + +pub fn bonk_pool_create_event_log_decode(data: &[u8]) -> Option { + if data.len() < BONK_POOL_CREATE_EVENT_LOG_SIZE { + return None; + } + borsh::from_slice::(&data[..BONK_POOL_CREATE_EVENT_LOG_SIZE]).ok() +} + // Macro to generate UnifiedEvent implementation, specifying the fields to be merged impl_unified_event!( BonkPoolCreateEvent, @@ -113,7 +138,8 @@ impl_unified_event!( config, base_mint_param, curve_param, - vesting_param + vesting_param, + amm_fee_on ); /// Create pool event @@ -287,6 +313,7 @@ pub mod discriminators { pub const SELL_EXACT_IN: &[u8] = &[149, 39, 222, 155, 211, 124, 152, 26]; pub const SELL_EXACT_OUT: &[u8] = &[95, 200, 71, 34, 8, 9, 11, 166]; pub const INITIALIZE: &[u8] = &[175, 175, 109, 31, 13, 152, 155, 237]; + pub const INITIALIZE_V2: &[u8] = &[67, 153, 175, 39, 218, 16, 38, 32]; pub const MIGRATE_TO_AMM: &[u8] = &[207, 82, 192, 145, 254, 207, 145, 223]; pub const MIGRATE_TO_CP_SWAP: &[u8] = &[136, 92, 200, 103, 28, 218, 144, 140]; diff --git a/src/streaming/event_parser/protocols/bonk/parser.rs b/src/streaming/event_parser/protocols/bonk/parser.rs index 4eaf8ce..c2b3977 100755 --- a/src/streaming/event_parser/protocols/bonk/parser.rs +++ b/src/streaming/event_parser/protocols/bonk/parser.rs @@ -8,9 +8,7 @@ use crate::streaming::event_parser::{ common::{utils::*, EventMetadata, EventType, ProtocolType}, core::traits::{EventParser, GenericEventParseConfig, GenericEventParser, UnifiedEvent}, protocols::bonk::{ - discriminators, BonkMigrateToAmmEvent, BonkMigrateToCpswapEvent, BonkPoolCreateEvent, - BonkTradeEvent, ConstantCurve, CurveParams, FixedCurve, LinearCurve, MintParams, - TradeDirection, VestingParams, + bonk_pool_create_event_log_decode, bonk_trade_event_log_decode, discriminators, AmmFeeOn, BonkMigrateToAmmEvent, BonkMigrateToCpswapEvent, BonkPoolCreateEvent, BonkTradeEvent, ConstantCurve, CurveParams, FixedCurve, LinearCurve, MintParams, TradeDirection, VestingParams }, }; @@ -78,6 +76,15 @@ impl BonkEventParser { inner_instruction_parser: Some(Self::parse_pool_create_inner_instruction), instruction_parser: Some(Self::parse_initialize_instruction), }, + GenericEventParseConfig { + program_id: BONK_PROGRAM_ID, + protocol_type: ProtocolType::Bonk, + inner_instruction_discriminator: discriminators::POOL_CREATE_EVENT, + instruction_discriminator: discriminators::INITIALIZE_V2, + event_type: EventType::BonkInitializeV2, + inner_instruction_parser: Some(Self::parse_pool_create_inner_instruction), + instruction_parser: Some(Self::parse_initialize_v2_instruction), + }, GenericEventParseConfig { program_id: BONK_PROGRAM_ID, protocol_type: ProtocolType::Bonk, @@ -108,7 +115,7 @@ impl BonkEventParser { data: &[u8], metadata: EventMetadata, ) -> Option> { - if let Ok(event) = borsh::from_slice::(data) { + if let Some(event) = bonk_pool_create_event_log_decode(data) { let mut metadata = metadata; metadata.set_id(metadata.signature.to_string()); Some(Box::new(BonkPoolCreateEvent { metadata, ..event })) @@ -122,7 +129,7 @@ impl BonkEventParser { data: &[u8], metadata: EventMetadata, ) -> Option> { - if let Ok(event) = borsh::from_slice::(data) { + if let Some(event) = bonk_trade_event_log_decode(data) { let mut metadata = metadata; metadata.set_id(format!("{}-{}", metadata.signature, event.pool_state)); if metadata.event_type == EventType::BonkBuyExactIn @@ -324,6 +331,48 @@ impl BonkEventParser { })) } + /// Parse initialize event + fn parse_initialize_v2_instruction( + data: &[u8], + accounts: &[Pubkey], + metadata: EventMetadata, + ) -> Option> { + if data.len() < 24 { + return None; + } + + let mut offset = 0; + let base_mint_param = Self::parse_mint_params(data, &mut offset)?; + let curve_param = Self::parse_curve_params(data, &mut offset)?; + let vesting_param = Self::parse_vesting_params(data, &mut offset)?; + let amm_fee_on = data[offset]; + + let mut metadata = metadata; + metadata.set_id(metadata.signature.to_string()); + + Some(Box::new(BonkPoolCreateEvent { + metadata, + payer: accounts[0], + creator: accounts[1], + global_config: accounts[2], + platform_config: accounts[3], + pool_state: accounts[5], + base_mint: accounts[6], + quote_mint: accounts[7], + base_vault: accounts[8], + quote_vault: accounts[9], + base_mint_param, + curve_param, + vesting_param, + amm_fee_on: if amm_fee_on == 0 { + Some(AmmFeeOn::QuoteToken) + } else { + Some(AmmFeeOn::BothToken) + }, + ..Default::default() + })) + } + /// Parse MintParams structure fn parse_mint_params(data: &[u8], offset: &mut usize) -> Option { // Read decimals (1 byte) diff --git a/src/streaming/event_parser/protocols/bonk/types.rs b/src/streaming/event_parser/protocols/bonk/types.rs index 5de6aa6..e3b453d 100755 --- a/src/streaming/event_parser/protocols/bonk/types.rs +++ b/src/streaming/event_parser/protocols/bonk/types.rs @@ -5,7 +5,9 @@ use solana_sdk::pubkey::Pubkey; use crate::streaming::{ event_parser::{ common::EventMetadata, - protocols::bonk::{BonkGlobalConfigAccountEvent, BonkPlatformConfigAccountEvent, BonkPoolStateAccountEvent}, + protocols::bonk::{ + BonkGlobalConfigAccountEvent, BonkPlatformConfigAccountEvent, BonkPoolStateAccountEvent, + }, UnifiedEvent, }, grpc::AccountPretty, @@ -41,6 +43,13 @@ pub struct VestingParams { pub unlock_period: u64, } +#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)] +pub enum AmmFeeOn { + #[default] + QuoteToken, + BothToken, +} + #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)] pub struct ConstantCurve { pub supply: u64, diff --git a/src/streaming/event_parser/protocols/pumpfun/events.rs b/src/streaming/event_parser/protocols/pumpfun/events.rs index 11f33a1..89b02d7 100755 --- a/src/streaming/event_parser/protocols/pumpfun/events.rs +++ b/src/streaming/event_parser/protocols/pumpfun/events.rs @@ -28,6 +28,15 @@ pub struct PumpFunCreateTokenEvent { pub associated_bonding_curve: Pubkey, } +pub const PUMPFUN_CREATE_TOKEN_EVENT_LOG_SIZE: usize = 257; + +pub fn pumpfun_create_token_event_log_decode(data: &[u8]) -> Option { + if data.len() < PUMPFUN_CREATE_TOKEN_EVENT_LOG_SIZE { + return None; + } + borsh::from_slice::(&data[..PUMPFUN_CREATE_TOKEN_EVENT_LOG_SIZE]).ok() +} + impl_unified_event!( PumpFunCreateTokenEvent, mint, @@ -108,6 +117,15 @@ pub struct PumpFunTradeEvent { pub user_volume_accumulator: Pubkey, } +pub const PUMPFUN_TRADE_EVENT_LOG_SIZE: usize = 250; + +pub fn pumpfun_trade_event_log_decode(data: &[u8]) -> Option { + if data.len() < PUMPFUN_TRADE_EVENT_LOG_SIZE { + return None; + } + borsh::from_slice::(&data[..PUMPFUN_TRADE_EVENT_LOG_SIZE]).ok() +} + impl_unified_event!( PumpFunTradeEvent, mint, @@ -184,6 +202,15 @@ pub struct PumpFunMigrateEvent { pub program: Pubkey, } +pub const PUMPFUN_MIGRATE_EVENT_LOG_SIZE: usize = 160; + +pub fn pumpfun_migrate_event_log_decode(data: &[u8]) -> Option { + if data.len() < PUMPFUN_MIGRATE_EVENT_LOG_SIZE { + return None; + } + borsh::from_slice::(&data[..PUMPFUN_MIGRATE_EVENT_LOG_SIZE]).ok() +} + impl_unified_event!( PumpFunMigrateEvent, user, diff --git a/src/streaming/event_parser/protocols/pumpfun/parser.rs b/src/streaming/event_parser/protocols/pumpfun/parser.rs index 7a4e399..4faaf01 100755 --- a/src/streaming/event_parser/protocols/pumpfun/parser.rs +++ b/src/streaming/event_parser/protocols/pumpfun/parser.rs @@ -8,7 +8,9 @@ use crate::streaming::event_parser::{ common::{EventMetadata, EventType, ProtocolType}, core::traits::{EventParser, GenericEventParseConfig, GenericEventParser, UnifiedEvent}, protocols::pumpfun::{ - discriminators, PumpFunCreateTokenEvent, PumpFunMigrateEvent, PumpFunTradeEvent, + discriminators, pumpfun_create_token_event_log_decode, pumpfun_migrate_event_log_decode, + pumpfun_trade_event_log_decode, PumpFunCreateTokenEvent, PumpFunMigrateEvent, + PumpFunTradeEvent, }, }; @@ -79,7 +81,7 @@ impl PumpFunEventParser { data: &[u8], metadata: EventMetadata, ) -> Option> { - if let Ok(event) = borsh::from_slice::(data) { + if let Some(event) = pumpfun_migrate_event_log_decode(data) { let mut metadata = metadata; metadata.set_id(format!("{}-{}-{}", metadata.signature, event.user, event.mint)); Some(Box::new(PumpFunMigrateEvent { metadata, ..event })) @@ -93,7 +95,7 @@ impl PumpFunEventParser { data: &[u8], metadata: EventMetadata, ) -> Option> { - if let Ok(event) = borsh::from_slice::(data) { + if let Some(event) = pumpfun_create_token_event_log_decode(data) { let mut metadata = metadata; metadata.set_id(format!( "{}-{}-{}-{}", @@ -110,7 +112,7 @@ impl PumpFunEventParser { data: &[u8], metadata: EventMetadata, ) -> Option> { - if let Ok(event) = borsh::from_slice::(data) { + if let Some(event) = pumpfun_trade_event_log_decode(data) { let mut metadata = metadata; metadata.set_id(format!( "{}-{}-{}-{}", diff --git a/src/streaming/event_parser/protocols/pumpswap/events.rs b/src/streaming/event_parser/protocols/pumpswap/events.rs index 215835f..58879a5 100755 --- a/src/streaming/event_parser/protocols/pumpswap/events.rs +++ b/src/streaming/event_parser/protocols/pumpswap/events.rs @@ -53,6 +53,15 @@ pub struct PumpSwapBuyEvent { pub coin_creator_vault_authority: Pubkey, } +pub const PUMP_SWAP_BUY_EVENT_LOG_SIZE: usize = 385; + +pub fn pump_swap_buy_event_log_decode(data: &[u8]) -> Option { + if data.len() < PUMP_SWAP_BUY_EVENT_LOG_SIZE { + return None; + } + borsh::from_slice::(&data[..PUMP_SWAP_BUY_EVENT_LOG_SIZE]).ok() +} + // 使用宏生成UnifiedEvent实现,指定需要合并的字段 impl_unified_event!( PumpSwapBuyEvent, @@ -123,6 +132,15 @@ pub struct PumpSwapSellEvent { pub coin_creator_vault_authority: Pubkey, } +pub const PUMP_SWAP_SELL_EVENT_LOG_SIZE: usize = 352; + +pub fn pump_swap_sell_event_log_decode(data: &[u8]) -> Option { + if data.len() < PUMP_SWAP_SELL_EVENT_LOG_SIZE { + return None; + } + borsh::from_slice::(&data[..PUMP_SWAP_SELL_EVENT_LOG_SIZE]).ok() +} + // 使用宏生成UnifiedEvent实现,指定需要合并的字段 impl_unified_event!( PumpSwapSellEvent, @@ -184,6 +202,15 @@ pub struct PumpSwapCreatePoolEvent { pub pool_quote_token_account: Pubkey, } +pub const PUMP_SWAP_CREATE_POOL_EVENT_LOG_SIZE: usize = 325; + +pub fn pump_swap_create_pool_event_log_decode(data: &[u8]) -> Option { + if data.len() < PUMP_SWAP_CREATE_POOL_EVENT_LOG_SIZE { + return None; + } + borsh::from_slice::(&data[..PUMP_SWAP_CREATE_POOL_EVENT_LOG_SIZE]).ok() +} + impl_unified_event!( PumpSwapCreatePoolEvent, timestamp, @@ -239,6 +266,15 @@ pub struct PumpSwapDepositEvent { pub pool_quote_token_account: Pubkey, } +pub const PUMP_SWAP_DEPOSIT_EVENT_LOG_SIZE: usize = 248; + +pub fn pump_swap_deposit_event_log_decode(data: &[u8]) -> Option { + if data.len() < PUMP_SWAP_DEPOSIT_EVENT_LOG_SIZE { + return None; + } + borsh::from_slice::(&data[..PUMP_SWAP_DEPOSIT_EVENT_LOG_SIZE]).ok() +} + impl_unified_event!( PumpSwapDepositEvent, timestamp, @@ -290,6 +326,15 @@ pub struct PumpSwapWithdrawEvent { pub pool_quote_token_account: Pubkey, } +pub const PUMP_SWAP_WITHDRAW_EVENT_LOG_SIZE: usize = 248; + +pub fn pump_swap_withdraw_event_log_decode(data: &[u8]) -> Option { + if data.len() < PUMP_SWAP_WITHDRAW_EVENT_LOG_SIZE { + return None; + } + borsh::from_slice::(&data[..PUMP_SWAP_WITHDRAW_EVENT_LOG_SIZE]).ok() +} + impl_unified_event!( PumpSwapWithdrawEvent, timestamp, diff --git a/src/streaming/event_parser/protocols/pumpswap/parser.rs b/src/streaming/event_parser/protocols/pumpswap/parser.rs index c55be69..98d2cf5 100755 --- a/src/streaming/event_parser/protocols/pumpswap/parser.rs +++ b/src/streaming/event_parser/protocols/pumpswap/parser.rs @@ -8,8 +8,10 @@ use crate::streaming::event_parser::{ common::{read_u64_le, EventMetadata, EventType, ProtocolType}, core::traits::{EventParser, GenericEventParseConfig, GenericEventParser, UnifiedEvent}, protocols::pumpswap::{ - discriminators, PumpSwapBuyEvent, PumpSwapCreatePoolEvent, PumpSwapDepositEvent, - PumpSwapSellEvent, PumpSwapWithdrawEvent, + discriminators, pump_swap_buy_event_log_decode, pump_swap_create_pool_event_log_decode, + pump_swap_deposit_event_log_decode, pump_swap_sell_event_log_decode, + pump_swap_withdraw_event_log_decode, PumpSwapBuyEvent, PumpSwapCreatePoolEvent, + PumpSwapDepositEvent, PumpSwapSellEvent, PumpSwapWithdrawEvent, }, }; @@ -89,16 +91,13 @@ impl PumpSwapEventParser { data: &[u8], metadata: EventMetadata, ) -> Option> { - if let Ok(event) = borsh::from_slice::(data) { + if let Some(event) = pump_swap_buy_event_log_decode(data) { let mut metadata = metadata; metadata.set_id(format!( "{}-{}-{}-{}", metadata.signature, event.user, event.pool, event.base_amount_out )); - Some(Box::new(PumpSwapBuyEvent { - metadata, - ..event - })) + Some(Box::new(PumpSwapBuyEvent { metadata, ..event })) } else { None } @@ -109,16 +108,13 @@ impl PumpSwapEventParser { data: &[u8], metadata: EventMetadata, ) -> Option> { - if let Ok(event) = borsh::from_slice::(data) { + if let Some(event) = pump_swap_sell_event_log_decode(data) { let mut metadata = metadata; metadata.set_id(format!( "{}-{}-{}-{}", metadata.signature, event.user, event.pool, event.base_amount_in )); - Some(Box::new(PumpSwapSellEvent { - metadata, - ..event - })) + Some(Box::new(PumpSwapSellEvent { metadata, ..event })) } else { None } @@ -129,16 +125,13 @@ impl PumpSwapEventParser { data: &[u8], metadata: EventMetadata, ) -> Option> { - if let Ok(event) = borsh::from_slice::(data) { + if let Some(event) = pump_swap_create_pool_event_log_decode(data) { let mut metadata = metadata; metadata.set_id(format!( "{}-{}-{}-{}", metadata.signature, event.pool, event.creator, event.base_amount_in )); - Some(Box::new(PumpSwapCreatePoolEvent { - metadata, - ..event - })) + Some(Box::new(PumpSwapCreatePoolEvent { metadata, ..event })) } else { None } @@ -149,16 +142,13 @@ impl PumpSwapEventParser { data: &[u8], metadata: EventMetadata, ) -> Option> { - if let Ok(event) = borsh::from_slice::(data) { + if let Some(event) = pump_swap_deposit_event_log_decode(data) { let mut metadata = metadata; metadata.set_id(format!( "{}-{}-{}-{}", metadata.signature, event.pool, event.user, event.lp_token_amount_out )); - Some(Box::new(PumpSwapDepositEvent { - metadata, - ..event - })) + Some(Box::new(PumpSwapDepositEvent { metadata, ..event })) } else { None } @@ -169,16 +159,13 @@ impl PumpSwapEventParser { data: &[u8], metadata: EventMetadata, ) -> Option> { - if let Ok(event) = borsh::from_slice::(data) { + if let Some(event) = pump_swap_withdraw_event_log_decode(data) { let mut metadata = metadata; metadata.set_id(format!( "{}-{}-{}-{}", metadata.signature, event.pool, event.user, event.lp_token_amount_in )); - Some(Box::new(PumpSwapWithdrawEvent { - metadata, - ..event - })) + Some(Box::new(PumpSwapWithdrawEvent { metadata, ..event })) } else { None }