diff --git a/README.md b/README.md index d4fb8c6..2707c48 100755 --- a/README.md +++ b/README.md @@ -296,7 +296,7 @@ Note: Multiple subscription attempts on the same client return an error. ### Unified Event Interface -- **UnifiedEvent Trait**: All protocol events implement a common interface +- **DexEvent Trait**: All protocol events implement a common interface - **Protocol Enum**: Easy identification of event sources - **Event Factory**: Automatic event parsing and categorization diff --git a/README_CN.md b/README_CN.md index 495d104..f009537 100644 --- a/README_CN.md +++ b/README_CN.md @@ -295,7 +295,7 @@ grpc.update_subscription( ### 统一事件接口 -- **UnifiedEvent Trait**: 所有协议事件实现通用接口 +- **DexEvent Trait**: 所有协议事件实现通用接口 - **Protocol Enum**: 轻松识别事件来源 - **Event Factory**: 自动事件解析和分类 diff --git a/examples/dynamic_subscription.rs b/examples/dynamic_subscription.rs index d6e84f9..38edeb2 100644 --- a/examples/dynamic_subscription.rs +++ b/examples/dynamic_subscription.rs @@ -31,10 +31,10 @@ async fn main() -> Result<()> { let counter = event_counter.clone(); let callback = - move |event: Box| { + move |event: solana_streamer_sdk::streaming::event_parser::DexEvent| { let count = counter.fetch_add(1, Ordering::Relaxed); - let protocol = match event.event_type() { + let protocol = match event.metadata().event_type { EventType::PumpFunBuy | EventType::PumpFunSell => "PumpFun", EventType::RaydiumCpmmSwapBaseInput | EventType::RaydiumCpmmSwapBaseOutput => { "RaydiumCpmm" @@ -42,7 +42,7 @@ async fn main() -> Result<()> { _ => "Unknown", }; - println!("Event #{}: {:11} - {:.8}...", count + 1, protocol, event.signature()); + println!("Event #{}: {:11} - {:.8}...", count + 1, protocol, event.metadata().signature); }; println!("\n=== Phase 1: PumpFun only ==="); @@ -254,7 +254,7 @@ async fn main() -> Result<()> { let shutdown_event_counter = Arc::new(AtomicU64::new(0)); let shutdown_counter = shutdown_event_counter.clone(); let shutdown_callback = - move |_event: Box| { + move |_event: solana_streamer_sdk::streaming::event_parser::DexEvent| { shutdown_counter.fetch_add(1, Ordering::Relaxed); }; @@ -327,7 +327,7 @@ async fn main() -> Result<()> { println!("\n=== Subscription enforcement ==="); let test_callback = - |_event: Box| {}; + |_event: solana_streamer_sdk::streaming::event_parser::DexEvent| {}; match client .subscribe_events_immediate( @@ -358,7 +358,7 @@ async fn main() -> Result<()> { let client2_counter = Arc::new(AtomicU64::new(0)); let counter2 = client2_counter.clone(); let client2_callback = - move |_event: Box| { + move |_event: solana_streamer_sdk::streaming::event_parser::DexEvent| { counter2.fetch_add(1, Ordering::Relaxed); }; @@ -390,7 +390,7 @@ async fn main() -> Result<()> { println!("\n=== Advanced subscription enforcement ==="); let test_callback_advanced = - |_event: Box| {}; + |_event: solana_streamer_sdk::streaming::event_parser::DexEvent| {}; let client3 = Arc::new(YellowstoneGrpc::new(GRPC_ENDPOINT.to_string(), API_KEY.map(|s| s.to_string()))?); @@ -447,7 +447,7 @@ async fn main() -> Result<()> { let client4_counter = Arc::new(AtomicU64::new(0)); let counter4 = client4_counter.clone(); let client4_callback = - move |_event: Box| { + move |_event: solana_streamer_sdk::streaming::event_parser::DexEvent| { counter4.fetch_add(1, Ordering::Relaxed); }; diff --git a/examples/grpc_example.rs b/examples/grpc_example.rs index b904628..73c61d8 100644 --- a/examples/grpc_example.rs +++ b/examples/grpc_example.rs @@ -1,52 +1,17 @@ -use solana_streamer_sdk::{ - match_event, - streaming::{ - event_parser::{ - common::EventType, - core::account_event_parser::{NonceAccountEvent, TokenAccountEvent, TokenInfoEvent}, - protocols::{ - bonk::{ - parser::BONK_PROGRAM_ID, BonkGlobalConfigAccountEvent, BonkMigrateToAmmEvent, - BonkMigrateToCpswapEvent, BonkPlatformConfigAccountEvent, BonkPoolCreateEvent, - BonkPoolStateAccountEvent, BonkTradeEvent, - }, - pumpfun::{ - parser::PUMPFUN_PROGRAM_ID, PumpFunBondingCurveAccountEvent, - PumpFunCreateTokenEvent, PumpFunGlobalAccountEvent, PumpFunMigrateEvent, - PumpFunTradeEvent, - }, - pumpswap::{ - parser::PUMPSWAP_PROGRAM_ID, PumpSwapBuyEvent, PumpSwapCreatePoolEvent, - PumpSwapDepositEvent, PumpSwapGlobalConfigAccountEvent, - PumpSwapPoolAccountEvent, PumpSwapSellEvent, PumpSwapWithdrawEvent, - }, - raydium_amm_v4::{ - parser::RAYDIUM_AMM_V4_PROGRAM_ID, RaydiumAmmV4AmmInfoAccountEvent, - RaydiumAmmV4DepositEvent, RaydiumAmmV4Initialize2Event, RaydiumAmmV4SwapEvent, - RaydiumAmmV4WithdrawEvent, RaydiumAmmV4WithdrawPnlEvent, - }, - raydium_clmm::{ - parser::RAYDIUM_CLMM_PROGRAM_ID, RaydiumClmmAmmConfigAccountEvent, - RaydiumClmmClosePositionEvent, RaydiumClmmCreatePoolEvent, - RaydiumClmmDecreaseLiquidityV2Event, RaydiumClmmIncreaseLiquidityV2Event, - RaydiumClmmOpenPositionV2Event, RaydiumClmmOpenPositionWithToken22NftEvent, - RaydiumClmmPoolStateAccountEvent, RaydiumClmmSwapEvent, RaydiumClmmSwapV2Event, - RaydiumClmmTickArrayStateAccountEvent, - }, - raydium_cpmm::{ - parser::RAYDIUM_CPMM_PROGRAM_ID, RaydiumCpmmAmmConfigAccountEvent, - RaydiumCpmmDepositEvent, RaydiumCpmmInitializeEvent, - RaydiumCpmmPoolStateAccountEvent, RaydiumCpmmSwapEvent, - RaydiumCpmmWithdrawEvent, - }, - BlockMetaEvent, - }, - Protocol, UnifiedEvent, +use solana_streamer_sdk::streaming::{ + event_parser::{ + protocols::{ + bonk::parser::BONK_PROGRAM_ID, pumpfun::parser::PUMPFUN_PROGRAM_ID, + pumpswap::parser::PUMPSWAP_PROGRAM_ID, + raydium_amm_v4::parser::RAYDIUM_AMM_V4_PROGRAM_ID, + raydium_clmm::parser::RAYDIUM_CLMM_PROGRAM_ID, + raydium_cpmm::parser::RAYDIUM_CPMM_PROGRAM_ID, }, - grpc::ClientConfig, - yellowstone_grpc::{AccountFilter, TransactionFilter}, - YellowstoneGrpc, + Protocol, DexEvent, }, + grpc::ClientConfig, + yellowstone_grpc::{AccountFilter, TransactionFilter}, + YellowstoneGrpc, }; #[tokio::main] @@ -60,7 +25,7 @@ async fn test_grpc() -> Result<(), Box> { println!("Subscribing to Yellowstone gRPC events..."); // Create low-latency configuration - let mut config: ClientConfig = ClientConfig::low_latency(); + let mut config: ClientConfig = ClientConfig::default(); // Enable performance monitoring, has performance overhead, disabled by default config.enable_metrics = true; let grpc = YellowstoneGrpc::new_with_config( @@ -105,7 +70,8 @@ async fn test_grpc() -> Result<(), Box> { }; // Listen to account data belonging to owner programs -> account event monitoring - let account_filter = AccountFilter { account: vec![], owner: account_include.clone(), filters: vec![] }; + let account_filter = + AccountFilter { account: vec![], owner: account_include.clone(), filters: vec![] }; // Event filtering // No event filtering, includes all events @@ -142,162 +108,21 @@ async fn test_grpc() -> Result<(), Box> { Ok(()) } -fn create_event_callback() -> impl Fn(Box) { - |event: Box| { +fn create_event_callback() -> impl Fn(DexEvent) { + |event: DexEvent| { println!( "🎉 Event received! Type: {:?}, transaction_index: {:?}", - event.event_type(), - event.transaction_index() + event.metadata().event_type, + event.metadata().transaction_index ); - match_event!(event, { - // -------------------------- block meta ----------------------- - BlockMetaEvent => |e: BlockMetaEvent| { - println!("BlockMetaEvent: {:?}", e.metadata.handle_us); - }, - // -------------------------- bonk ----------------------- - BonkPoolCreateEvent => |e: BonkPoolCreateEvent| { - // When using grpc, you can get block_time from each event - println!("block_time: {:?}, block_time_ms: {:?}", e.metadata.block_time, e.metadata.block_time_ms); - println!("BonkPoolCreateEvent: {:?}", e.base_mint_param.symbol); - }, - BonkTradeEvent => |e: BonkTradeEvent| { - println!("BonkTradeEvent: {e:?}"); - }, - BonkMigrateToAmmEvent => |e: BonkMigrateToAmmEvent| { - println!("BonkMigrateToAmmEvent: {e:?}"); - }, - BonkMigrateToCpswapEvent => |e: BonkMigrateToCpswapEvent| { - println!("BonkMigrateToCpswapEvent: {e:?}"); - }, - // -------------------------- pumpfun ----------------------- - PumpFunTradeEvent => |e: PumpFunTradeEvent| { - println!("PumpFunTradeEvent: {e:?}"); - }, - PumpFunMigrateEvent => |e: PumpFunMigrateEvent| { - println!("PumpFunMigrateEvent: {e:?}"); - }, - PumpFunCreateTokenEvent => |e: PumpFunCreateTokenEvent| { - println!("PumpFunCreateTokenEvent: {e:?}"); - }, - // -------------------------- pumpswap ----------------------- - PumpSwapBuyEvent => |e: PumpSwapBuyEvent| { - println!("Buy event: {e:?}"); - }, - PumpSwapSellEvent => |e: PumpSwapSellEvent| { - println!("Sell event: {e:?}"); - }, - PumpSwapCreatePoolEvent => |e: PumpSwapCreatePoolEvent| { - println!("CreatePool event: {e:?}"); - }, - PumpSwapDepositEvent => |e: PumpSwapDepositEvent| { - println!("Deposit event: {e:?}"); - }, - PumpSwapWithdrawEvent => |e: PumpSwapWithdrawEvent| { - println!("Withdraw event: {e:?}"); - }, - // -------------------------- raydium_cpmm ----------------------- - RaydiumCpmmSwapEvent => |e: RaydiumCpmmSwapEvent| { - println!("RaydiumCpmmSwapEvent: {e:?}"); - }, - RaydiumCpmmDepositEvent => |e: RaydiumCpmmDepositEvent| { - println!("RaydiumCpmmDepositEvent: {e:?}"); - }, - RaydiumCpmmInitializeEvent => |e: RaydiumCpmmInitializeEvent| { - println!("RaydiumCpmmInitializeEvent: {e:?}"); - }, - RaydiumCpmmWithdrawEvent => |e: RaydiumCpmmWithdrawEvent| { - println!("RaydiumCpmmWithdrawEvent: {e:?}"); - }, - // -------------------------- raydium_clmm ----------------------- - RaydiumClmmSwapEvent => |e: RaydiumClmmSwapEvent| { - println!("RaydiumClmmSwapEvent: {e:?}"); - }, - RaydiumClmmSwapV2Event => |e: RaydiumClmmSwapV2Event| { - println!("RaydiumClmmSwapV2Event: {e:?}"); - }, - RaydiumClmmClosePositionEvent => |e: RaydiumClmmClosePositionEvent| { - println!("RaydiumClmmClosePositionEvent: {e:?}"); - }, - RaydiumClmmDecreaseLiquidityV2Event => |e: RaydiumClmmDecreaseLiquidityV2Event| { - println!("RaydiumClmmDecreaseLiquidityV2Event: {e:?}"); - }, - RaydiumClmmCreatePoolEvent => |e: RaydiumClmmCreatePoolEvent| { - println!("RaydiumClmmCreatePoolEvent: {e:?}"); - }, - RaydiumClmmIncreaseLiquidityV2Event => |e: RaydiumClmmIncreaseLiquidityV2Event| { - println!("RaydiumClmmIncreaseLiquidityV2Event: {e:?}"); - }, - RaydiumClmmOpenPositionWithToken22NftEvent => |e: RaydiumClmmOpenPositionWithToken22NftEvent| { - println!("RaydiumClmmOpenPositionWithToken22NftEvent: {e:?}"); - }, - RaydiumClmmOpenPositionV2Event => |e: RaydiumClmmOpenPositionV2Event| { - println!("RaydiumClmmOpenPositionV2Event: {e:?}"); - }, - // -------------------------- raydium_amm_v4 ----------------------- - RaydiumAmmV4SwapEvent => |e: RaydiumAmmV4SwapEvent| { - println!("RaydiumAmmV4SwapEvent: {e:?}"); - }, - RaydiumAmmV4DepositEvent => |e: RaydiumAmmV4DepositEvent| { - println!("RaydiumAmmV4DepositEvent: {e:?}"); - }, - RaydiumAmmV4Initialize2Event => |e: RaydiumAmmV4Initialize2Event| { - println!("RaydiumAmmV4Initialize2Event: {e:?}"); - }, - RaydiumAmmV4WithdrawEvent => |e: RaydiumAmmV4WithdrawEvent| { - println!("RaydiumAmmV4WithdrawEvent: {e:?}"); - }, - RaydiumAmmV4WithdrawPnlEvent => |e: RaydiumAmmV4WithdrawPnlEvent| { - println!("RaydiumAmmV4WithdrawPnlEvent: {e:?}"); - }, - // -------------------------- account ----------------------- - BonkPoolStateAccountEvent => |e: BonkPoolStateAccountEvent| { - println!("BonkPoolStateAccountEvent: {e:?}"); - }, - BonkGlobalConfigAccountEvent => |e: BonkGlobalConfigAccountEvent| { - println!("BonkGlobalConfigAccountEvent: {e:?}"); - }, - BonkPlatformConfigAccountEvent => |e: BonkPlatformConfigAccountEvent| { - println!("BonkPlatformConfigAccountEvent: {e:?}"); - }, - PumpSwapGlobalConfigAccountEvent => |e: PumpSwapGlobalConfigAccountEvent| { - println!("PumpSwapGlobalConfigAccountEvent: {e:?}"); - }, - PumpSwapPoolAccountEvent => |e: PumpSwapPoolAccountEvent| { - println!("PumpSwapPoolAccountEvent: {e:?}"); - }, - PumpFunBondingCurveAccountEvent => |e: PumpFunBondingCurveAccountEvent| { - println!("PumpFunBondingCurveAccountEvent: {e:?}"); - }, - PumpFunGlobalAccountEvent => |e: PumpFunGlobalAccountEvent| { - println!("PumpFunGlobalAccountEvent: {e:?}"); - }, - RaydiumAmmV4AmmInfoAccountEvent => |e: RaydiumAmmV4AmmInfoAccountEvent| { - println!("RaydiumAmmV4AmmInfoAccountEvent: {e:?}"); - }, - RaydiumClmmAmmConfigAccountEvent => |e: RaydiumClmmAmmConfigAccountEvent| { - println!("RaydiumClmmAmmConfigAccountEvent: {e:?}"); - }, - RaydiumClmmPoolStateAccountEvent => |e: RaydiumClmmPoolStateAccountEvent| { - println!("RaydiumClmmPoolStateAccountEvent: {e:?}"); - }, - RaydiumClmmTickArrayStateAccountEvent => |e: RaydiumClmmTickArrayStateAccountEvent| { - println!("RaydiumClmmTickArrayStateAccountEvent: {e:?}"); - }, - RaydiumCpmmAmmConfigAccountEvent => |e: RaydiumCpmmAmmConfigAccountEvent| { - println!("RaydiumCpmmAmmConfigAccountEvent: {e:?}"); - }, - RaydiumCpmmPoolStateAccountEvent => |e: RaydiumCpmmPoolStateAccountEvent| { - println!("RaydiumCpmmPoolStateAccountEvent: {e:?}"); - }, - TokenAccountEvent => |e: TokenAccountEvent| { - println!("TokenAccountEvent: {e:?}"); - }, - NonceAccountEvent => |e: NonceAccountEvent| { - println!("NonceAccountEvent: {e:?}"); - }, - TokenInfoEvent => |e: TokenInfoEvent| { - println!("TokenInfoEvent: {e:?}"); - }, - }); + match event { + DexEvent::BlockMetaEvent(e) => { + println!("{:?}", e); + } + // .... other events + _ => { + println!("{:?}", event); + } + } } } diff --git a/examples/mint_all_ata_account_listen_example.rs b/examples/mint_all_ata_account_listen_example.rs index b0fc11d..86368ea 100644 --- a/examples/mint_all_ata_account_listen_example.rs +++ b/examples/mint_all_ata_account_listen_example.rs @@ -1,18 +1,14 @@ use std::str::FromStr; use solana_sdk::pubkey::Pubkey; -use solana_streamer_sdk::{ - match_event, - streaming::{ - event_parser::{ - common::{filter::EventTypeFilter, EventType}, - core::account_event_parser::TokenAccountEvent, - UnifiedEvent, - }, - grpc::ClientConfig, - yellowstone_grpc::{AccountFilter, TransactionFilter}, - YellowstoneGrpc, +use solana_streamer_sdk::streaming::{ + event_parser::{ + common::{filter::EventTypeFilter, EventType}, + DexEvent, }, + grpc::ClientConfig, + yellowstone_grpc::{AccountFilter, TransactionFilter}, + YellowstoneGrpc, }; use yellowstone_grpc_proto::geyser::{ subscribe_request_filter_accounts_filter::Filter, @@ -30,7 +26,7 @@ async fn main() -> Result<(), Box> { async fn test_grpc() -> Result<(), Box> { println!("Subscribing to Yellowstone gRPC events..."); // Create low-latency configuration - let mut config: ClientConfig = ClientConfig::low_latency(); + let mut config: ClientConfig = ClientConfig::default(); // Enable performance monitoring, has performance overhead, disabled by default config.enable_metrics = true; let grpc = YellowstoneGrpc::new_with_config( @@ -106,12 +102,11 @@ async fn test_grpc() -> Result<(), Box> { Ok(()) } -fn create_event_callback() -> impl Fn(Box) { - |event: Box| { - match_event!(event, { - TokenAccountEvent => |e: TokenAccountEvent| { - println!("TokenAccount: {:?} amount: {:?}", e.pubkey, e.amount); - }, - }); +fn create_event_callback() -> impl Fn(DexEvent) { + |event: DexEvent| match event { + DexEvent::TokenAccountEvent(e) => { + println!("TokenAccount: {:?} amount: {:?}", e.pubkey, e.amount); + } + _ => {} } } diff --git a/examples/nonce_listen_example.rs b/examples/nonce_listen_example.rs index fc4c52f..32e2a4e 100644 --- a/examples/nonce_listen_example.rs +++ b/examples/nonce_listen_example.rs @@ -1,7 +1,7 @@ use solana_streamer_sdk::streaming::{ event_parser::{ common::{filter::EventTypeFilter, EventType}, - UnifiedEvent, + DexEvent, }, grpc::ClientConfig, yellowstone_grpc::{AccountFilter, TransactionFilter}, @@ -18,7 +18,7 @@ async fn main() -> Result<(), Box> { async fn test_grpc() -> Result<(), Box> { println!("Subscribing to Yellowstone gRPC events..."); // Create low-latency configuration - let mut config: ClientConfig = ClientConfig::low_latency(); + let mut config: ClientConfig = ClientConfig::default(); // Enable performance monitoring, has performance overhead, disabled by default config.enable_metrics = true; let grpc = YellowstoneGrpc::new_with_config( @@ -75,8 +75,8 @@ async fn test_grpc() -> Result<(), Box> { Ok(()) } -fn create_event_callback() -> impl Fn(Box) { - |event: Box| { +fn create_event_callback() -> impl Fn(DexEvent) { + |event: DexEvent| { println!("🎉 Event received! {:?}", event); } } diff --git a/examples/parse_tx_events.rs b/examples/parse_tx_events.rs index 61c884b..677e281 100644 --- a/examples/parse_tx_events.rs +++ b/examples/parse_tx_events.rs @@ -2,7 +2,7 @@ use anyhow::Result; use solana_commitment_config::CommitmentConfig; use solana_streamer_sdk::streaming::event_parser::core::event_parser::EventParser; use solana_streamer_sdk::streaming::event_parser::Protocol; -use solana_streamer_sdk::streaming::event_parser::UnifiedEvent; +use solana_streamer_sdk::streaming::event_parser::DexEvent; use std::str::FromStr; use std::sync::Arc; /// Get transaction data based on transaction signature @@ -96,16 +96,16 @@ async fn get_single_transaction_details(signature_str: &str) -> Result<()> { Protocol::RaydiumCpmm, Protocol::RaydiumAmmV4, ]; - let parser: Arc = Arc::new(EventParser::new(protocols, None)); - parser - .parse_encoded_confirmed_transaction_with_status_meta( - signature, - transaction, - Arc::new(move |event: &Box| { - println!("{:?}\n", event); - }), - ) - .await?; + EventParser::parse_encoded_confirmed_transaction_with_status_meta( + &protocols, + None, + signature, + transaction, + Arc::new(move |event: &DexEvent| { + println!("{:?}\n", event); + }), + ) + .await?; } Err(e) => { println!("Failed to get transaction: {}", e); diff --git a/examples/pumpswap_pool_account_listen_example.rs b/examples/pumpswap_pool_account_listen_example.rs index b7fe92e..72f83e2 100644 --- a/examples/pumpswap_pool_account_listen_example.rs +++ b/examples/pumpswap_pool_account_listen_example.rs @@ -1,18 +1,14 @@ use std::str::FromStr; use solana_sdk::pubkey::Pubkey; -use solana_streamer_sdk::{ - match_event, - streaming::{ - event_parser::{ - common::{filter::EventTypeFilter, EventType}, - core::account_event_parser::TokenAccountEvent, - UnifiedEvent, - }, - grpc::ClientConfig, - yellowstone_grpc::{AccountFilter, TransactionFilter}, - YellowstoneGrpc, +use solana_streamer_sdk::streaming::{ + event_parser::{ + common::{filter::EventTypeFilter, EventType}, + DexEvent, }, + grpc::ClientConfig, + yellowstone_grpc::{AccountFilter, TransactionFilter}, + YellowstoneGrpc, }; use yellowstone_grpc_proto::geyser::{ subscribe_request_filter_accounts_filter::Filter, @@ -108,12 +104,11 @@ async fn test_grpc() -> Result<(), Box> { Ok(()) } -fn create_event_callback() -> impl Fn(Box) { - |event: Box| { - match_event!(event, { - TokenAccountEvent => |e: TokenAccountEvent| { - println!("TokenAccount: {:?} amount: {:?}", e.pubkey, e.amount); - }, - }); +fn create_event_callback() -> impl Fn(DexEvent) { + |event: DexEvent| match event { + DexEvent::TokenAccountEvent(e) => { + println!("TokenAccount: {:?} amount: {:?}", e.pubkey, e.amount); + } + _ => {} } } diff --git a/examples/shred_example.rs b/examples/shred_example.rs index bc7bb34..c01b74d 100644 --- a/examples/shred_example.rs +++ b/examples/shred_example.rs @@ -1,51 +1,7 @@ -use solana_streamer_sdk::{ - match_event, - streaming::{ - event_parser::{ - common::EventType, - core::account_event_parser::TokenAccountEvent, - protocols::{ - bonk::{ - BonkGlobalConfigAccountEvent, BonkMigrateToAmmEvent, - BonkMigrateToCpswapEvent, BonkPlatformConfigAccountEvent, BonkPoolCreateEvent, - BonkPoolStateAccountEvent, BonkTradeEvent, - }, - pumpfun::{ - PumpFunBondingCurveAccountEvent, - PumpFunCreateTokenEvent, PumpFunGlobalAccountEvent, PumpFunMigrateEvent, - PumpFunTradeEvent, - }, - pumpswap::{ - PumpSwapBuyEvent, PumpSwapCreatePoolEvent, - PumpSwapDepositEvent, PumpSwapGlobalConfigAccountEvent, - PumpSwapPoolAccountEvent, PumpSwapSellEvent, PumpSwapWithdrawEvent, - }, - raydium_amm_v4::{ - RaydiumAmmV4AmmInfoAccountEvent, - RaydiumAmmV4DepositEvent, RaydiumAmmV4Initialize2Event, RaydiumAmmV4SwapEvent, - RaydiumAmmV4WithdrawEvent, RaydiumAmmV4WithdrawPnlEvent, - }, - raydium_clmm::{ - RaydiumClmmAmmConfigAccountEvent, - RaydiumClmmClosePositionEvent, RaydiumClmmCreatePoolEvent, - RaydiumClmmDecreaseLiquidityV2Event, RaydiumClmmIncreaseLiquidityV2Event, - RaydiumClmmOpenPositionV2Event, RaydiumClmmOpenPositionWithToken22NftEvent, - RaydiumClmmPoolStateAccountEvent, RaydiumClmmSwapEvent, RaydiumClmmSwapV2Event, - RaydiumClmmTickArrayStateAccountEvent, - }, - raydium_cpmm::{ - RaydiumCpmmAmmConfigAccountEvent, - RaydiumCpmmDepositEvent, RaydiumCpmmInitializeEvent, - RaydiumCpmmPoolStateAccountEvent, RaydiumCpmmSwapEvent, - RaydiumCpmmWithdrawEvent, - }, - BlockMetaEvent, - }, - Protocol, UnifiedEvent, - }, - shred::StreamClientConfig, - ShredStreamGrpc, - }, +use solana_streamer_sdk::streaming::{ + event_parser::{Protocol, DexEvent}, + shred::StreamClientConfig, + ShredStreamGrpc, }; #[tokio::main] @@ -98,156 +54,18 @@ async fn test_shreds() -> Result<(), Box> { Ok(()) } -fn create_event_callback() -> impl Fn(Box) { - |event: Box| { +fn create_event_callback() -> impl Fn(DexEvent) { + |event: DexEvent| { println!( "🎉 Event received! Type: {:?}, transaction_index: {:?}", - event.event_type(), - event.transaction_index() + event.metadata().event_type, + event.metadata().transaction_index ); - match_event!(event, { - // -------------------------- block meta ----------------------- - BlockMetaEvent => |e: BlockMetaEvent| { + match event { + DexEvent::BlockMetaEvent(e) => { println!("BlockMetaEvent: {:?}", e.metadata.handle_us); - }, - // -------------------------- bonk ----------------------- - BonkPoolCreateEvent => |e: BonkPoolCreateEvent| { - // When using grpc, you can get block_time from each event - println!("block_time: {:?}, block_time_ms: {:?}", e.metadata.block_time, e.metadata.block_time_ms); - println!("BonkPoolCreateEvent: {:?}", e.base_mint_param.symbol); - }, - BonkTradeEvent => |e: BonkTradeEvent| { - println!("BonkTradeEvent: {e:?}"); - }, - BonkMigrateToAmmEvent => |e: BonkMigrateToAmmEvent| { - println!("BonkMigrateToAmmEvent: {e:?}"); - }, - BonkMigrateToCpswapEvent => |e: BonkMigrateToCpswapEvent| { - println!("BonkMigrateToCpswapEvent: {e:?}"); - }, - // -------------------------- pumpfun ----------------------- - PumpFunTradeEvent => |e: PumpFunTradeEvent| { - println!("PumpFunTradeEvent: {e:?}"); - }, - PumpFunMigrateEvent => |e: PumpFunMigrateEvent| { - println!("PumpFunMigrateEvent: {e:?}"); - }, - PumpFunCreateTokenEvent => |e: PumpFunCreateTokenEvent| { - println!("PumpFunCreateTokenEvent: {e:?}"); - }, - // -------------------------- pumpswap ----------------------- - PumpSwapBuyEvent => |e: PumpSwapBuyEvent| { - println!("Buy event: {e:?}"); - }, - PumpSwapSellEvent => |e: PumpSwapSellEvent| { - println!("Sell event: {e:?}"); - }, - PumpSwapCreatePoolEvent => |e: PumpSwapCreatePoolEvent| { - println!("CreatePool event: {e:?}"); - }, - PumpSwapDepositEvent => |e: PumpSwapDepositEvent| { - println!("Deposit event: {e:?}"); - }, - PumpSwapWithdrawEvent => |e: PumpSwapWithdrawEvent| { - println!("Withdraw event: {e:?}"); - }, - // -------------------------- raydium_cpmm ----------------------- - RaydiumCpmmSwapEvent => |e: RaydiumCpmmSwapEvent| { - println!("RaydiumCpmmSwapEvent: {e:?}"); - }, - RaydiumCpmmDepositEvent => |e: RaydiumCpmmDepositEvent| { - println!("RaydiumCpmmDepositEvent: {e:?}"); - }, - RaydiumCpmmInitializeEvent => |e: RaydiumCpmmInitializeEvent| { - println!("RaydiumCpmmInitializeEvent: {e:?}"); - }, - RaydiumCpmmWithdrawEvent => |e: RaydiumCpmmWithdrawEvent| { - println!("RaydiumCpmmWithdrawEvent: {e:?}"); - }, - // -------------------------- raydium_clmm ----------------------- - RaydiumClmmSwapEvent => |e: RaydiumClmmSwapEvent| { - println!("RaydiumClmmSwapEvent: {e:?}"); - }, - RaydiumClmmSwapV2Event => |e: RaydiumClmmSwapV2Event| { - println!("RaydiumClmmSwapV2Event: {e:?}"); - }, - RaydiumClmmClosePositionEvent => |e: RaydiumClmmClosePositionEvent| { - println!("RaydiumClmmClosePositionEvent: {e:?}"); - }, - RaydiumClmmDecreaseLiquidityV2Event => |e: RaydiumClmmDecreaseLiquidityV2Event| { - println!("RaydiumClmmDecreaseLiquidityV2Event: {e:?}"); - }, - RaydiumClmmCreatePoolEvent => |e: RaydiumClmmCreatePoolEvent| { - println!("RaydiumClmmCreatePoolEvent: {e:?}"); - }, - RaydiumClmmIncreaseLiquidityV2Event => |e: RaydiumClmmIncreaseLiquidityV2Event| { - println!("RaydiumClmmIncreaseLiquidityV2Event: {e:?}"); - }, - RaydiumClmmOpenPositionWithToken22NftEvent => |e: RaydiumClmmOpenPositionWithToken22NftEvent| { - println!("RaydiumClmmOpenPositionWithToken22NftEvent: {e:?}"); - }, - RaydiumClmmOpenPositionV2Event => |e: RaydiumClmmOpenPositionV2Event| { - println!("RaydiumClmmOpenPositionV2Event: {e:?}"); - }, - // -------------------------- raydium_amm_v4 ----------------------- - RaydiumAmmV4SwapEvent => |e: RaydiumAmmV4SwapEvent| { - println!("RaydiumAmmV4SwapEvent: {e:?}"); - }, - RaydiumAmmV4DepositEvent => |e: RaydiumAmmV4DepositEvent| { - println!("RaydiumAmmV4DepositEvent: {e:?}"); - }, - RaydiumAmmV4Initialize2Event => |e: RaydiumAmmV4Initialize2Event| { - println!("RaydiumAmmV4Initialize2Event: {e:?}"); - }, - RaydiumAmmV4WithdrawEvent => |e: RaydiumAmmV4WithdrawEvent| { - println!("RaydiumAmmV4WithdrawEvent: {e:?}"); - }, - RaydiumAmmV4WithdrawPnlEvent => |e: RaydiumAmmV4WithdrawPnlEvent| { - println!("RaydiumAmmV4WithdrawPnlEvent: {e:?}"); - }, - // -------------------------- account ----------------------- - BonkPoolStateAccountEvent => |e: BonkPoolStateAccountEvent| { - println!("BonkPoolStateAccountEvent: {e:?}"); - }, - BonkGlobalConfigAccountEvent => |e: BonkGlobalConfigAccountEvent| { - println!("BonkGlobalConfigAccountEvent: {e:?}"); - }, - BonkPlatformConfigAccountEvent => |e: BonkPlatformConfigAccountEvent| { - println!("BonkPlatformConfigAccountEvent: {e:?}"); - }, - PumpSwapGlobalConfigAccountEvent => |e: PumpSwapGlobalConfigAccountEvent| { - println!("PumpSwapGlobalConfigAccountEvent: {e:?}"); - }, - PumpSwapPoolAccountEvent => |e: PumpSwapPoolAccountEvent| { - println!("PumpSwapPoolAccountEvent: {e:?}"); - }, - PumpFunBondingCurveAccountEvent => |e: PumpFunBondingCurveAccountEvent| { - println!("PumpFunBondingCurveAccountEvent: {e:?}"); - }, - PumpFunGlobalAccountEvent => |e: PumpFunGlobalAccountEvent| { - println!("PumpFunGlobalAccountEvent: {e:?}"); - }, - RaydiumAmmV4AmmInfoAccountEvent => |e: RaydiumAmmV4AmmInfoAccountEvent| { - println!("RaydiumAmmV4AmmInfoAccountEvent: {e:?}"); - }, - RaydiumClmmAmmConfigAccountEvent => |e: RaydiumClmmAmmConfigAccountEvent| { - println!("RaydiumClmmAmmConfigAccountEvent: {e:?}"); - }, - RaydiumClmmPoolStateAccountEvent => |e: RaydiumClmmPoolStateAccountEvent| { - println!("RaydiumClmmPoolStateAccountEvent: {e:?}"); - }, - RaydiumClmmTickArrayStateAccountEvent => |e: RaydiumClmmTickArrayStateAccountEvent| { - println!("RaydiumClmmTickArrayStateAccountEvent: {e:?}"); - }, - RaydiumCpmmAmmConfigAccountEvent => |e: RaydiumCpmmAmmConfigAccountEvent| { - println!("RaydiumCpmmAmmConfigAccountEvent: {e:?}"); - }, - RaydiumCpmmPoolStateAccountEvent => |e: RaydiumCpmmPoolStateAccountEvent| { - println!("RaydiumCpmmPoolStateAccountEvent: {e:?}"); - }, - TokenAccountEvent => |e: TokenAccountEvent| { - println!("TokenAccountEvent: {e:?}"); - }, - }); + } + _ => {} + } } -} \ No newline at end of file +} diff --git a/examples/token_balance_listen_example.rs b/examples/token_balance_listen_example.rs index ac1cb54..b36e750 100644 --- a/examples/token_balance_listen_example.rs +++ b/examples/token_balance_listen_example.rs @@ -1,7 +1,7 @@ use solana_streamer_sdk::streaming::{ event_parser::{ common::{filter::EventTypeFilter, EventType}, - UnifiedEvent, + DexEvent, }, grpc::ClientConfig, yellowstone_grpc::{AccountFilter, TransactionFilter}, @@ -76,8 +76,8 @@ async fn test_grpc() -> Result<(), Box> { Ok(()) } -fn create_event_callback() -> impl Fn(Box) { - |event: Box| { +fn create_event_callback() -> impl Fn(DexEvent) { + |event: DexEvent| { println!("🎉 Event received! {:?}", event); } } diff --git a/examples/token_decimals_listen_example.rs b/examples/token_decimals_listen_example.rs index 1abde5d..159db96 100644 --- a/examples/token_decimals_listen_example.rs +++ b/examples/token_decimals_listen_example.rs @@ -1,15 +1,11 @@ -use solana_streamer_sdk::{ - match_event, - streaming::{ - event_parser::{ - common::{filter::EventTypeFilter, EventType}, - core::account_event_parser::TokenInfoEvent, - UnifiedEvent, - }, - grpc::ClientConfig, - yellowstone_grpc::{AccountFilter, TransactionFilter}, - YellowstoneGrpc, +use solana_streamer_sdk::streaming::{ + event_parser::{ + common::{filter::EventTypeFilter, EventType}, + DexEvent, }, + grpc::ClientConfig, + yellowstone_grpc::{AccountFilter, TransactionFilter}, + YellowstoneGrpc, }; #[tokio::main] @@ -80,12 +76,11 @@ async fn test_grpc() -> Result<(), Box> { Ok(()) } -fn create_event_callback() -> impl Fn(Box) { - |event: Box| { - match_event!(event, { - TokenInfoEvent => |e: TokenInfoEvent| { - println!("TokenInfoEvent: {:?}", e.decimals); - }, - }); +fn create_event_callback() -> impl Fn(DexEvent) { + |event: DexEvent| match event { + DexEvent::TokenInfoEvent(e) => { + println!("TokenInfoEvent: {:?}", e.decimals); + } + _ => {} } } diff --git a/src/streaming/common/config.rs b/src/streaming/common/config.rs index c314813..de63689 100644 --- a/src/streaming/common/config.rs +++ b/src/streaming/common/config.rs @@ -1,35 +1,5 @@ use super::constants::*; -/// Backpressure handling strategy -#[derive(Debug, Clone, Copy)] -pub enum BackpressureStrategy { - /// Block and wait (default) - Block, - /// Drop messages - Drop, -} - -impl Default for BackpressureStrategy { - fn default() -> Self { - Self::Block - } -} - -/// Backpressure configuration -#[derive(Debug, Clone)] -pub struct BackpressureConfig { - /// Channel size (default: 1000) - pub permits: usize, - /// Backpressure handling strategy (default: Block) - pub strategy: BackpressureStrategy, -} - -impl Default for BackpressureConfig { - fn default() -> Self { - Self { permits: 3000, strategy: BackpressureStrategy::default() } - } -} - /// Connection configuration #[derive(Debug, Clone)] pub struct ConnectionConfig { @@ -56,57 +26,21 @@ impl Default for ConnectionConfig { pub struct StreamClientConfig { /// Connection configuration pub connection: ConnectionConfig, - /// Backpressure configuration - pub backpressure: BackpressureConfig, /// Whether performance monitoring is enabled (default: false) pub enable_metrics: bool, } impl Default for StreamClientConfig { fn default() -> Self { - Self { - connection: ConnectionConfig::default(), - backpressure: BackpressureConfig::default(), - enable_metrics: false, - } + Self { connection: ConnectionConfig::default(), enable_metrics: false } } } impl StreamClientConfig { - /// Creates a high-throughput configuration optimized for high-concurrency scenarios. - /// - /// This configuration prioritizes throughput over latency by: - /// - Implementing a drop strategy for backpressure to avoid blocking - /// - Setting a large permit buffer (5,000) to handle burst traffic - /// - /// Ideal for scenarios where you need to process large volumes of data - /// and can tolerate occasional message drops during peak loads. - pub fn high_throughput() -> Self { - Self { - connection: ConnectionConfig::default(), - backpressure: BackpressureConfig { - permits: 20000, - strategy: BackpressureStrategy::Drop, - }, - enable_metrics: false, - } - } - - /// Creates a low-latency configuration optimized for real-time scenarios. - /// - /// This configuration prioritizes latency over throughput by: - /// - Processing events immediately without buffering - /// - Implementing a blocking backpressure strategy to ensure no data loss - /// - Setting optimal permits (4000) for balanced throughput and latency - /// - /// Ideal for scenarios where every millisecond counts and you cannot - /// afford to lose any events, such as trading applications or real-time monitoring. pub fn low_latency() -> Self { - Self { - connection: ConnectionConfig::default(), - backpressure: BackpressureConfig { permits: 4000, strategy: BackpressureStrategy::Block }, - enable_metrics: false, - } + Self::default() + } + pub fn high_throughput() -> Self { + Self::default() } - } diff --git a/src/streaming/common/event_processor.rs b/src/streaming/common/event_processor.rs index 7d4915e..7e7c81e 100644 --- a/src/streaming/common/event_processor.rs +++ b/src/streaming/common/event_processor.rs @@ -1,429 +1,149 @@ -use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; -use std::sync::Arc; - -use crossbeam_queue::SegQueue; -use solana_sdk::pubkey::Pubkey; - use crate::common::AnyResult; -use crate::streaming::common::BackpressureStrategy; -use crate::streaming::common::{ - MetricsEventType, MetricsManager, StreamClientConfig as ClientConfig, -}; +use crate::streaming::common::MetricsEventType; use crate::streaming::event_parser::common::filter::EventTypeFilter; use crate::streaming::event_parser::core::account_event_parser::AccountEventParser; use crate::streaming::event_parser::core::common_event_parser::CommonEventParser; - use crate::streaming::event_parser::core::event_parser::EventParser; -use crate::streaming::event_parser::{core::traits::UnifiedEvent, Protocol}; -use crate::streaming::grpc::{BackpressureConfig, EventPretty}; +use crate::streaming::event_parser::{core::traits::DexEvent, Protocol}; +use crate::streaming::grpc::{EventPretty, MetricsManager}; use crate::streaming::shred::TransactionWithSlot; -use once_cell::sync::OnceCell; +use solana_sdk::pubkey::Pubkey; +use std::sync::Arc; -pub enum EventSource { - Grpc, - Shred, +/// 创建带 metrics 统计的 callback 包装器 +/// +/// 用于 Transaction 事件处理,在调用原始 callback 的同时更新 metrics +#[inline] +fn create_metrics_callback( + callback: Arc, +) -> Arc { + Arc::new(move |event: DexEvent| { + let processing_time_us = event.metadata().handle_us as f64; + callback(event); + MetricsManager::global().update_metrics( + MetricsEventType::Transaction, + 1, + processing_time_us, + ); + }) } -/// High-performance Event processor using SegQueue for all strategies -pub struct EventProcessor { - pub(crate) metrics_manager: MetricsManager, - pub(crate) config: ClientConfig, - pub(crate) parser_cache: OnceCell>, - pub(crate) protocols: Vec, - pub(crate) event_type_filter: Option, - pub(crate) callback: Option) + Send + Sync>>, - pub(crate) backpressure_config: BackpressureConfig, - pub(crate) grpc_queue: Arc)>>, - pub(crate) shred_queue: Arc)>>, - pub(crate) grpc_pending_count: Arc, - pub(crate) shred_pending_count: Arc, - pub(crate) processing_shutdown: Arc, -} +/// Process GRPC transaction events +pub async fn process_grpc_transaction( + event_pretty: EventPretty, + protocols: &[Protocol], + event_type_filter: Option<&EventTypeFilter>, + callback: Arc, + bot_wallet: Option, +) -> AnyResult<()> { + match event_pretty { + EventPretty::Account(account_pretty) => { + MetricsManager::global().add_account_process_count(); -impl EventProcessor { - pub fn new(metrics_manager: MetricsManager, config: ClientConfig) -> Self { - let backpressure_config = config.backpressure.clone(); - let grpc_queue = Arc::new(SegQueue::new()); - let shred_queue = Arc::new(SegQueue::new()); - let grpc_pending_count = Arc::new(AtomicUsize::new(0)); - let shred_pending_count = Arc::new(AtomicUsize::new(0)); - let processing_shutdown = Arc::new(AtomicBool::new(false)); + let account_event = AccountEventParser::parse_account_event( + protocols, + account_pretty, + event_type_filter, + ); - Self { - metrics_manager, - config, - parser_cache: OnceCell::new(), - protocols: vec![], - event_type_filter: None, - backpressure_config, - callback: None, - grpc_queue, - shred_queue, - grpc_pending_count, - shred_pending_count, - processing_shutdown, - } - } - - pub fn set_protocols_and_event_type_filter( - &mut self, - source: EventSource, - protocols: Vec, - event_type_filter: Option, - backpressure_config: BackpressureConfig, - callback: Option) + Send + Sync>>, - ) { - self.protocols = protocols; - self.event_type_filter = event_type_filter; - - self.backpressure_config = backpressure_config; - self.callback = callback; - let protocols_ref = &self.protocols; - let event_type_filter_ref = self.event_type_filter.as_ref(); - self.parser_cache.get_or_init(|| { - Arc::new(EventParser::new(protocols_ref.clone(), event_type_filter_ref.cloned())) - }); - - if matches!(self.backpressure_config.strategy, BackpressureStrategy::Block) { - self.start_block_processing_thread(source); - } - } - - pub fn get_parser(&self) -> Arc { - self.parser_cache.get().unwrap().clone() - } - - fn create_adapter_callback(&self) -> Arc) + Send + Sync> { - let callback = self.callback.clone().unwrap(); - let metrics_manager = self.metrics_manager.clone(); - - Arc::new(move |event: Box| { - let processing_time_us = event.handle_us() as f64; - callback(event); - metrics_manager.update_metrics(MetricsEventType::Transaction, 1, processing_time_us); - }) - } - - pub async fn process_grpc_event_transaction_with_metrics( - &self, - event_pretty: EventPretty, - bot_wallet: Option, - ) -> AnyResult<()> { - self.apply_backpressure_control(event_pretty, bot_wallet).await - } - - async fn apply_backpressure_control( - &self, - event_pretty: EventPretty, - bot_wallet: Option, - ) -> AnyResult<()> { - match self.backpressure_config.strategy { - BackpressureStrategy::Block => { - loop { - let current_pending = self.grpc_pending_count.load(Ordering::Relaxed); - if current_pending < self.backpressure_config.permits { - self.grpc_queue.push((event_pretty, bot_wallet)); - self.grpc_pending_count.fetch_add(1, Ordering::Relaxed); - break; - } - - tokio::task::yield_now().await; - } - Ok(()) - } - BackpressureStrategy::Drop => { - let current_pending = self.grpc_pending_count.load(Ordering::Relaxed); - if current_pending >= self.backpressure_config.permits { - self.metrics_manager.increment_dropped_events(); - Ok(()) - } else { - self.grpc_pending_count.fetch_add(1, Ordering::Relaxed); - let processor = self.clone(); - tokio::spawn(async move { - match processor - .process_grpc_event_transaction(event_pretty, bot_wallet) - .await - { - Ok(_) => { - processor.grpc_pending_count.fetch_sub(1, Ordering::Relaxed); - } - Err(e) => { - log::error!("Error in async gRPC processing: {}", e); - } - } - }); - Ok(()) - } + if let Some(event) = account_event { + let processing_time_us = event.metadata().handle_us as f64; + callback(event); + update_metrics(MetricsEventType::Account, 1, processing_time_us); } } - } + EventPretty::Transaction(transaction_pretty) => { + MetricsManager::global().add_tx_process_count(); - async fn process_grpc_event_transaction( - &self, - event_pretty: EventPretty, - bot_wallet: Option, - ) -> AnyResult<()> { - if self.callback.is_none() { - return Ok(()); - } - match event_pretty { - EventPretty::Account(account_pretty) => { - self.metrics_manager.add_account_process_count(); - let account_event = AccountEventParser::parse_account_event( - &self.protocols, - account_pretty, - self.event_type_filter.as_ref(), - ); - if let Some(event) = account_event { - let processing_time_us = event.handle_us() as f64; - self.invoke_callback(event); - self.update_metrics(MetricsEventType::Account, 1, processing_time_us); - } - } - EventPretty::Transaction(transaction_pretty) => { - self.metrics_manager.add_tx_process_count(); - let slot = transaction_pretty.slot; - let signature = transaction_pretty.signature; - let block_time = transaction_pretty.block_time; - let recv_us = transaction_pretty.recv_us; - let transaction_index = transaction_pretty.transaction_index; - let grpc_tx = transaction_pretty.grpc_tx; + let slot = transaction_pretty.slot; + let signature = transaction_pretty.signature; + let block_time = transaction_pretty.block_time; + let recv_us = transaction_pretty.recv_us; + let transaction_index = transaction_pretty.transaction_index; + let grpc_tx = transaction_pretty.grpc_tx; - let parser = self.get_parser(); - let adapter_callback = self.create_adapter_callback(); - parser - .parse_grpc_transaction_owned( - grpc_tx, - signature, - Some(slot), - block_time, - recv_us, - bot_wallet, - transaction_index, - adapter_callback, - ) - .await?; - } - EventPretty::BlockMeta(block_meta_pretty) => { - self.metrics_manager.add_block_meta_process_count(); - let block_time_ms = block_meta_pretty - .block_time - .map(|ts| ts.seconds * 1000 + ts.nanos as i64 / 1_000_000) - .unwrap_or_else(|| chrono::Utc::now().timestamp_millis()); - let block_meta_event = CommonEventParser::generate_block_meta_event( - block_meta_pretty.slot, - block_meta_pretty.block_hash, - block_time_ms, - block_meta_pretty.recv_us, - ); - let processing_time_us = block_meta_event.handle_us() as f64; - self.invoke_callback(block_meta_event); - self.update_metrics(MetricsEventType::BlockMeta, 1, processing_time_us); - } - } + let adapter_callback = create_metrics_callback(callback.clone()); - Ok(()) - } - - pub fn invoke_callback(&self, event: Box) { - if let Some(callback) = self.callback.as_ref() { - callback(event); - } - } - - pub async fn process_shred_transaction_immediate( - &self, - transaction_with_slot: TransactionWithSlot, - bot_wallet: Option, - ) -> AnyResult<()> { - self.process_shred_transaction(transaction_with_slot, bot_wallet).await - } - - pub async fn process_shred_transaction_with_metrics( - &self, - transaction_with_slot: TransactionWithSlot, - bot_wallet: Option, - ) -> AnyResult<()> { - self.apply_shred_backpressure_control(transaction_with_slot, bot_wallet).await - } - - async fn apply_shred_backpressure_control( - &self, - transaction_with_slot: TransactionWithSlot, - bot_wallet: Option, - ) -> AnyResult<()> { - match self.backpressure_config.strategy { - BackpressureStrategy::Block => { - loop { - let current_pending = self.shred_pending_count.load(Ordering::Relaxed); - if current_pending < self.backpressure_config.permits { - self.shred_queue.push((transaction_with_slot, bot_wallet)); - self.shred_pending_count.fetch_add(1, Ordering::Relaxed); - break; - } - - tokio::task::yield_now().await; - } - Ok(()) - } - BackpressureStrategy::Drop => { - let current_pending = self.shred_pending_count.load(Ordering::Relaxed); - if current_pending >= self.backpressure_config.permits { - self.metrics_manager.increment_dropped_events(); - Ok(()) - } else { - self.shred_pending_count.fetch_add(1, Ordering::Relaxed); - let processor = self.clone(); - tokio::spawn(async move { - match processor - .process_shred_transaction(transaction_with_slot, bot_wallet) - .await - { - Ok(_) => { - processor.shred_pending_count.fetch_sub(1, Ordering::Relaxed); - } - Err(e) => { - log::error!("Error in async shred processing: {}", e); - } - } - }); - Ok(()) - } - } - } - } - - pub async fn process_shred_transaction( - &self, - transaction_with_slot: TransactionWithSlot, - bot_wallet: Option, - ) -> AnyResult<()> { - if self.callback.is_none() { - return Ok(()); - } - self.metrics_manager.add_tx_process_count(); - let tx = transaction_with_slot.transaction; - - let slot = transaction_with_slot.slot; - if tx.signatures.is_empty() { - return Ok(()); - } - let signature = tx.signatures[0]; - let recv_us = transaction_with_slot.recv_us; - - let parser = self.get_parser(); - let adapter_callback = self.create_adapter_callback(); - parser - .parse_versioned_transaction_owned( - tx, + EventParser::parse_grpc_transaction_owned( + protocols, + event_type_filter, + grpc_tx, signature, Some(slot), - None, + block_time, recv_us, bot_wallet, - None, - &[], + transaction_index, adapter_callback, ) .await?; + } + EventPretty::BlockMeta(block_meta_pretty) => { + MetricsManager::global().add_block_meta_process_count(); - Ok(()) - } + let block_time_ms = block_meta_pretty + .block_time + .map(|ts| ts.seconds * 1000 + ts.nanos as i64 / 1_000_000) + .unwrap_or_else(|| chrono::Utc::now().timestamp_millis()); - fn update_metrics(&self, ty: MetricsEventType, count: u64, time_us: f64) { - self.metrics_manager.update_metrics(ty, count, time_us); - } + let block_meta_event = CommonEventParser::generate_block_meta_event( + block_meta_pretty.slot, + block_meta_pretty.block_hash, + block_time_ms, + block_meta_pretty.recv_us, + ); - fn start_block_processing_thread(&self, source: EventSource) { - self.processing_shutdown.store(false, Ordering::Relaxed); - - let grpc_queue = Arc::clone(&self.grpc_queue); - let shred_queue = Arc::clone(&self.shred_queue); - let grpc_pending_count = Arc::clone(&self.grpc_pending_count); - let shred_pending_count = Arc::clone(&self.shred_pending_count); - let shutdown_flag = Arc::clone(&self.processing_shutdown); - let shutdown_flag_clone = Arc::clone(&self.processing_shutdown); - let processor = self.clone(); - let processor_clone = self.clone(); - // Dedicated thread with busy-wait and lock-free processing - match source { - EventSource::Grpc => { - std::thread::spawn(move || { - let worker_threads = - std::thread::available_parallelism().map(|n| n.get()).unwrap_or(4); // 如果获取失败则回退到4个线程 - - let rt = tokio::runtime::Builder::new_multi_thread() - .worker_threads(worker_threads) - .enable_all() - .build() - .unwrap(); - - while !shutdown_flag.load(Ordering::Relaxed) { - if let Some((event_pretty, bot_wallet)) = grpc_queue.pop() { - grpc_pending_count.fetch_sub(1, Ordering::Relaxed); - if let Err(e) = rt.block_on( - processor.process_grpc_event_transaction(event_pretty, bot_wallet), - ) { - println!("Error processing gRPC event: {}", e); - } - } else { - // 待测试替换方案: lock-free queue + spin + batch - std::thread::sleep(std::time::Duration::from_micros(500)); - } - } - }); - } - EventSource::Shred => { - // Shred processing with same low-latency optimization - std::thread::spawn(move || { - let worker_threads = - std::thread::available_parallelism().map(|n| n.get()).unwrap_or(4); // 如果获取失败则回退到4个线程 - - let rt = tokio::runtime::Builder::new_multi_thread() - .worker_threads(worker_threads) - .enable_all() - .build() - .unwrap(); - - while !shutdown_flag_clone.load(Ordering::Relaxed) { - if let Some((transaction_with_slot, bot_wallet)) = shred_queue.pop() { - shred_pending_count.fetch_sub(1, Ordering::Relaxed); - if let Err(e) = rt.block_on( - processor_clone - .process_shred_transaction(transaction_with_slot, bot_wallet), - ) { - log::error!("Error processing shred transaction: {}", e); - } - } else { - // 待测试替换方案: lock-free queue + spin + batch - std::thread::sleep(std::time::Duration::from_micros(500)); - } - } - }); - } + let processing_time_us = block_meta_event.metadata().handle_us as f64; + callback(block_meta_event); + update_metrics(MetricsEventType::BlockMeta, 1, processing_time_us); } } - pub fn stop_processing(&self) { - self.processing_shutdown.store(true, Ordering::Relaxed); - } + Ok(()) } -impl Clone for EventProcessor { - fn clone(&self) -> Self { - Self { - metrics_manager: self.metrics_manager.clone(), - config: self.config.clone(), - parser_cache: self.parser_cache.clone(), - protocols: self.protocols.clone(), - event_type_filter: self.event_type_filter.clone(), - backpressure_config: self.backpressure_config.clone(), - callback: self.callback.clone(), - grpc_queue: self.grpc_queue.clone(), - shred_queue: self.shred_queue.clone(), - grpc_pending_count: self.grpc_pending_count.clone(), - shred_pending_count: self.shred_pending_count.clone(), - processing_shutdown: self.processing_shutdown.clone(), - } +/// Process Shred transaction events +pub async fn process_shred_transaction( + transaction_with_slot: TransactionWithSlot, + protocols: &[Protocol], + event_type_filter: Option<&EventTypeFilter>, + callback: Arc, + bot_wallet: Option, +) -> AnyResult<()> { + MetricsManager::global().add_tx_process_count(); + + let tx = transaction_with_slot.transaction; + let slot = transaction_with_slot.slot; + + if tx.signatures.is_empty() { + return Ok(()); } + + let signature = tx.signatures[0]; + let recv_us = transaction_with_slot.recv_us; + + let adapter_callback = create_metrics_callback(callback); + + EventParser::parse_versioned_transaction_owned( + protocols, + event_type_filter, + tx, + signature, + Some(slot), + None, + recv_us, + bot_wallet, + None, + &[], + adapter_callback, + ) + .await?; + + Ok(()) +} + +/// Update metrics for event processing +#[inline] +fn update_metrics(ty: MetricsEventType, count: u64, time_us: f64) { + MetricsManager::global().update_metrics(ty, count, time_us); } diff --git a/src/streaming/common/metrics.rs b/src/streaming/common/metrics.rs index f76e866..dea8bbf 100644 --- a/src/streaming/common/metrics.rs +++ b/src/streaming/common/metrics.rs @@ -1,5 +1,4 @@ use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; -use std::sync::Arc; use super::constants::*; @@ -44,13 +43,13 @@ struct AtomicEventMetrics { } impl AtomicEventMetrics { - fn new(now_nanos: u64) -> Self { + const fn new_const() -> Self { Self { process_count: AtomicU64::new(0), events_processed: AtomicU64::new(0), events_in_window: AtomicU64::new(0), - window_start_nanos: AtomicU64::new(now_nanos), - processing_stats: AtomicProcessingTimeStats::new(), + window_start_nanos: AtomicU64::new(0), + processing_stats: AtomicProcessingTimeStats::new_const(), } } @@ -105,97 +104,27 @@ impl AtomicEventMetrics { /// High-performance atomic processing time statistics #[derive(Debug)] struct AtomicProcessingTimeStats { - min_time_bits: AtomicU64, - max_time_bits: AtomicU64, - min_time_timestamp_nanos: AtomicU64, // Timestamp of min value update (nanoseconds) - max_time_timestamp_nanos: AtomicU64, // Timestamp of max value update (nanoseconds) - total_time_us: AtomicU64, // Store integer part of microseconds + last_time_bits: AtomicU64, // Last processing time (f64 as u64 bits) + total_time_us: AtomicU64, // Store integer part of microseconds total_events: AtomicU64, } impl AtomicProcessingTimeStats { - fn new() -> Self { - let now_nanos = - std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_nanos() - as u64; - + const fn new_const() -> Self { Self { - min_time_bits: AtomicU64::new(f64::INFINITY.to_bits()), - max_time_bits: AtomicU64::new(0), - min_time_timestamp_nanos: AtomicU64::new(now_nanos), - max_time_timestamp_nanos: AtomicU64::new(now_nanos), + last_time_bits: AtomicU64::new(0), total_time_us: AtomicU64::new(0), total_events: AtomicU64::new(0), } } - /// Atomically update processing time statistics + /// Atomically update processing time statistics (hot path - no syscalls) #[inline] fn update(&self, time_us: f64, event_count: u64) { let time_bits = time_us.to_bits(); - let now_nanos = - std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_nanos() - as u64; - // Update minimum value, check time difference and reset if over 10 seconds - let mut current_min = self.min_time_bits.load(Ordering::Relaxed); - let min_timestamp = self.min_time_timestamp_nanos.load(Ordering::Relaxed); - - // Check if min value timestamp exceeds 10 seconds (10_000_000_000 nanoseconds) - let min_time_diff_nanos = now_nanos.saturating_sub(min_timestamp); - if min_time_diff_nanos > 10_000_000_000 { - // Over 10 seconds, reset min value - self.min_time_bits.store(f64::INFINITY.to_bits(), Ordering::Relaxed); - self.min_time_timestamp_nanos.store(now_nanos, Ordering::Relaxed); - current_min = f64::INFINITY.to_bits(); - } - - // If current time is less than min value, update min value and timestamp - while time_bits < current_min { - match self.min_time_bits.compare_exchange_weak( - current_min, - time_bits, - Ordering::Relaxed, - Ordering::Relaxed, - ) { - Ok(_) => { - // Successfully updated min value, also update timestamp - self.min_time_timestamp_nanos.store(now_nanos, Ordering::Relaxed); - break; - } - Err(x) => current_min = x, - } - } - - // Update maximum value, check time difference and reset if over 10 seconds - let mut current_max = self.max_time_bits.load(Ordering::Relaxed); - let max_timestamp = self.max_time_timestamp_nanos.load(Ordering::Relaxed); - - // Check if max value timestamp exceeds 10 seconds (10_000_000_000 nanoseconds) - let time_diff_nanos = now_nanos.saturating_sub(max_timestamp); - if time_diff_nanos > 10_000_000_000 { - // Over 10 seconds, reset max value - self.max_time_bits.store(0, Ordering::Relaxed); - self.max_time_timestamp_nanos.store(now_nanos, Ordering::Relaxed); - current_max = 0; - } - - // If current time is greater than max value, update max value and timestamp - while time_bits > current_max { - match self.max_time_bits.compare_exchange_weak( - current_max, - time_bits, - Ordering::Relaxed, - Ordering::Relaxed, - ) { - Ok(_) => { - // Successfully updated max value, also update timestamp - self.max_time_timestamp_nanos.store(now_nanos, Ordering::Relaxed); - break; - } - Err(x) => current_max = x, - } - } + // Update last processing time (simple store, no compare-exchange needed) + self.last_time_bits.store(time_bits, Ordering::Relaxed); // Update cumulative values (convert microseconds to integers to avoid floating point accumulation issues) let total_time_us_int = (time_us * event_count as f64) as u64; @@ -206,30 +135,23 @@ impl AtomicProcessingTimeStats { /// Get statistics (non-blocking) #[inline] fn get_stats(&self) -> ProcessingTimeStats { - let min_bits = self.min_time_bits.load(Ordering::Relaxed); - let max_bits = self.max_time_bits.load(Ordering::Relaxed); + let last_bits = self.last_time_bits.load(Ordering::Relaxed); let total_time_us_int = self.total_time_us.load(Ordering::Relaxed); let total_events = self.total_events.load(Ordering::Relaxed); - let min_time = f64::from_bits(min_bits); - let max_time = f64::from_bits(max_bits); + let last_time = f64::from_bits(last_bits); let avg_time = if total_events > 0 { total_time_us_int as f64 / total_events as f64 } else { 0.0 }; - ProcessingTimeStats { - min_us: if min_time == f64::INFINITY { 0.0 } else { min_time }, - max_us: max_time, - avg_us: avg_time, - } + ProcessingTimeStats { last_us: last_time, avg_us: avg_time } } } /// Processing time statistics result #[derive(Debug, Clone)] pub struct ProcessingTimeStats { - pub min_us: f64, - pub max_us: f64, - pub avg_us: f64, + pub last_us: f64, // Last processing time in microseconds + pub avg_us: f64, // Average processing time in microseconds } /// Event metrics snapshot @@ -254,7 +176,7 @@ pub struct PerformanceMetrics { impl PerformanceMetrics { /// Create default performance metrics (compatibility method) pub fn new() -> Self { - let default_stats = ProcessingTimeStats { min_us: 0.0, max_us: 0.0, avg_us: 0.0 }; + let default_stats = ProcessingTimeStats { last_us: 0.0, avg_us: 0.0 }; let default_metrics = EventMetricsSnapshot { process_count: 0, events_processed: 0, @@ -275,7 +197,7 @@ impl PerformanceMetrics { /// High-performance metrics system #[derive(Debug)] pub struct HighPerformanceMetrics { - start_nanos: u64, + start_nanos: AtomicU64, event_metrics: [AtomicEventMetrics; 3], processing_stats: AtomicProcessingTimeStats, // 丢弃事件指标 @@ -283,20 +205,16 @@ pub struct HighPerformanceMetrics { } impl HighPerformanceMetrics { - fn new() -> Self { - let now_nanos = - std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_nanos() - as u64; - + /// Const constructor for static initialization (zero-cost) + const fn new_const() -> Self { Self { - start_nanos: now_nanos, + start_nanos: AtomicU64::new(0), // Will be lazily initialized on first access event_metrics: [ - AtomicEventMetrics::new(now_nanos), - AtomicEventMetrics::new(now_nanos), - AtomicEventMetrics::new(now_nanos), + AtomicEventMetrics::new_const(), + AtomicEventMetrics::new_const(), + AtomicEventMetrics::new_const(), ], - processing_stats: AtomicProcessingTimeStats::new(), - // 初始化丢弃事件指标 + processing_stats: AtomicProcessingTimeStats::new_const(), dropped_events_count: AtomicU64::new(0), } } @@ -307,7 +225,23 @@ impl HighPerformanceMetrics { let now_nanos = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_nanos() as u64; - (now_nanos - self.start_nanos) as f64 / 1_000_000_000.0 + + // Lazy initialization of start_nanos (compare-and-swap once) + let mut start = self.start_nanos.load(Ordering::Relaxed); + if start == 0 { + // Try to initialize with current time + match self.start_nanos.compare_exchange( + 0, + now_nanos, + Ordering::Relaxed, + Ordering::Relaxed, + ) { + Ok(_) => start = now_nanos, + Err(existing) => start = existing, + } + } + + (now_nanos - start) as f64 / 1_000_000_000.0 } /// 获取事件指标快照 @@ -348,122 +282,116 @@ impl HighPerformanceMetrics { } } -/// 高性能指标管理器 -pub struct MetricsManager { - metrics: Arc, - enable_metrics: bool, - stream_name: String, - background_task_running: AtomicBool, -} +/// Global singleton instance - zero-cost static allocation +static GLOBAL_METRICS: HighPerformanceMetrics = HighPerformanceMetrics::new_const(); + +/// Background task initialization flag +static BACKGROUND_TASK_STARTED: AtomicBool = AtomicBool::new(false); + +/// Metrics enabled flag +static METRICS_ENABLED: AtomicBool = AtomicBool::new(true); + +/// 高性能指标管理器 (Singleton) +#[derive(Clone, Copy)] +pub struct MetricsManager; impl MetricsManager { - /// 创建新的指标管理器 - pub fn new(enable_metrics: bool, stream_name: String) -> Self { - let manager = Self { - metrics: Arc::new(HighPerformanceMetrics::new()), - enable_metrics, - stream_name, - background_task_running: AtomicBool::new(false), - }; - - // 启动后台任务 - manager.start_background_tasks(); - manager + /// Get global singleton instance (zero-cost) + #[inline] + pub const fn global() -> Self { + Self } - /// 启动后台任务 - fn start_background_tasks(&self) { - if self - .background_task_running - .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed) - .is_ok() + /// Initialize and start background task (call once at startup) + pub fn init(enable_metrics: bool) { + METRICS_ENABLED.store(enable_metrics, Ordering::Relaxed); + + // Start background task only once + if enable_metrics + && BACKGROUND_TASK_STARTED + .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) + .is_ok() { - if !self.enable_metrics { - return; - } - - let metrics = self.metrics.clone(); - - tokio::spawn(async move { + tokio::spawn(async { let mut interval = tokio::time::interval(std::time::Duration::from_millis(500)); - loop { interval.tick().await; - let window_duration_nanos = DEFAULT_METRICS_WINDOW_SECONDS * 1_000_000_000; - // 更新所有事件类型的窗口指标 - metrics.update_window_metrics(EventType::Transaction, window_duration_nanos); - metrics.update_window_metrics(EventType::Account, window_duration_nanos); - metrics.update_window_metrics(EventType::BlockMeta, window_duration_nanos); + // Update window metrics for all event types + GLOBAL_METRICS + .update_window_metrics(EventType::Transaction, window_duration_nanos); + GLOBAL_METRICS.update_window_metrics(EventType::Account, window_duration_nanos); + GLOBAL_METRICS + .update_window_metrics(EventType::BlockMeta, window_duration_nanos); } }); } } + #[inline] + fn is_enabled(&self) -> bool { + METRICS_ENABLED.load(Ordering::Relaxed) + } + /// 记录处理次数(非阻塞) #[inline] pub fn record_process(&self, event_type: EventType) { - if self.enable_metrics { - self.metrics.event_metrics[event_type.as_index()].add_process_count(); + if self.is_enabled() { + GLOBAL_METRICS.event_metrics[event_type.as_index()].add_process_count(); } } /// 记录事件处理(非阻塞) #[inline] pub fn record_events(&self, event_type: EventType, count: u64, processing_time_us: f64) { - if !self.enable_metrics { + if !self.is_enabled() { return; } let index = event_type.as_index(); // 原子更新事件计数 - self.metrics.event_metrics[index].add_events_processed(count); + GLOBAL_METRICS.event_metrics[index].add_events_processed(count); // 原子更新该事件类型的处理时间统计 - self.metrics.event_metrics[index].update_processing_stats(processing_time_us, count); + GLOBAL_METRICS.event_metrics[index].update_processing_stats(processing_time_us, count); // 保持全局处理时间统计的兼容性 - self.metrics.processing_stats.update(processing_time_us, count); + GLOBAL_METRICS.processing_stats.update(processing_time_us, count); } /// 记录慢处理操作 #[inline] pub fn log_slow_processing(&self, processing_time_us: f64, event_count: usize) { if processing_time_us > SLOW_PROCESSING_THRESHOLD_US { - log::debug!( - "{} slow processing: {:.2}us for {} events", - self.stream_name, - processing_time_us, - event_count, - ); + log::debug!("Slow processing: {:.2}us for {} events", processing_time_us, event_count); } } /// 获取运行时长 pub fn get_uptime(&self) -> std::time::Duration { - std::time::Duration::from_secs_f64(self.metrics.get_uptime_seconds()) + std::time::Duration::from_secs_f64(GLOBAL_METRICS.get_uptime_seconds()) } /// 获取事件指标 pub fn get_event_metrics(&self, event_type: EventType) -> EventMetricsSnapshot { - self.metrics.get_event_metrics(event_type) + GLOBAL_METRICS.get_event_metrics(event_type) } /// 获取处理时间统计 pub fn get_processing_stats(&self) -> ProcessingTimeStats { - self.metrics.get_processing_stats() + GLOBAL_METRICS.get_processing_stats() } /// 获取丢弃事件计数 pub fn get_dropped_events_count(&self) -> u64 { - self.metrics.get_dropped_events_count() + GLOBAL_METRICS.get_dropped_events_count() } /// 打印性能指标(非阻塞) pub fn print_metrics(&self) { - println!("\n📊 {} Performance Metrics", self.stream_name); + println!("\n📊 Performance Metrics"); println!(" Run Time: {:?}", self.get_uptime()); // 打印丢弃事件指标 @@ -473,57 +401,44 @@ impl MetricsManager { } // 打印事件指标表格(包含处理时间统计) - println!("┌─────────────┬──────────────┬──────────────────┬─────────────┬─────────────┬─────────────┐"); - println!("│ Event Type │ Process Count│ Events Processed │ Avg Time(μs)│ Min 10s(μs) │ Max 10s(μs) │"); - println!("├─────────────┼──────────────┼──────────────────┼─────────────┼─────────────┼─────────────┤"); + println!("┌─────────────┬──────────────┬──────────────────┬─────────────┬─────────────┐"); + println!("│ Event Type │ Process Count│ Events Processed │ Last(μs) │ Avg(μs) │"); + println!("├─────────────┼──────────────┼──────────────────┼─────────────┼─────────────┤"); for event_type in [EventType::Transaction, EventType::Account, EventType::BlockMeta] { let metrics = self.get_event_metrics(event_type); println!( - "│ {:11} │ {:12} │ {:16} │ {:9.2} │ {:9.2} │ {:9.2} │", + "│ {:11} │ {:12} │ {:16} │ {:9.2} │ {:9.2} │", event_type.name(), metrics.process_count, metrics.events_processed, - metrics.processing_stats.avg_us, - metrics.processing_stats.min_us, - metrics.processing_stats.max_us + metrics.processing_stats.last_us, + metrics.processing_stats.avg_us ); } - println!("└─────────────┴──────────────┴──────────────────┴─────────────┴─────────────┴─────────────┘"); + println!("└─────────────┴──────────────┴──────────────────┴─────────────┴─────────────┘"); println!(); } /// 启动自动性能监控任务 pub async fn start_auto_monitoring(&self) -> Option> { - if !self.enable_metrics { + if !self.is_enabled() { return None; } - let manager = self.clone(); - let handle = tokio::spawn(async move { + let handle = tokio::spawn(async { let mut interval = tokio::time::interval(std::time::Duration::from_secs( DEFAULT_METRICS_PRINT_INTERVAL_SECONDS, )); loop { interval.tick().await; - manager.print_metrics(); + MetricsManager::global().print_metrics(); } }); Some(handle) } - // === 兼容性方法 === - - /// 兼容性构造函数 - pub fn new_with_metrics( - _metrics: Arc>, - enable_metrics: bool, - stream_name: String, - ) -> Self { - Self::new(enable_metrics, stream_name) - } - /// 获取完整的性能指标(兼容性方法) pub fn get_metrics(&self) -> PerformanceMetrics { PerformanceMetrics { @@ -532,7 +447,7 @@ impl MetricsManager { account_metrics: self.get_event_metrics(EventType::Account), block_meta_metrics: self.get_event_metrics(EventType::BlockMeta), processing_stats: self.get_processing_stats(), - dropped_events_count: self.metrics.get_dropped_events_count(), + dropped_events_count: self.get_dropped_events_count(), } } @@ -569,54 +484,38 @@ impl MetricsManager { /// 增加丢弃事件计数 #[inline] pub fn increment_dropped_events(&self) { - if !self.enable_metrics { + if !self.is_enabled() { return; } // 原子地增加丢弃事件计数 - let new_count = self.metrics.dropped_events_count.fetch_add(1, Ordering::Relaxed) + 1; + let new_count = GLOBAL_METRICS.dropped_events_count.fetch_add(1, Ordering::Relaxed) + 1; // 每丢弃1000个事件记录一次警告日志 if new_count % 1000 == 0 { - log::debug!("{} dropped events count reached: {}", self.stream_name, new_count); + log::debug!("Dropped events count reached: {}", new_count); } } /// 批量增加丢弃事件计数 #[inline] pub fn increment_dropped_events_by(&self, count: u64) { - if !self.enable_metrics || count == 0 { + if !self.is_enabled() || count == 0 { return; } // 原子地增加丢弃事件计数 let new_count = - self.metrics.dropped_events_count.fetch_add(count, Ordering::Relaxed) + count; + GLOBAL_METRICS.dropped_events_count.fetch_add(count, Ordering::Relaxed) + count; // 记录批量丢弃事件的日志 if count > 1 { - log::debug!( - "{} dropped batch of {} events, total dropped: {}", - self.stream_name, - count, - new_count - ); + log::debug!("Dropped batch of {} events, total dropped: {}", count, new_count); } // 每丢弃1000个事件记录一次警告日志 if new_count % 1000 == 0 || (new_count / 1000) != ((new_count - count) / 1000) { - log::debug!("{} dropped events count reached: {}", self.stream_name, new_count); - } - } -} - -impl Clone for MetricsManager { - fn clone(&self) -> Self { - Self { - metrics: self.metrics.clone(), - enable_metrics: self.enable_metrics, - stream_name: self.stream_name.clone(), - background_task_running: AtomicBool::new(false), // 新实例不自动启动后台任务 + log::debug!("Dropped events count reached: {}", new_count); } } } diff --git a/src/streaming/event_parser/common/filter.rs b/src/streaming/event_parser/common/filter.rs index 2ac0743..3c22fe5 100644 --- a/src/streaming/event_parser/common/filter.rs +++ b/src/streaming/event_parser/common/filter.rs @@ -2,7 +2,7 @@ use crate::streaming::event_parser::common::{ types::EventType, ACCOUNT_EVENT_TYPES, BLOCK_EVENT_TYPES, }; -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)] pub struct EventTypeFilter { pub include: Vec, } diff --git a/src/streaming/event_parser/common/mod.rs b/src/streaming/event_parser/common/mod.rs index 479d978..3794626 100755 --- a/src/streaming/event_parser/common/mod.rs +++ b/src/streaming/event_parser/common/mod.rs @@ -1,79 +1,6 @@ -pub mod types; -pub mod utils; pub mod filter; pub mod high_performance_clock; - -/// 自动生成UnifiedEvent trait实现的宏 -#[macro_export] -macro_rules! impl_unified_event { - // 带有自定义ID表达式的版本 - ($struct_name:ident, $($field:ident),*) => { - impl $crate::streaming::event_parser::core::traits::UnifiedEvent for $struct_name { - fn event_type(&self) -> $crate::streaming::event_parser::common::types::EventType { - self.metadata.event_type.clone() - } - - fn signature(&self) -> &solana_sdk::signature::Signature { - &self.metadata.signature - } - - fn slot(&self) -> u64 { - self.metadata.slot - } - - fn recv_us(&self) -> i64 { - self.metadata.recv_us - } - - fn handle_us(&self) -> i64 { - self.metadata.handle_us - } - - fn set_handle_us(&mut self, handle_us: i64) { - self.metadata.handle_us = handle_us; - } - - fn as_any(&self) -> &dyn std::any::Any { - self - } - - fn as_any_mut(&mut self) -> &mut dyn std::any::Any { - self - } - - fn clone_boxed(&self) -> Box { - Box::new(self.clone()) - } - - fn merge(&mut self, other: &dyn $crate::streaming::event_parser::core::traits::UnifiedEvent) { - if let Some(_e) = other.as_any().downcast_ref::<$struct_name>() { - $( - self.$field = _e.$field.clone(); - )* - } - } - - fn set_swap_data(&mut self, swap_data: $crate::streaming::event_parser::common::types::SwapData) { - self.metadata.set_swap_data(swap_data); - } - - fn swap_data_is_parsed(&self) -> bool { - self.metadata.swap_data.is_some() - } - - fn outer_index(&self) -> i64 { - self.metadata.outer_index - } - - fn inner_index(&self) -> Option { - self.metadata.inner_index - } - fn transaction_index(&self) -> Option { - self.metadata.transaction_index - } - } - }; -} - +pub mod types; +pub mod utils; pub use types::*; pub use utils::*; diff --git a/src/streaming/event_parser/common/types.rs b/src/streaming/event_parser/common/types.rs index 0aabf08..904da19 100755 --- a/src/streaming/event_parser/common/types.rs +++ b/src/streaming/event_parser/common/types.rs @@ -4,23 +4,7 @@ use serde::{Deserialize, Serialize}; use solana_sdk::{pubkey::Pubkey, signature::Signature}; use std::{borrow::Cow, fmt, str::FromStr, sync::Arc}; -use crate::{ - match_event, - streaming::{ - common::SimdUtils, - event_parser::{ - protocols::{ - bonk::BonkTradeEvent, - pumpfun::PumpFunTradeEvent, - pumpswap::{PumpSwapBuyEvent, PumpSwapSellEvent}, - raydium_amm_v4::RaydiumAmmV4SwapEvent, - raydium_clmm::{RaydiumClmmSwapEvent, RaydiumClmmSwapV2Event}, - raydium_cpmm::RaydiumCpmmSwapEvent, - }, - UnifiedEvent, - }, - }, -}; +use crate::streaming::{common::SimdUtils, event_parser::DexEvent}; // Object pool size configuration const EVENT_METADATA_POOL_SIZE: usize = 1000; @@ -72,7 +56,7 @@ pub enum ProtocolType { /// Event type enumeration #[derive( - Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, BorshSerialize, BorshDeserialize, + Debug, Clone, Default, PartialEq, Eq, Hash, Serialize, Deserialize, BorshSerialize, BorshDeserialize, )] pub enum EventType { // PumpSwap events @@ -364,7 +348,7 @@ lazy_static::lazy_static! { /// Parse token transfer data from next instructions pub fn parse_swap_data_from_next_instructions( - event: &dyn UnifiedEvent, + event: &DexEvent, inner_instruction: &solana_transaction_status::InnerInstructions, current_index: i8, accounts: &[Pubkey], @@ -378,7 +362,7 @@ pub fn parse_swap_data_from_next_instructions( }; // 先根据 event 取出关键信息 - let mut user: Option = None; + // let mut user: Option = None; let mut from_mint: Option = None; let mut to_mint: Option = None; let mut user_from_token: Option = None; @@ -386,63 +370,66 @@ pub fn parse_swap_data_from_next_instructions( let mut from_vault: Option = None; let mut to_vault: Option = None; - match_event!(&*event, { - BonkTradeEvent => |e: BonkTradeEvent| { - user = Some(e.payer); + match event { + DexEvent::BonkTradeEvent(e) => { + // user = Some(e.payer); from_mint = Some(e.base_token_mint); to_mint = Some(e.quote_token_mint); user_from_token = Some(e.user_base_token); user_to_token = Some(e.user_quote_token); from_vault = Some(e.base_vault); to_vault = Some(e.quote_vault); - }, - PumpFunTradeEvent => |e: PumpFunTradeEvent| { + } + DexEvent::PumpFunTradeEvent(e) => { swap_data.from_mint = if e.is_buy { *SOL_MINT } else { e.mint }; - swap_data.to_mint = if e.is_buy { e.mint } else { *SOL_MINT }; - }, - PumpSwapBuyEvent => |e: PumpSwapBuyEvent| { + swap_data.to_mint = if e.is_buy { e.mint } else { *SOL_MINT }; + } + DexEvent::PumpSwapBuyEvent(e) => { swap_data.from_mint = e.quote_mint; - swap_data.to_mint = e.base_mint; - }, - PumpSwapSellEvent => |e: PumpSwapSellEvent| { + swap_data.to_mint = e.base_mint; + } + DexEvent::PumpSwapSellEvent(e) => { swap_data.from_mint = e.base_mint; - swap_data.to_mint = e.quote_mint; - }, - RaydiumCpmmSwapEvent => |e: RaydiumCpmmSwapEvent| { - user = Some(e.payer); + swap_data.to_mint = e.quote_mint; + } + DexEvent::RaydiumCpmmSwapEvent(e) => { + // user = Some(e.payer); from_mint = Some(e.input_token_mint); - to_mint = Some(e.output_token_mint); + to_mint = Some(e.output_token_mint); user_from_token = Some(e.input_token_account); - user_to_token = Some(e.output_token_account); + user_to_token = Some(e.output_token_account); from_vault = Some(e.input_vault); - to_vault = Some(e.output_vault); - }, - RaydiumClmmSwapEvent => |e: RaydiumClmmSwapEvent| { - user = Some(e.payer); - swap_data.description = Some("Unable to get from_mint and to_mint from RaydiumClmmSwapEvent".into()); + to_vault = Some(e.output_vault); + } + DexEvent::RaydiumClmmSwapEvent(e) => { + // user = Some(e.payer); + swap_data.description = + Some("Unable to get from_mint and to_mint from RaydiumClmmSwapEvent".into()); user_from_token = Some(e.input_token_account); - user_to_token = Some(e.output_token_account); + user_to_token = Some(e.output_token_account); from_vault = Some(e.input_vault); - to_vault = Some(e.output_vault); - }, - RaydiumClmmSwapV2Event => |e: RaydiumClmmSwapV2Event| { - user = Some(e.payer); + to_vault = Some(e.output_vault); + } + DexEvent::RaydiumClmmSwapV2Event(e) => { + // user = Some(e.payer); from_mint = Some(e.input_vault_mint); - to_mint = Some(e.output_vault_mint); + to_mint = Some(e.output_vault_mint); user_from_token = Some(e.input_token_account); - user_to_token = Some(e.output_token_account); + user_to_token = Some(e.output_token_account); from_vault = Some(e.input_vault); - to_vault = Some(e.output_vault); - }, - RaydiumAmmV4SwapEvent => |e: RaydiumAmmV4SwapEvent| { - user = Some(e.user_source_owner); - swap_data.description = Some("Unable to get from_mint and to_mint from RaydiumAmmV4SwapEvent".into()); + to_vault = Some(e.output_vault); + } + DexEvent::RaydiumAmmV4SwapEvent(e) => { + // user = Some(e.user_source_owner); + swap_data.description = + Some("Unable to get from_mint and to_mint from RaydiumAmmV4SwapEvent".into()); user_from_token = Some(e.user_source_token_account); - user_to_token = Some(e.user_destination_token_account); + user_to_token = Some(e.user_destination_token_account); from_vault = Some(e.pool_pc_token_account); - to_vault = Some(e.pool_coin_token_account); - }, - }); + to_vault = Some(e.pool_coin_token_account); + } + _ => {} + } let user_to_token = user_to_token.unwrap_or_default(); let user_from_token = user_from_token.unwrap_or_default(); @@ -531,7 +518,7 @@ pub fn parse_swap_data_from_next_instructions( /// Parse token transfer data from next instructions /// TODO: - wait refactor pub fn parse_swap_data_from_next_grpc_instructions( - event: &dyn UnifiedEvent, + event: &DexEvent, inner_instruction: &yellowstone_grpc_proto::prelude::InnerInstructions, current_index: i8, accounts: &[Pubkey], @@ -545,7 +532,7 @@ pub fn parse_swap_data_from_next_grpc_instructions( }; // 先根据 event 取出关键信息 - let mut user: Option = None; + // let mut user: Option = None; let mut from_mint: Option = None; let mut to_mint: Option = None; let mut user_from_token: Option = None; @@ -553,63 +540,66 @@ pub fn parse_swap_data_from_next_grpc_instructions( let mut from_vault: Option = None; let mut to_vault: Option = None; - match_event!(&*event, { - BonkTradeEvent => |e: BonkTradeEvent| { - user = Some(e.payer); + match event { + DexEvent::BonkTradeEvent(e) => { + // user = Some(e.payer); from_mint = Some(e.base_token_mint); to_mint = Some(e.quote_token_mint); user_from_token = Some(e.user_base_token); user_to_token = Some(e.user_quote_token); from_vault = Some(e.base_vault); to_vault = Some(e.quote_vault); - }, - PumpFunTradeEvent => |e: PumpFunTradeEvent| { + } + DexEvent::PumpFunTradeEvent(e) => { swap_data.from_mint = if e.is_buy { *SOL_MINT } else { e.mint }; - swap_data.to_mint = if e.is_buy { e.mint } else { *SOL_MINT }; - }, - PumpSwapBuyEvent => |e: PumpSwapBuyEvent| { + swap_data.to_mint = if e.is_buy { e.mint } else { *SOL_MINT }; + } + DexEvent::PumpSwapBuyEvent(e) => { swap_data.from_mint = e.quote_mint; - swap_data.to_mint = e.base_mint; - }, - PumpSwapSellEvent => |e: PumpSwapSellEvent| { + swap_data.to_mint = e.base_mint; + } + DexEvent::PumpSwapSellEvent(e) => { swap_data.from_mint = e.base_mint; - swap_data.to_mint = e.quote_mint; - }, - RaydiumCpmmSwapEvent => |e: RaydiumCpmmSwapEvent| { - user = Some(e.payer); + swap_data.to_mint = e.quote_mint; + } + DexEvent::RaydiumCpmmSwapEvent(e) => { + // user = Some(e.payer); from_mint = Some(e.input_token_mint); - to_mint = Some(e.output_token_mint); + to_mint = Some(e.output_token_mint); user_from_token = Some(e.input_token_account); - user_to_token = Some(e.output_token_account); + user_to_token = Some(e.output_token_account); from_vault = Some(e.input_vault); - to_vault = Some(e.output_vault); - }, - RaydiumClmmSwapEvent => |e: RaydiumClmmSwapEvent| { - user = Some(e.payer); - swap_data.description = Some("Unable to get from_mint and to_mint from RaydiumClmmSwapEvent".into()); + to_vault = Some(e.output_vault); + } + DexEvent::RaydiumClmmSwapEvent(e) => { + // user = Some(e.payer); + swap_data.description = + Some("Unable to get from_mint and to_mint from RaydiumClmmSwapEvent".into()); user_from_token = Some(e.input_token_account); - user_to_token = Some(e.output_token_account); + user_to_token = Some(e.output_token_account); from_vault = Some(e.input_vault); - to_vault = Some(e.output_vault); - }, - RaydiumClmmSwapV2Event => |e: RaydiumClmmSwapV2Event| { - user = Some(e.payer); + to_vault = Some(e.output_vault); + } + DexEvent::RaydiumClmmSwapV2Event(e) => { + // user = Some(e.payer); from_mint = Some(e.input_vault_mint); - to_mint = Some(e.output_vault_mint); + to_mint = Some(e.output_vault_mint); user_from_token = Some(e.input_token_account); - user_to_token = Some(e.output_token_account); + user_to_token = Some(e.output_token_account); from_vault = Some(e.input_vault); - to_vault = Some(e.output_vault); - }, - RaydiumAmmV4SwapEvent => |e: RaydiumAmmV4SwapEvent| { - user = Some(e.user_source_owner); - swap_data.description = Some("Unable to get from_mint and to_mint from RaydiumAmmV4SwapEvent".into()); + to_vault = Some(e.output_vault); + } + DexEvent::RaydiumAmmV4SwapEvent(e) => { + // user = Some(e.user_source_owner); + swap_data.description = + Some("Unable to get from_mint and to_mint from RaydiumAmmV4SwapEvent".into()); user_from_token = Some(e.user_source_token_account); - user_to_token = Some(e.user_destination_token_account); + user_to_token = Some(e.user_destination_token_account); from_vault = Some(e.pool_pc_token_account); - to_vault = Some(e.pool_coin_token_account); - }, - }); + to_vault = Some(e.pool_coin_token_account); + } + _ => {} + } let user_to_token = user_to_token.unwrap_or_default(); let user_from_token = user_from_token.unwrap_or_default(); diff --git a/src/streaming/event_parser/core/account_event_parser.rs b/src/streaming/event_parser/core/account_event_parser.rs index 8a9090e..22e3d20 100644 --- a/src/streaming/event_parser/core/account_event_parser.rs +++ b/src/streaming/event_parser/core/account_event_parser.rs @@ -1,15 +1,9 @@ -use crate::impl_unified_event; use crate::streaming::common::SimdUtils; use crate::streaming::event_parser::common::filter::EventTypeFilter; use crate::streaming::event_parser::common::high_performance_clock::elapsed_micros_since; -use crate::streaming::event_parser::common::{EventMetadata, EventType, ProtocolType}; -use crate::streaming::event_parser::core::traits::UnifiedEvent; -use crate::streaming::event_parser::protocols::bonk::parser::BONK_PROGRAM_ID; -use crate::streaming::event_parser::protocols::pumpfun::parser::PUMPFUN_PROGRAM_ID; -use crate::streaming::event_parser::protocols::pumpswap::parser::PUMPSWAP_PROGRAM_ID; -use crate::streaming::event_parser::protocols::raydium_amm_v4::parser::RAYDIUM_AMM_V4_PROGRAM_ID; -use crate::streaming::event_parser::protocols::raydium_clmm::parser::RAYDIUM_CLMM_PROGRAM_ID; -use crate::streaming::event_parser::protocols::raydium_cpmm::parser::RAYDIUM_CPMM_PROGRAM_ID; +use crate::streaming::event_parser::common::EventMetadata; +use crate::streaming::event_parser::core::parser_cache::get_account_configs; +use crate::streaming::event_parser::core::traits::DexEvent; use crate::streaming::event_parser::Protocol; use crate::streaming::grpc::AccountPretty; use serde::{Deserialize, Serialize}; @@ -21,18 +15,6 @@ use spl_token_2022::{ extension::StateWithExtensions, state::{Account as Account2022, Mint as Mint2022}, }; -use std::collections::HashMap; -use std::sync::OnceLock; - -/// 通用事件解析器配置 -#[derive(Debug, Clone)] -pub struct AccountEventParseConfig { - pub program_id: Pubkey, - pub protocol_type: ProtocolType, - pub event_type: EventType, - pub account_discriminator: &'static [u8], - pub account_parser: AccountEventParserFn, -} /// 通用账户事件 #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] @@ -46,7 +28,6 @@ pub struct TokenAccountEvent { pub amount: Option, pub token_owner: Pubkey, } -impl_unified_event!(TokenAccountEvent,); /// Nonce account event #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] @@ -60,7 +41,6 @@ pub struct NonceAccountEvent { pub nonce: String, pub authority: String, } -impl_unified_event!(NonceAccountEvent,); /// Nonce account event #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] @@ -74,190 +54,22 @@ pub struct TokenInfoEvent { pub supply: u64, pub decimals: u8, } -impl_unified_event!(TokenInfoEvent,); - -/// 账户事件解析器 -pub type AccountEventParserFn = - fn(account: &AccountPretty, metadata: EventMetadata) -> Option>; - -static PROTOCOL_CONFIGS_CACHE: OnceLock>> = - OnceLock::new(); - -// 通用账户解析配置的静态缓存 -static COMMON_CONFIG: OnceLock = OnceLock::new(); -// Nonce account config -static NONCE_CONFIG: OnceLock = OnceLock::new(); pub struct AccountEventParser {} impl AccountEventParser { - pub fn configs( - protocols: &[Protocol], - event_type_filter: Option<&EventTypeFilter>, - ) -> Vec { - let protocols_map = PROTOCOL_CONFIGS_CACHE.get_or_init(|| { - let mut map: HashMap> = HashMap::new(); - map.insert(Protocol::PumpSwap, vec![ - AccountEventParseConfig { - program_id: PUMPSWAP_PROGRAM_ID, - protocol_type: ProtocolType::PumpSwap, - event_type: EventType::AccountPumpSwapGlobalConfig, - account_discriminator: crate::streaming::event_parser::protocols::pumpswap::discriminators::GLOBAL_CONFIG_ACCOUNT, - account_parser: crate::streaming::event_parser::protocols::pumpswap::types::global_config_parser, - }, - AccountEventParseConfig { - program_id: PUMPSWAP_PROGRAM_ID, - protocol_type: ProtocolType::PumpSwap, - event_type: EventType::AccountPumpSwapPool, - account_discriminator: crate::streaming::event_parser::protocols::pumpswap::discriminators::POOL_ACCOUNT, - account_parser: crate::streaming::event_parser::protocols::pumpswap::types::pool_parser, - }, - ]); - map.insert(Protocol::PumpFun, vec![ - AccountEventParseConfig { - program_id: PUMPFUN_PROGRAM_ID, - protocol_type: ProtocolType::PumpFun, - event_type: EventType::AccountPumpFunBondingCurve, - account_discriminator: crate::streaming::event_parser::protocols::pumpfun::discriminators::BONDING_CURVE_ACCOUNT, - account_parser: crate::streaming::event_parser::protocols::pumpfun::types::bonding_curve_parser, - }, - AccountEventParseConfig { - program_id: PUMPFUN_PROGRAM_ID, - protocol_type: ProtocolType::PumpFun, - event_type: EventType::AccountPumpFunGlobal, - account_discriminator: crate::streaming::event_parser::protocols::pumpfun::discriminators::GLOBAL_ACCOUNT, - account_parser: crate::streaming::event_parser::protocols::pumpfun::types::global_parser, - }, - ]); - map.insert(Protocol::Bonk, vec![ - AccountEventParseConfig { - program_id: BONK_PROGRAM_ID, - protocol_type: ProtocolType::Bonk, - event_type: EventType::AccountBonkPoolState, - account_discriminator: crate::streaming::event_parser::protocols::bonk::discriminators::POOL_STATE_ACCOUNT, - account_parser: crate::streaming::event_parser::protocols::bonk::types::pool_state_parser, - }, - AccountEventParseConfig { - program_id: BONK_PROGRAM_ID, - protocol_type: ProtocolType::Bonk, - event_type: EventType::AccountBonkGlobalConfig, - account_discriminator: crate::streaming::event_parser::protocols::bonk::discriminators::GLOBAL_CONFIG_ACCOUNT, - account_parser: crate::streaming::event_parser::protocols::bonk::types::global_config_parser, - }, - AccountEventParseConfig { - program_id: BONK_PROGRAM_ID, - protocol_type: ProtocolType::Bonk, - event_type: EventType::AccountBonkPlatformConfig, - account_discriminator: crate::streaming::event_parser::protocols::bonk::discriminators::PLATFORM_CONFIG_ACCOUNT, - account_parser: crate::streaming::event_parser::protocols::bonk::types::platform_config_parser, - }, - ]); - map.insert(Protocol::RaydiumCpmm, vec![ - AccountEventParseConfig { - program_id: RAYDIUM_CPMM_PROGRAM_ID, - protocol_type: ProtocolType::RaydiumCpmm, - event_type: EventType::AccountRaydiumCpmmAmmConfig, - account_discriminator: crate::streaming::event_parser::protocols::raydium_cpmm::discriminators::AMM_CONFIG, - account_parser: crate::streaming::event_parser::protocols::raydium_cpmm::types::amm_config_parser, - }, - AccountEventParseConfig { - program_id: RAYDIUM_CPMM_PROGRAM_ID, - protocol_type: ProtocolType::RaydiumCpmm, - event_type: EventType::AccountRaydiumCpmmPoolState, - account_discriminator: crate::streaming::event_parser::protocols::raydium_cpmm::discriminators::POOL_STATE, - account_parser: crate::streaming::event_parser::protocols::raydium_cpmm::types::pool_state_parser, - }, - ]); - map.insert(Protocol::RaydiumClmm, vec![ - AccountEventParseConfig { - program_id: RAYDIUM_CLMM_PROGRAM_ID, - protocol_type: ProtocolType::RaydiumClmm, - event_type: EventType::AccountRaydiumClmmAmmConfig, - account_discriminator: crate::streaming::event_parser::protocols::raydium_clmm::discriminators::AMM_CONFIG, - account_parser: crate::streaming::event_parser::protocols::raydium_clmm::types::amm_config_parser, - }, - AccountEventParseConfig { - program_id: RAYDIUM_CLMM_PROGRAM_ID, - protocol_type: ProtocolType::RaydiumClmm, - event_type: EventType::AccountRaydiumClmmPoolState, - account_discriminator: crate::streaming::event_parser::protocols::raydium_clmm::discriminators::POOL_STATE, - account_parser: crate::streaming::event_parser::protocols::raydium_clmm::types::pool_state_parser, - }, - AccountEventParseConfig { - program_id: RAYDIUM_CLMM_PROGRAM_ID, - protocol_type: ProtocolType::RaydiumClmm, - event_type: EventType::AccountRaydiumClmmTickArrayState, - account_discriminator: crate::streaming::event_parser::protocols::raydium_clmm::discriminators::TICK_ARRAY_STATE, - account_parser: crate::streaming::event_parser::protocols::raydium_clmm::types::tick_array_state_parser, - }, - ]); - map.insert(Protocol::RaydiumAmmV4, vec![ - AccountEventParseConfig { - program_id: RAYDIUM_AMM_V4_PROGRAM_ID, - protocol_type: ProtocolType::RaydiumAmmV4, - event_type: EventType::AccountRaydiumAmmV4AmmInfo, - account_discriminator: crate::streaming::event_parser::protocols::raydium_amm_v4::discriminators::AMM_INFO, - account_parser: crate::streaming::event_parser::protocols::raydium_amm_v4::types::amm_info_parser, - }, - ]); - map - }); - - let mut configs = Vec::new(); - let empty_vec = Vec::new(); - - // 预估容量以减少重新分配 - let estimated_capacity = protocols.len() * 3; // 大多数协议有2-3个配置 - configs.reserve(estimated_capacity); - - for protocol in protocols { - let protocol_configs = protocols_map.get(protocol).unwrap_or(&empty_vec); - // 如果没有过滤器,直接扩展所有配置 - if event_type_filter.is_none() { - configs.extend(protocol_configs.iter().cloned()); - } else { - // 有过滤器时才进行过滤 - let filter = event_type_filter.unwrap(); - configs.extend( - protocol_configs - .iter() - .filter(|config| filter.include.contains(&config.event_type)) - .cloned(), - ); - } - } - - if event_type_filter.is_none() - || event_type_filter.unwrap().include.contains(&EventType::NonceAccount) - { - let nonce_config = NONCE_CONFIG.get_or_init(|| AccountEventParseConfig { - program_id: Pubkey::default(), - protocol_type: ProtocolType::Common, - event_type: EventType::NonceAccount, - account_discriminator: &[1, 0, 0, 0, 1, 0, 0, 0], - account_parser: Self::parse_nonce_account_event, - }); - configs.push(nonce_config.clone()); - } - - let common_config = COMMON_CONFIG.get_or_init(|| AccountEventParseConfig { - program_id: Pubkey::default(), - protocol_type: ProtocolType::Common, - event_type: EventType::TokenAccount, - account_discriminator: &[], - account_parser: Self::parse_token_account_event, - }); - configs.push(common_config.clone()); - - configs - } - pub fn parse_account_event( protocols: &[Protocol], account: AccountPretty, event_type_filter: Option<&EventTypeFilter>, - ) -> Option> { - let configs = Self::configs(protocols, event_type_filter); + ) -> Option { + // 直接从 parser_cache 获取配置 + let configs = get_account_configs( + protocols, + event_type_filter, + Self::parse_nonce_account_event, + Self::parse_token_account_event, + ); for config in configs { if config.program_id == Pubkey::default() || (account.owner == config.program_id @@ -275,12 +87,12 @@ impl AccountEventParser { event_type: config.event_type, program_id: config.program_id, recv_us: account.recv_us, + handle_us: elapsed_micros_since(account.recv_us), ..Default::default() }, ); - if let Some(mut event) = event { - event.set_handle_us(elapsed_micros_since(account.recv_us)); - return Some(event); + if event.is_some() { + return event; } } } @@ -290,7 +102,7 @@ impl AccountEventParser { pub fn parse_token_account_event( account: &AccountPretty, metadata: EventMetadata, - ) -> Option> { + ) -> Option { let pubkey = account.pubkey; let executable = account.executable; let lamports = account.lamports; @@ -310,8 +122,8 @@ impl AccountEventParser { decimals: mint.decimals, }; let recv_delta = elapsed_micros_since(account.recv_us); - event.set_handle_us(recv_delta); - return Some(Box::new(event)); + event.metadata.handle_us = recv_delta; + return Some(DexEvent::TokenInfoEvent(event)); } } // Spl Token2022 Mint @@ -328,8 +140,8 @@ impl AccountEventParser { decimals: mint.base.decimals, }; let recv_delta = elapsed_micros_since(account.recv_us); - event.set_handle_us(recv_delta); - return Some(Box::new(event)); + event.metadata.handle_us = recv_delta; + return Some(DexEvent::TokenInfoEvent(event)); } } let amount = if account.owner.to_bytes() == spl_token_2022::ID.to_bytes() { @@ -351,14 +163,14 @@ impl AccountEventParser { token_owner: account.owner, }; let recv_delta = elapsed_micros_since(account.recv_us); - event.set_handle_us(recv_delta); - Some(Box::new(event)) + event.metadata.handle_us = recv_delta; + Some(DexEvent::TokenAccountEvent(event)) } pub fn parse_nonce_account_event( account: &AccountPretty, metadata: EventMetadata, - ) -> Option> { + ) -> Option { if let Ok(info) = parse_nonce(&account.data) { match info { solana_account_decoder::parse_nonce::UiNonceState::Initialized(details) => { @@ -372,8 +184,8 @@ impl AccountEventParser { nonce: details.blockhash, authority: details.authority, }; - event.set_handle_us(elapsed_micros_since(account.recv_us)); - return Some(Box::new(event)); + event.metadata.handle_us = elapsed_micros_since(account.recv_us); + return Some(DexEvent::NonceAccountEvent(event)); } solana_account_decoder::parse_nonce::UiNonceState::Uninitialized => {} } diff --git a/src/streaming/event_parser/core/common_event_parser.rs b/src/streaming/event_parser/core/common_event_parser.rs index 633a1b8..6f34fd3 100644 --- a/src/streaming/event_parser/core/common_event_parser.rs +++ b/src/streaming/event_parser/core/common_event_parser.rs @@ -1,5 +1,5 @@ use crate::streaming::event_parser::common::high_performance_clock::elapsed_micros_since; -use crate::streaming::event_parser::core::traits::UnifiedEvent; +use crate::streaming::event_parser::core::traits::DexEvent; use crate::streaming::event_parser::protocols::block::block_meta_event::BlockMetaEvent; pub struct CommonEventParser {} @@ -10,9 +10,9 @@ impl CommonEventParser { block_hash: String, block_time_ms: i64, recv_us: i64, - ) -> Box { + ) -> DexEvent { let mut block_meta_event = BlockMetaEvent::new(slot, block_hash, block_time_ms, recv_us); - block_meta_event.set_handle_us(elapsed_micros_since(recv_us)); - Box::new(block_meta_event) + block_meta_event.metadata.handle_us = elapsed_micros_since(recv_us); + DexEvent::BlockMetaEvent(block_meta_event) } } diff --git a/src/streaming/event_parser/core/event_parser.rs b/src/streaming/event_parser/core/event_parser.rs index de133f1..e155885 100644 --- a/src/streaming/event_parser/core/event_parser.rs +++ b/src/streaming/event_parser/core/event_parser.rs @@ -5,192 +5,40 @@ use crate::streaming::{ filter::EventTypeFilter, high_performance_clock::{elapsed_micros_since, get_high_perf_clock}, parse_swap_data_from_next_grpc_instructions, parse_swap_data_from_next_instructions, - EventMetadata, EventType, ProtocolType, + EventMetadata, }, - core::global_state::{ - add_bonk_dev_address, add_dev_address, is_bonk_dev_address_in_signature, - is_dev_address_in_signature, + core::{ + global_state::{ + add_bonk_dev_address, add_dev_address, is_bonk_dev_address_in_signature, + is_dev_address_in_signature, + }, + merger_event::merge, + parser_cache::{ + build_account_pubkeys_with_cache, get_global_instruction_configs, + get_global_program_ids, GenericEventParseConfig, + }, }, - protocols::{ - bonk::{parser::BONK_PROGRAM_ID, BonkPoolCreateEvent, BonkTradeEvent}, - pumpfun::{parser::PUMPFUN_PROGRAM_ID, PumpFunCreateTokenEvent, PumpFunTradeEvent}, - pumpswap::{parser::PUMPSWAP_PROGRAM_ID, PumpSwapBuyEvent, PumpSwapSellEvent}, - raydium_amm_v4::parser::RAYDIUM_AMM_V4_PROGRAM_ID, - raydium_clmm::parser::RAYDIUM_CLMM_PROGRAM_ID, - raydium_cpmm::parser::RAYDIUM_CPMM_PROGRAM_ID, - }, - Protocol, UnifiedEvent, + Protocol, DexEvent, }, }; use prost_types::Timestamp; -use solana_sdk::{bs58, message::compiled_instruction::CompiledInstruction, pubkey::Pubkey, signature::Signature, transaction::VersionedTransaction}; +use solana_sdk::{ + bs58, message::compiled_instruction::CompiledInstruction, pubkey::Pubkey, signature::Signature, + transaction::VersionedTransaction, +}; use solana_transaction_status::{ EncodedConfirmedTransactionWithStatusMeta, InnerInstruction, InnerInstructions, UiInstruction, }; -use std::{ - collections::HashMap, - sync::{Arc, LazyLock}, -}; +use std::sync::Arc; use yellowstone_grpc_proto::geyser::SubscribeUpdateTransactionInfo; -/// 高性能账户公钥缓存,避免重复Vec分配 -#[derive(Debug)] -pub struct AccountPubkeyCache { - /// 预分配的账户公钥向量,避免每次重新分配 - cache: Vec, -} - -impl AccountPubkeyCache { - /// 创建新的账户公钥缓存 - pub fn new() -> Self { - Self { - cache: Vec::with_capacity(32), // 预分配32个位置,覆盖大多数交易 - } - } - - /// 从指令账户索引构建账户公钥向量,重用缓存内存 - #[inline] - pub fn build_account_pubkeys( - &mut self, - instruction_accounts: &[u8], - all_accounts: &[Pubkey], - ) -> &[Pubkey] { - self.cache.clear(); - - // 确保容量足够,避免动态扩容 - if self.cache.capacity() < instruction_accounts.len() { - self.cache.reserve(instruction_accounts.len() - self.cache.capacity()); - } - - // 快速填充账户公钥 - for &idx in instruction_accounts.iter() { - if (idx as usize) < all_accounts.len() { - self.cache.push(all_accounts[idx as usize]); - } - } - - &self.cache - } -} - -impl Default for AccountPubkeyCache { - fn default() -> Self { - Self::new() - } -} - -/// 内联指令事件解析器 -pub type InnerInstructionEventParser = - fn(data: &[u8], metadata: EventMetadata) -> Option>; - -/// 指令事件解析器 -pub type InstructionEventParser = - fn(data: &[u8], accounts: &[Pubkey], metadata: EventMetadata) -> Option>; - -/// 通用事件解析器配置 -#[derive(Debug, Clone)] -pub struct GenericEventParseConfig { - pub program_id: Pubkey, - pub protocol_type: ProtocolType, - pub inner_instruction_discriminator: &'static [u8], - pub instruction_discriminator: &'static [u8], - pub event_type: EventType, - pub inner_instruction_parser: Option, - pub instruction_parser: Option, - pub requires_inner_instruction: bool, -} - -pub static EVENT_PARSERS: LazyLock> = - LazyLock::new(|| { - // 预分配容量,避免动态扩容 - let mut parsers: HashMap = - HashMap::with_capacity(6); - parsers.insert( - Protocol::PumpSwap, - ( - PUMPSWAP_PROGRAM_ID, - crate::streaming::event_parser::protocols::pumpswap::parser::CONFIGS, - ), - ); - parsers.insert( - Protocol::PumpFun, - ( - PUMPFUN_PROGRAM_ID, - crate::streaming::event_parser::protocols::pumpfun::parser::CONFIGS, - ), - ); - parsers.insert( - Protocol::Bonk, - (BONK_PROGRAM_ID, crate::streaming::event_parser::protocols::bonk::parser::CONFIGS), - ); - parsers.insert( - Protocol::RaydiumCpmm, - ( - RAYDIUM_CPMM_PROGRAM_ID, - crate::streaming::event_parser::protocols::raydium_cpmm::parser::CONFIGS, - ), - ); - parsers.insert( - Protocol::RaydiumClmm, - ( - RAYDIUM_CLMM_PROGRAM_ID, - crate::streaming::event_parser::protocols::raydium_clmm::parser::CONFIGS, - ), - ); - parsers.insert( - Protocol::RaydiumAmmV4, - ( - RAYDIUM_AMM_V4_PROGRAM_ID, - crate::streaming::event_parser::protocols::raydium_amm_v4::parser::CONFIGS, - ), - ); - parsers - }); - -/// 通用事件解析器基类 -pub struct EventParser { - pub program_ids: Vec, - // pub inner_instruction_configs: HashMap, Vec>, - pub instruction_configs: HashMap, Vec>, - /// 账户公钥缓存,避免重复分配 - pub account_cache: parking_lot::Mutex, -} +pub struct EventParser {} impl EventParser { - pub fn new(protocols: Vec, event_type_filter: Option) -> Self { - let mut instruction_configs = HashMap::with_capacity(protocols.len()); - let mut program_ids = Vec::with_capacity(protocols.len()); - // Configure all event types - for protocol in protocols { - let parse = EVENT_PARSERS.get(&protocol).unwrap(); - // Merge instruction_configs, append configurations to existing Vec - parse - .1 - .iter() - .filter(|config| { - event_type_filter - .as_ref() - .map(|filter| filter.include.contains(&config.event_type)) - .unwrap_or(true) - }) - .for_each(|config| { - instruction_configs - .entry(config.instruction_discriminator.to_vec()) - .or_insert_with(Vec::new) - .push(config.clone()); - }); - - // Append program_ids (this is already appending) - program_ids.push(parse.0); - } - let account_cache = parking_lot::Mutex::new(AccountPubkeyCache::new()); - - Self { program_ids, instruction_configs, account_cache } - } - #[allow(clippy::too_many_arguments)] async fn parse_instruction_events_from_grpc_transaction( - &self, + protocols: &[Protocol], + event_type_filter: Option<&EventTypeFilter>, compiled_instructions: &[yellowstone_grpc_proto::prelude::CompiledInstruction], signature: Signature, slot: Option, @@ -200,12 +48,14 @@ impl EventParser { inner_instructions: &[yellowstone_grpc_proto::prelude::InnerInstructions], bot_wallet: Option, transaction_index: Option, - callback: Arc Fn(&'a Box) + Send + Sync>, + callback: Arc Fn(&'a DexEvent) + Send + Sync>, ) -> anyhow::Result<()> { // 获取交易的指令和账户 let mut accounts = accounts.to_vec(); // 检查交易中是否包含程序 - let has_program = accounts.iter().any(|account| self.should_handle(account)); + let has_program = accounts + .iter() + .any(|account| Self::should_handle(protocols, event_type_filter, account)); if has_program { // 解析每个指令 for (index, instruction) in compiled_instructions.iter().enumerate() { @@ -219,8 +69,10 @@ impl EventParser { if *max_idx as usize >= accounts.len() { accounts.resize(*max_idx as usize + 1, Pubkey::default()); } - if self.should_handle(&program_id) { - self.parse_events_from_grpc_instruction( + if Self::should_handle(protocols, event_type_filter, &program_id) { + Self::parse_events_from_grpc_instruction( + protocols, + event_type_filter, instruction, &accounts, signature, @@ -248,7 +100,9 @@ impl EventParser { accounts: inner_accounts.to_vec(), data: data.to_vec(), }; - self.parse_events_from_grpc_instruction( + Self::parse_events_from_grpc_instruction( + protocols, + event_type_filter, &instruction, &accounts, signature, @@ -273,7 +127,8 @@ impl EventParser { /// 从VersionedTransaction中解析指令事件的通用方法 #[allow(clippy::too_many_arguments)] async fn parse_instruction_events_from_versioned_transaction( - &self, + protocols: &[Protocol], + event_type_filter: Option<&EventTypeFilter>, transaction: &VersionedTransaction, signature: Signature, slot: Option, @@ -283,13 +138,15 @@ impl EventParser { inner_instructions: &[InnerInstructions], bot_wallet: Option, transaction_index: Option, - callback: Arc Fn(&'a Box) + Send + Sync>, + callback: Arc Fn(&'a DexEvent) + Send + Sync>, ) -> anyhow::Result<()> { // 获取交易的指令和账户 let compiled_instructions = transaction.message.instructions(); let mut accounts: Vec = accounts.to_vec(); // 检查交易中是否包含程序 - let has_program = accounts.iter().any(|account| self.should_handle(account)); + let has_program = accounts + .iter() + .any(|account| Self::should_handle(protocols, event_type_filter, account)); if has_program { // 解析每个指令 for (index, instruction) in compiled_instructions.iter().enumerate() { @@ -298,13 +155,15 @@ impl EventParser { let inner_instructions = inner_instructions .iter() .find(|inner_instruction| inner_instruction.index == index as u8); - if self.should_handle(&program_id) { + if Self::should_handle(protocols, event_type_filter, &program_id) { let max_idx = instruction.accounts.iter().max().unwrap_or(&0); // 补齐accounts(使用Pubkey::default()) if *max_idx as usize >= accounts.len() { accounts.resize(*max_idx as usize + 1, Pubkey::default()); } - self.parse_events_from_instruction( + Self::parse_events_from_instruction( + protocols, + event_type_filter, instruction, &accounts, signature, @@ -324,7 +183,9 @@ impl EventParser { for (inner_index, inner_instruction) in inner_instructions.instructions.iter().enumerate() { - self.parse_events_from_instruction( + Self::parse_events_from_instruction( + protocols, + event_type_filter, &inner_instruction.instruction, &accounts, signature, @@ -347,7 +208,8 @@ impl EventParser { } pub async fn parse_versioned_transaction_owned( - &self, + protocols: &[Protocol], + event_type_filter: Option<&EventTypeFilter>, versioned_tx: VersionedTransaction, signature: Signature, slot: Option, @@ -356,13 +218,15 @@ impl EventParser { bot_wallet: Option, transaction_index: Option, inner_instructions: &[InnerInstructions], - callback: Arc) + Send + Sync>, + callback: Arc, ) -> anyhow::Result<()> { // 创建适配器回调,将所有权回调转换为引用回调 - let adapter_callback = Arc::new(move |event: &Box| { - callback(event.clone_boxed()); + let adapter_callback = Arc::new(move |event: &DexEvent| { + callback(event.clone()); }); - self.parse_versioned_transaction( + Self::parse_versioned_transaction( + protocols, + event_type_filter, &versioned_tx, signature, slot, @@ -378,7 +242,8 @@ impl EventParser { } async fn parse_versioned_transaction( - &self, + protocols: &[Protocol], + event_type_filter: Option<&EventTypeFilter>, versioned_tx: &VersionedTransaction, signature: Signature, slot: Option, @@ -387,10 +252,12 @@ impl EventParser { bot_wallet: Option, transaction_index: Option, inner_instructions: &[InnerInstructions], - callback: Arc Fn(&'a Box) + Send + Sync>, + callback: Arc Fn(&'a DexEvent) + Send + Sync>, ) -> anyhow::Result<()> { let accounts: Vec = versioned_tx.message.static_account_keys().to_vec(); - self.parse_instruction_events_from_versioned_transaction( + Self::parse_instruction_events_from_versioned_transaction( + protocols, + event_type_filter, versioned_tx, signature, slot, @@ -407,7 +274,8 @@ impl EventParser { } pub async fn parse_grpc_transaction_owned( - &self, + protocols: &[Protocol], + event_type_filter: Option<&EventTypeFilter>, grpc_tx: SubscribeUpdateTransactionInfo, signature: Signature, slot: Option, @@ -415,14 +283,16 @@ impl EventParser { recv_us: i64, bot_wallet: Option, transaction_index: Option, - callback: Arc) + Send + Sync>, + callback: Arc, ) -> anyhow::Result<()> { // 创建适配器回调,将所有权回调转换为引用回调 - let adapter_callback = Arc::new(move |event: &Box| { - callback(event.clone_boxed()); + let adapter_callback = Arc::new(move |event: &DexEvent| { + callback(event.clone()); }); // 调用原始方法 - self.parse_grpc_transaction( + Self::parse_grpc_transaction( + protocols, + event_type_filter, grpc_tx, signature, slot, @@ -436,7 +306,8 @@ impl EventParser { } async fn parse_grpc_transaction( - &self, + protocols: &[Protocol], + event_type_filter: Option<&EventTypeFilter>, grpc_tx: SubscribeUpdateTransactionInfo, signature: Signature, slot: Option, @@ -444,7 +315,7 @@ impl EventParser { recv_us: i64, bot_wallet: Option, transaction_index: Option, - callback: Arc Fn(&'a Box) + Send + Sync>, + callback: Arc Fn(&'a DexEvent) + Send + Sync>, ) -> anyhow::Result<()> { if let Some(transition) = grpc_tx.transaction { if let Some(message) = &transition.message { @@ -456,7 +327,7 @@ impl EventParser { if let Some(meta) = grpc_tx.meta { inner_instructions = meta.inner_instructions; address_table_lookups.reserve( - meta.loaded_writable_addresses.len() + meta.loaded_writable_addresses.len(), + meta.loaded_writable_addresses.len() + meta.loaded_readonly_addresses.len(), ); let loaded_writable_addresses = meta.loaded_writable_addresses; let loaded_readonly_addresses = meta.loaded_readonly_addresses; @@ -485,7 +356,9 @@ impl EventParser { let inner_instructions_arc = Arc::new(inner_instructions); // 解析指令事件 let instructions = &message.instructions; - self.parse_instruction_events_from_grpc_transaction( + Self::parse_instruction_events_from_grpc_transaction( + protocols, + event_type_filter, &instructions, signature, slot, @@ -505,10 +378,11 @@ impl EventParser { } pub async fn parse_encoded_confirmed_transaction_with_status_meta( - &self, + protocols: &[Protocol], + event_type_filter: Option<&EventTypeFilter>, signature: Signature, transaction: EncodedConfirmedTransactionWithStatusMeta, - callback: Arc Fn(&'a Box) + Send + Sync>, + callback: Arc Fn(&'a DexEvent) + Send + Sync>, ) -> anyhow::Result<()> { let versioned_tx = match transaction.transaction.transaction.decode() { Some(tx) => tx, @@ -598,7 +472,9 @@ impl EventParser { let bot_wallet = None; let transaction_index = None; // 解析指令事件 - self.parse_instruction_events_from_versioned_transaction( + Self::parse_instruction_events_from_versioned_transaction( + protocols, + event_type_filter, &versioned_tx, signature, Some(slot), @@ -618,7 +494,6 @@ impl EventParser { /// 通用的内联指令解析方法 #[allow(clippy::too_many_arguments)] fn parse_inner_instruction_event( - &self, config: &GenericEventParseConfig, data: &[u8], signature: Signature, @@ -628,7 +503,7 @@ impl EventParser { outer_index: i64, inner_index: Option, transaction_index: Option, - ) -> Option> { + ) -> Option { if let Some(parser) = config.inner_instruction_parser { let timestamp = block_time.unwrap_or(Timestamp { seconds: 0, nanos: 0 }); let block_time_ms = timestamp.seconds * 1000 + (timestamp.nanos as i64) / 1_000_000; @@ -654,7 +529,6 @@ impl EventParser { /// 通用的指令解析方法 #[allow(clippy::too_many_arguments)] fn parse_instruction_event( - &self, config: &GenericEventParseConfig, data: &[u8], account_pubkeys: &[Pubkey], @@ -665,7 +539,7 @@ impl EventParser { outer_index: i64, inner_index: Option, transaction_index: Option, - ) -> Option> { + ) -> Option { if let Some(parser) = config.instruction_parser { let timestamp = block_time.unwrap_or(Timestamp { seconds: 0, nanos: 0 }); let block_time_ms = timestamp.seconds * 1000 + (timestamp.nanos as i64) / 1_000_000; @@ -691,7 +565,6 @@ impl EventParser { /// 从内联指令中解析事件数据 #[allow(clippy::too_many_arguments)] fn parse_events_from_inner_instruction( - &self, inner_instruction: &CompiledInstruction, signature: Signature, slot: u64, @@ -701,7 +574,7 @@ impl EventParser { inner_index: Option, transaction_index: Option, config: &GenericEventParseConfig, - ) -> Vec> { + ) -> Vec { // Use SIMD-optimized data validation with correct discriminator length let discriminator_len = config.inner_instruction_discriminator.len(); if !SimdUtils::validate_instruction_data_simd( @@ -722,7 +595,7 @@ impl EventParser { let data = &inner_instruction.data[16..]; let mut events = Vec::new(); - if let Some(event) = self.parse_inner_instruction_event( + if let Some(event) = Self::parse_inner_instruction_event( config, data, signature, @@ -741,7 +614,6 @@ impl EventParser { /// 从内联指令中解析事件数据 #[allow(clippy::too_many_arguments)] fn parse_events_from_grpc_inner_instruction( - &self, inner_instruction: &yellowstone_grpc_proto::prelude::InnerInstruction, signature: Signature, slot: u64, @@ -751,7 +623,7 @@ impl EventParser { inner_index: Option, transaction_index: Option, config: &GenericEventParseConfig, - ) -> Vec> { + ) -> Vec { // Use SIMD-optimized data validation with correct discriminator length let discriminator_len = config.inner_instruction_discriminator.len(); if !SimdUtils::validate_instruction_data_simd( @@ -772,7 +644,7 @@ impl EventParser { let data = &inner_instruction.data[16..]; let mut events = Vec::new(); - if let Some(event) = self.parse_inner_instruction_event( + if let Some(event) = Self::parse_inner_instruction_event( config, data, signature, @@ -791,7 +663,8 @@ impl EventParser { /// 从指令中解析事件 #[allow(clippy::too_many_arguments)] fn parse_events_from_instruction( - &self, + protocols: &[Protocol], + event_type_filter: Option<&EventTypeFilter>, instruction: &CompiledInstruction, accounts: &[Pubkey], signature: Signature, @@ -803,15 +676,20 @@ impl EventParser { bot_wallet: Option, transaction_index: Option, inner_instructions: Option<&InnerInstructions>, - callback: Arc Fn(&'a Box) + Send + Sync>, + callback: Arc Fn(&'a DexEvent) + Send + Sync>, ) -> anyhow::Result<()> { - let program_id = accounts[instruction.program_id_index as usize]; - if !self.should_handle(&program_id) { + // 添加边界检查以防止越界访问 + let program_id_index = instruction.program_id_index as usize; + if program_id_index >= accounts.len() { + return Ok(()); + } + let program_id = accounts[program_id_index]; + if !Self::should_handle(protocols, event_type_filter, &program_id) { return Ok(()); } // 一维化并行处理:将所有 (discriminator, config) 组合展开并行处理 - let all_processing_params: Vec<_> = self - .instruction_configs + let instruction_configs = get_global_instruction_configs(protocols, event_type_filter); + let all_processing_params: Vec<_> = instruction_configs .iter() .filter(|(disc, _)| { // Use SIMD-optimized data validation and discriminator matching @@ -831,18 +709,15 @@ impl EventParser { return Ok(()); } - // 使用缓存构建账户公钥列表,避免重复分配 (只需构建一次) - let account_pubkeys = { - let mut cache_guard = self.account_cache.lock(); - cache_guard.build_account_pubkeys(&instruction.accounts, accounts).to_vec() - }; + // 使用线程局部缓存构建账户公钥列表,避免重复分配 (只需构建一次) + let account_pubkeys = build_account_pubkeys_with_cache(&instruction.accounts, accounts); // 并行处理所有 (discriminator, config) 组合 let all_results: Vec<_> = all_processing_params .iter() .filter_map(|(disc, config)| { let data = &instruction.data[disc.len()..]; - self.parse_instruction_event( + Self::parse_instruction_event( config, data, &account_pubkeys, @@ -860,7 +735,7 @@ impl EventParser { for (_disc, config, mut event) in all_results { // 阻塞处理:原有的同步逻辑 - let mut inner_instruction_event: Option> = None; + let mut inner_instruction_event: Option = None; if inner_instructions.is_some() { let inner_instructions_ref = inner_instructions.unwrap(); @@ -868,7 +743,7 @@ impl EventParser { let (inner_event_result, swap_data_result) = std::thread::scope(|s| { let inner_event_handle = s.spawn(|| { for inner_instruction in inner_instructions_ref.instructions.iter() { - let result = self.parse_events_from_inner_instruction( + let result = Self::parse_events_from_inner_instruction( &inner_instruction.instruction, signature, slot, @@ -879,7 +754,7 @@ impl EventParser { transaction_index, &config, ); - if result.len() > 0 { + if !result.is_empty() { return Some(result[0].clone()); } } @@ -887,9 +762,9 @@ impl EventParser { }); let swap_data_handle = s.spawn(|| { - if !event.swap_data_is_parsed() { + if !event.metadata().swap_data.is_some() { parse_swap_data_from_next_instructions( - &*event, + &event, inner_instructions_ref, inner_index.unwrap_or(-1_i64) as i8, &accounts, @@ -905,7 +780,7 @@ impl EventParser { inner_instruction_event = inner_event_result; if let Some(swap_data) = swap_data_result { - event.set_swap_data(swap_data); + event.metadata_mut().set_swap_data(swap_data); } } @@ -916,11 +791,11 @@ impl EventParser { // 合并事件 if let Some(inner_instruction_event) = inner_instruction_event { - event.merge(&*inner_instruction_event); + merge(&mut event, inner_instruction_event); } // 设置处理时间(使用高性能时钟) - event.set_handle_us(elapsed_micros_since(recv_us)); - event = process_event(event, bot_wallet); + event.metadata_mut().handle_us = elapsed_micros_since(recv_us); + event = Self::process_event(event, bot_wallet); callback(&event); } Ok(()) @@ -930,7 +805,8 @@ impl EventParser { /// TODO: - wait refactor #[allow(clippy::too_many_arguments)] fn parse_events_from_grpc_instruction( - &self, + protocols: &[Protocol], + event_type_filter: Option<&EventTypeFilter>, instruction: &yellowstone_grpc_proto::prelude::CompiledInstruction, accounts: &[Pubkey], signature: Signature, @@ -942,15 +818,20 @@ impl EventParser { bot_wallet: Option, transaction_index: Option, inner_instructions: Option<&yellowstone_grpc_proto::prelude::InnerInstructions>, - callback: Arc Fn(&'a Box) + Send + Sync>, + callback: Arc Fn(&'a DexEvent) + Send + Sync>, ) -> anyhow::Result<()> { - let program_id = accounts[instruction.program_id_index as usize]; - if !self.should_handle(&program_id) { + // 添加边界检查以防止越界访问 + let program_id_index = instruction.program_id_index as usize; + if program_id_index >= accounts.len() { + return Ok(()); + } + let program_id = accounts[program_id_index]; + if !Self::should_handle(protocols, event_type_filter, &program_id) { return Ok(()); } // 一维化并行处理:将所有 (discriminator, config) 组合展开并行处理 - let all_processing_params: Vec<_> = self - .instruction_configs + let instruction_configs = get_global_instruction_configs(protocols, event_type_filter); + let all_processing_params: Vec<_> = instruction_configs .iter() .filter(|(disc, _)| { // Use SIMD-optimized data validation and discriminator matching @@ -970,18 +851,15 @@ impl EventParser { return Ok(()); } - // 使用缓存构建账户公钥列表,避免重复分配 (只需构建一次) - let account_pubkeys = { - let mut cache_guard = self.account_cache.lock(); - cache_guard.build_account_pubkeys(&instruction.accounts, accounts).to_vec() - }; + // 使用线程局部缓存构建账户公钥列表,避免重复分配 (只需构建一次) + let account_pubkeys = build_account_pubkeys_with_cache(&instruction.accounts, accounts); // 并行处理所有 (discriminator, config) 组合 let all_results: Vec<_> = all_processing_params .iter() .filter_map(|(disc, config)| { let data = &instruction.data[disc.len()..]; - self.parse_instruction_event( + Self::parse_instruction_event( config, data, &account_pubkeys, @@ -999,7 +877,7 @@ impl EventParser { for (_disc, config, mut event) in all_results { // 阻塞处理:原有的同步逻辑 - let mut inner_instruction_event: Option> = None; + let mut inner_instruction_event: Option = None; if inner_instructions.is_some() { let inner_instructions_ref = inner_instructions.unwrap(); @@ -1007,7 +885,7 @@ impl EventParser { let (inner_event_result, swap_data_result) = std::thread::scope(|s| { let inner_event_handle = s.spawn(|| { for inner_instruction in inner_instructions_ref.instructions.iter() { - let result = self.parse_events_from_grpc_inner_instruction( + let result = Self::parse_events_from_grpc_inner_instruction( &inner_instruction, signature, slot, @@ -1018,7 +896,7 @@ impl EventParser { transaction_index, &config, ); - if result.len() > 0 { + if !result.is_empty() { return Some(result[0].clone()); } } @@ -1026,9 +904,9 @@ impl EventParser { }); let swap_data_handle = s.spawn(|| { - if !event.swap_data_is_parsed() { + if !event.metadata().swap_data.is_some() { parse_swap_data_from_next_grpc_instructions( - &*event, + &event, inner_instructions_ref, inner_index.unwrap_or(-1_i64) as i8, &accounts, @@ -1044,7 +922,7 @@ impl EventParser { inner_instruction_event = inner_event_result; if let Some(swap_data) = swap_data_result { - event.set_swap_data(swap_data); + event.metadata_mut().set_swap_data(swap_data); } } @@ -1055,73 +933,94 @@ impl EventParser { // 合并事件 if let Some(inner_instruction_event) = inner_instruction_event { - event.merge(&*inner_instruction_event); + merge(&mut event, inner_instruction_event); } // 设置处理时间(使用高性能时钟) - event.set_handle_us(elapsed_micros_since(recv_us)); - event = process_event(event, bot_wallet); + event.metadata_mut().handle_us = elapsed_micros_since(recv_us); + event = Self::process_event(event, bot_wallet); callback(&event); } Ok(()) } - fn should_handle(&self, program_id: &Pubkey) -> bool { - self.program_ids.contains(program_id) + fn should_handle( + protocols: &[Protocol], + event_type_filter: Option<&EventTypeFilter>, + program_id: &Pubkey, + ) -> bool { + get_global_program_ids(protocols, event_type_filter).contains(program_id) } - // fn supported_program_ids(&self) -> Vec { - // self.program_ids.clone() - // } -} - -fn process_event( - mut event: Box, - bot_wallet: Option, -) -> Box { - let signature = *event.signature(); // Copy the signature to avoid borrowing issues - if let Some(token_info) = event.as_any().downcast_ref::() { - add_dev_address(&signature, token_info.user); - if token_info.creator != Pubkey::default() && token_info.creator != token_info.user { - add_dev_address(&signature, token_info.creator); - } - } else if let Some(trade_info) = event.as_any_mut().downcast_mut::() { - if is_dev_address_in_signature(&signature, &trade_info.user) - || is_dev_address_in_signature(&signature, &trade_info.creator) - { - trade_info.is_dev_create_token_trade = true; - } else if Some(trade_info.user) == bot_wallet { - trade_info.is_bot = true; - } else { - trade_info.is_dev_create_token_trade = false; - } - if trade_info.metadata.swap_data.is_some() { - trade_info.metadata.swap_data.as_mut().unwrap().from_amount = - if trade_info.is_buy { trade_info.sol_amount } else { trade_info.token_amount }; - trade_info.metadata.swap_data.as_mut().unwrap().to_amount = - if trade_info.is_buy { trade_info.token_amount } else { trade_info.sol_amount }; - } - } else if let Some(trade_info) = event.as_any_mut().downcast_mut::() { - if trade_info.metadata.swap_data.is_some() { - trade_info.metadata.swap_data.as_mut().unwrap().from_amount = - trade_info.user_quote_amount_in; - trade_info.metadata.swap_data.as_mut().unwrap().to_amount = trade_info.base_amount_out; - } - } else if let Some(trade_info) = event.as_any_mut().downcast_mut::() { - if trade_info.metadata.swap_data.is_some() { - trade_info.metadata.swap_data.as_mut().unwrap().from_amount = trade_info.base_amount_in; - trade_info.metadata.swap_data.as_mut().unwrap().to_amount = - trade_info.user_quote_amount_out; - } - } else if let Some(pool_info) = event.as_any().downcast_ref::() { - add_bonk_dev_address(&signature, pool_info.creator); - } else if let Some(trade_info) = event.as_any_mut().downcast_mut::() { - if is_bonk_dev_address_in_signature(&signature, &trade_info.payer) { - trade_info.is_dev_create_token_trade = true; - } else if Some(trade_info.payer) == bot_wallet { - trade_info.is_bot = true; - } else { - trade_info.is_dev_create_token_trade = false; + fn process_event(event: DexEvent, bot_wallet: Option) -> DexEvent { + let signature = event.metadata().signature; // Copy the signature to avoid borrowing issues + match event { + DexEvent::PumpFunCreateTokenEvent(token_info) => { + add_dev_address(&signature, token_info.user); + if token_info.creator != Pubkey::default() && token_info.creator != token_info.user + { + add_dev_address(&signature, token_info.creator); + } + DexEvent::PumpFunCreateTokenEvent(token_info) + } + DexEvent::PumpFunTradeEvent(mut trade_info) => { + if is_dev_address_in_signature(&signature, &trade_info.user) + || is_dev_address_in_signature(&signature, &trade_info.creator) + { + trade_info.is_dev_create_token_trade = true; + } else if Some(trade_info.user) == bot_wallet { + trade_info.is_bot = true; + } else { + trade_info.is_dev_create_token_trade = false; + } + if trade_info.metadata.swap_data.is_some() { + trade_info.metadata.swap_data.as_mut().unwrap().from_amount = + if trade_info.is_buy { + trade_info.sol_amount + } else { + trade_info.token_amount + }; + trade_info.metadata.swap_data.as_mut().unwrap().to_amount = if trade_info.is_buy + { + trade_info.token_amount + } else { + trade_info.sol_amount + }; + } + DexEvent::PumpFunTradeEvent(trade_info) + } + DexEvent::PumpSwapBuyEvent(mut trade_info) => { + if trade_info.metadata.swap_data.is_some() { + trade_info.metadata.swap_data.as_mut().unwrap().from_amount = + trade_info.user_quote_amount_in; + trade_info.metadata.swap_data.as_mut().unwrap().to_amount = + trade_info.base_amount_out; + } + DexEvent::PumpSwapBuyEvent(trade_info) + } + DexEvent::PumpSwapSellEvent(mut trade_info) => { + if trade_info.metadata.swap_data.is_some() { + trade_info.metadata.swap_data.as_mut().unwrap().from_amount = + trade_info.base_amount_in; + trade_info.metadata.swap_data.as_mut().unwrap().to_amount = + trade_info.user_quote_amount_out; + } + DexEvent::PumpSwapSellEvent(trade_info) + } + DexEvent::BonkPoolCreateEvent(pool_info) => { + add_bonk_dev_address(&signature, pool_info.creator); + DexEvent::BonkPoolCreateEvent(pool_info) + } + DexEvent::BonkTradeEvent(mut trade_info) => { + if is_bonk_dev_address_in_signature(&signature, &trade_info.payer) { + trade_info.is_dev_create_token_trade = true; + } else if Some(trade_info.payer) == bot_wallet { + trade_info.is_bot = true; + } else { + trade_info.is_dev_create_token_trade = false; + } + DexEvent::BonkTradeEvent(trade_info) + } + _ => event, } } - event } diff --git a/src/streaming/event_parser/core/merger_event.rs b/src/streaming/event_parser/core/merger_event.rs new file mode 100644 index 0000000..2497d87 --- /dev/null +++ b/src/streaming/event_parser/core/merger_event.rs @@ -0,0 +1,226 @@ +use crate::streaming::event_parser::DexEvent; + +pub fn merge(instruction_event: &mut DexEvent, cpi_log_event: DexEvent) { + match instruction_event { + // PumpFun events + DexEvent::PumpFunTradeEvent(e) => match cpi_log_event { + DexEvent::PumpFunTradeEvent(cpie) => { + e.mint = cpie.mint; + e.sol_amount = cpie.sol_amount; + e.token_amount = cpie.token_amount; + e.is_buy = cpie.is_buy; + e.user = cpie.user; + e.timestamp = cpie.timestamp; + e.virtual_sol_reserves = cpie.virtual_sol_reserves; + e.virtual_token_reserves = cpie.virtual_token_reserves; + e.real_sol_reserves = cpie.real_sol_reserves; + e.real_token_reserves = cpie.real_token_reserves; + e.fee_recipient = cpie.fee_recipient; + e.fee_basis_points = cpie.fee_basis_points; + e.fee = cpie.fee; + e.creator = cpie.creator; + e.creator_fee_basis_points = cpie.creator_fee_basis_points; + e.creator_fee = cpie.creator_fee; + } + _ => {} + }, + DexEvent::PumpFunCreateTokenEvent(e) => match cpi_log_event { + DexEvent::PumpFunCreateTokenEvent(cpie) => { + e.mint = cpie.mint; + e.bonding_curve = cpie.bonding_curve; + e.user = cpie.user; + e.creator = cpie.creator; + e.timestamp = cpie.timestamp; + e.virtual_token_reserves = cpie.virtual_token_reserves; + e.virtual_sol_reserves = cpie.virtual_sol_reserves; + e.real_token_reserves = cpie.real_token_reserves; + e.token_total_supply = cpie.token_total_supply; + } + _ => {} + }, + DexEvent::PumpFunMigrateEvent(e) => match cpi_log_event { + DexEvent::PumpFunMigrateEvent(cpie) => { + e.user = cpie.user; + e.mint = cpie.mint; + e.mint_amount = cpie.mint_amount; + e.sol_amount = cpie.sol_amount; + e.pool_migration_fee = cpie.pool_migration_fee; + e.bonding_curve = cpie.bonding_curve; + e.timestamp = cpie.timestamp; + e.pool = cpie.pool; + } + _ => {} + }, + + // Bonk events + DexEvent::BonkTradeEvent(e) => match cpi_log_event { + DexEvent::BonkTradeEvent(cpie) => { + e.pool_state = cpie.pool_state; + e.total_base_sell = cpie.total_base_sell; + e.virtual_base = cpie.virtual_base; + e.virtual_quote = cpie.virtual_quote; + e.real_base_before = cpie.real_base_before; + e.real_quote_before = cpie.real_quote_before; + e.real_base_after = cpie.real_base_after; + e.real_quote_after = cpie.real_quote_after; + e.amount_in = cpie.amount_in; + e.amount_out = cpie.amount_out; + e.protocol_fee = cpie.protocol_fee; + e.platform_fee = cpie.platform_fee; + e.creator_fee = cpie.creator_fee; + e.share_fee = cpie.share_fee; + e.trade_direction = cpie.trade_direction; + e.pool_status = cpie.pool_status; + e.exact_in = cpie.exact_in; + } + _ => {} + }, + DexEvent::BonkPoolCreateEvent(e) => match cpi_log_event { + DexEvent::BonkPoolCreateEvent(cpie) => { + e.pool_state = cpie.pool_state; + e.creator = cpie.creator; + e.config = cpie.config; + e.base_mint_param = cpie.base_mint_param; + e.curve_param = cpie.curve_param; + e.vesting_param = cpie.vesting_param; + e.amm_fee_on = cpie.amm_fee_on; + } + _ => {} + }, + DexEvent::BonkMigrateToAmmEvent(e) => match cpi_log_event { + DexEvent::BonkMigrateToAmmEvent(cpie) => { + e.base_lot_size = cpie.base_lot_size; + e.quote_lot_size = cpie.quote_lot_size; + e.market_vault_signer_nonce = cpie.market_vault_signer_nonce; + } + _ => {} + }, + + // PumpSwap events + DexEvent::PumpSwapBuyEvent(e) => match cpi_log_event { + DexEvent::PumpSwapBuyEvent(cpie) => { + e.timestamp = cpie.timestamp; + e.base_amount_out = cpie.base_amount_out; + e.max_quote_amount_in = cpie.max_quote_amount_in; + e.user_base_token_reserves = cpie.user_base_token_reserves; + e.user_quote_token_reserves = cpie.user_quote_token_reserves; + e.pool_base_token_reserves = cpie.pool_base_token_reserves; + e.pool_quote_token_reserves = cpie.pool_quote_token_reserves; + e.quote_amount_in = cpie.quote_amount_in; + e.lp_fee_basis_points = cpie.lp_fee_basis_points; + e.lp_fee = cpie.lp_fee; + e.protocol_fee_basis_points = cpie.protocol_fee_basis_points; + e.protocol_fee = cpie.protocol_fee; + e.quote_amount_in_with_lp_fee = cpie.quote_amount_in_with_lp_fee; + e.user_quote_amount_in = cpie.user_quote_amount_in; + e.pool = cpie.pool; + e.user = cpie.user; + e.user_base_token_account = cpie.user_base_token_account; + e.user_quote_token_account = cpie.user_quote_token_account; + e.protocol_fee_recipient = cpie.protocol_fee_recipient; + e.protocol_fee_recipient_token_account = cpie.protocol_fee_recipient_token_account; + e.coin_creator = cpie.coin_creator; + e.coin_creator_fee_basis_points = cpie.coin_creator_fee_basis_points; + e.coin_creator_fee = cpie.coin_creator_fee; + } + _ => {} + }, + DexEvent::PumpSwapSellEvent(e) => match cpi_log_event { + DexEvent::PumpSwapSellEvent(cpie) => { + e.timestamp = cpie.timestamp; + e.base_amount_in = cpie.base_amount_in; + e.min_quote_amount_out = cpie.min_quote_amount_out; + e.user_base_token_reserves = cpie.user_base_token_reserves; + e.user_quote_token_reserves = cpie.user_quote_token_reserves; + e.pool_base_token_reserves = cpie.pool_base_token_reserves; + e.pool_quote_token_reserves = cpie.pool_quote_token_reserves; + e.quote_amount_out = cpie.quote_amount_out; + e.lp_fee_basis_points = cpie.lp_fee_basis_points; + e.lp_fee = cpie.lp_fee; + e.protocol_fee_basis_points = cpie.protocol_fee_basis_points; + e.protocol_fee = cpie.protocol_fee; + e.quote_amount_out_without_lp_fee = cpie.quote_amount_out_without_lp_fee; + e.user_quote_amount_out = cpie.user_quote_amount_out; + e.pool = cpie.pool; + e.user = cpie.user; + e.user_base_token_account = cpie.user_base_token_account; + e.user_quote_token_account = cpie.user_quote_token_account; + e.protocol_fee_recipient = cpie.protocol_fee_recipient; + e.protocol_fee_recipient_token_account = cpie.protocol_fee_recipient_token_account; + e.coin_creator = cpie.coin_creator; + e.coin_creator_fee_basis_points = cpie.coin_creator_fee_basis_points; + e.coin_creator_fee = cpie.coin_creator_fee; + } + _ => {} + }, + DexEvent::PumpSwapCreatePoolEvent(e) => match cpi_log_event { + DexEvent::PumpSwapCreatePoolEvent(cpie) => { + e.timestamp = cpie.timestamp; + e.index = cpie.index; + e.creator = cpie.creator; + e.base_mint = cpie.base_mint; + e.quote_mint = cpie.quote_mint; + e.base_mint_decimals = cpie.base_mint_decimals; + e.quote_mint_decimals = cpie.quote_mint_decimals; + e.base_amount_in = cpie.base_amount_in; + e.quote_amount_in = cpie.quote_amount_in; + e.pool_base_amount = cpie.pool_base_amount; + e.pool_quote_amount = cpie.pool_quote_amount; + e.minimum_liquidity = cpie.minimum_liquidity; + e.initial_liquidity = cpie.initial_liquidity; + e.lp_token_amount_out = cpie.lp_token_amount_out; + e.pool_bump = cpie.pool_bump; + e.pool = cpie.pool; + e.lp_mint = cpie.lp_mint; + e.user_base_token_account = cpie.user_base_token_account; + e.user_quote_token_account = cpie.user_quote_token_account; + e.coin_creator = cpie.coin_creator; + } + _ => {} + }, + DexEvent::PumpSwapDepositEvent(e) => match cpi_log_event { + DexEvent::PumpSwapDepositEvent(cpie) => { + e.timestamp = cpie.timestamp; + e.lp_token_amount_out = cpie.lp_token_amount_out; + e.max_base_amount_in = cpie.max_base_amount_in; + e.max_quote_amount_in = cpie.max_quote_amount_in; + e.user_base_token_reserves = cpie.user_base_token_reserves; + e.user_quote_token_reserves = cpie.user_quote_token_reserves; + e.pool_base_token_reserves = cpie.pool_base_token_reserves; + e.pool_quote_token_reserves = cpie.pool_quote_token_reserves; + e.base_amount_in = cpie.base_amount_in; + e.quote_amount_in = cpie.quote_amount_in; + e.lp_mint_supply = cpie.lp_mint_supply; + e.pool = cpie.pool; + e.user = cpie.user; + e.user_base_token_account = cpie.user_base_token_account; + e.user_quote_token_account = cpie.user_quote_token_account; + e.user_pool_token_account = cpie.user_pool_token_account; + } + _ => {} + }, + DexEvent::PumpSwapWithdrawEvent(e) => match cpi_log_event { + DexEvent::PumpSwapWithdrawEvent(cpie) => { + e.timestamp = cpie.timestamp; + e.lp_token_amount_in = cpie.lp_token_amount_in; + e.min_base_amount_out = cpie.min_base_amount_out; + e.min_quote_amount_out = cpie.min_quote_amount_out; + e.user_base_token_reserves = cpie.user_base_token_reserves; + e.user_quote_token_reserves = cpie.user_quote_token_reserves; + e.pool_base_token_reserves = cpie.pool_base_token_reserves; + e.pool_quote_token_reserves = cpie.pool_quote_token_reserves; + e.base_amount_out = cpie.base_amount_out; + e.quote_amount_out = cpie.quote_amount_out; + e.lp_mint_supply = cpie.lp_mint_supply; + e.pool = cpie.pool; + e.user = cpie.user; + e.user_base_token_account = cpie.user_base_token_account; + e.user_quote_token_account = cpie.user_quote_token_account; + e.user_pool_token_account = cpie.user_pool_token_account; + } + _ => {} + }, + + _ => {} + } +} diff --git a/src/streaming/event_parser/core/mod.rs b/src/streaming/event_parser/core/mod.rs index 39fda22..2fcba6a 100755 --- a/src/streaming/event_parser/core/mod.rs +++ b/src/streaming/event_parser/core/mod.rs @@ -1,7 +1,13 @@ pub mod account_event_parser; pub mod common_event_parser; pub mod global_state; +pub mod parser_cache; pub mod traits; -pub use traits::UnifiedEvent; +pub use traits::DexEvent; +pub use parser_cache::{ + AccountEventParseConfig, AccountEventParserFn, GenericEventParseConfig, + InnerInstructionEventParser, InstructionEventParser, get_account_configs, +}; pub mod event_parser; +pub mod merger_event; \ No newline at end of file diff --git a/src/streaming/event_parser/core/parser_cache.rs b/src/streaming/event_parser/core/parser_cache.rs new file mode 100644 index 0000000..d13cb8c --- /dev/null +++ b/src/streaming/event_parser/core/parser_cache.rs @@ -0,0 +1,601 @@ +//! # 事件解析器配置缓存模块 +//! +//! 本模块统一管理所有事件解析器的配置和缓存,包括: +//! - 指令事件解析器(Instruction Event Parser) +//! - 账户事件解析器(Account Event Parser) +//! - 高性能缓存工具 +//! +//! ## 设计目标 +//! - **统一配置管理**:所有协议的解析器配置集中管理 +//! - **高性能缓存**:避免重复初始化和内存分配 +//! - **易于扩展**:添加新协议只需修改配置映射 + +use crate::streaming::{ + event_parser::{ + common::{filter::EventTypeFilter, EventMetadata, EventType, ProtocolType}, + protocols::{ + bonk::parser::BONK_PROGRAM_ID, + pumpfun::parser::PUMPFUN_PROGRAM_ID, + pumpswap::parser::PUMPSWAP_PROGRAM_ID, + raydium_amm_v4::parser::RAYDIUM_AMM_V4_PROGRAM_ID, + raydium_clmm::parser::RAYDIUM_CLMM_PROGRAM_ID, + raydium_cpmm::parser::RAYDIUM_CPMM_PROGRAM_ID, + }, + Protocol, DexEvent, + }, + grpc::AccountPretty, +}; +use solana_sdk::pubkey::Pubkey; +use std::{ + collections::HashMap, + sync::{Arc, LazyLock, OnceLock}, +}; + +// ============================================================================ +// 第一部分:指令事件解析器配置(Instruction Event Parser) +// ============================================================================ + +/// 内联指令事件解析器函数类型 +/// +/// 用于解析内联指令(Inner Instruction)生成的事件 +pub type InnerInstructionEventParser = + fn(data: &[u8], metadata: EventMetadata) -> Option; + +/// 指令事件解析器函数类型 +/// +/// 用于解析普通指令(Instruction)生成的事件,需要账户信息 +pub type InstructionEventParser = + fn(data: &[u8], accounts: &[Pubkey], metadata: EventMetadata) -> Option; + +/// 指令事件解析器配置 +/// +/// 定义如何解析特定协议的指令事件 +#[derive(Debug, Clone)] +pub struct GenericEventParseConfig { + /// 程序ID(Program ID) + pub program_id: Pubkey, + /// 协议类型 + pub protocol_type: ProtocolType, + /// 内联指令判别器(8字节) + pub inner_instruction_discriminator: &'static [u8], + /// 普通指令判别器(8字节) + pub instruction_discriminator: &'static [u8], + /// 事件类型 + pub event_type: EventType, + /// 内联指令解析器 + pub inner_instruction_parser: Option, + /// 普通指令解析器 + pub instruction_parser: Option, + /// 是否需要内联指令数据 + pub requires_inner_instruction: bool, +} + +/// 全局指令事件解析器注册表 +/// +/// 存储所有协议的指令解析器配置,启动时初始化一次 +pub static EVENT_PARSERS: LazyLock> = + LazyLock::new(|| { + let mut parsers = HashMap::with_capacity(6); + + // 注册各协议的解析器配置 + parsers.insert( + Protocol::PumpSwap, + ( + PUMPSWAP_PROGRAM_ID, + crate::streaming::event_parser::protocols::pumpswap::parser::CONFIGS, + ), + ); + parsers.insert( + Protocol::PumpFun, + ( + PUMPFUN_PROGRAM_ID, + crate::streaming::event_parser::protocols::pumpfun::parser::CONFIGS, + ), + ); + parsers.insert( + Protocol::Bonk, + (BONK_PROGRAM_ID, crate::streaming::event_parser::protocols::bonk::parser::CONFIGS), + ); + parsers.insert( + Protocol::RaydiumCpmm, + ( + RAYDIUM_CPMM_PROGRAM_ID, + crate::streaming::event_parser::protocols::raydium_cpmm::parser::CONFIGS, + ), + ); + parsers.insert( + Protocol::RaydiumClmm, + ( + RAYDIUM_CLMM_PROGRAM_ID, + crate::streaming::event_parser::protocols::raydium_clmm::parser::CONFIGS, + ), + ); + parsers.insert( + Protocol::RaydiumAmmV4, + ( + RAYDIUM_AMM_V4_PROGRAM_ID, + crate::streaming::event_parser::protocols::raydium_amm_v4::parser::CONFIGS, + ), + ); + + parsers + }); + +// ---------------------------------------------------------------------------- +// 指令解析器缓存工具 +// ---------------------------------------------------------------------------- + +/// 缓存键:用于标识不同的协议和过滤器组合 +/// +/// 通过对协议和事件类型排序,确保相同组合生成相同的哈希键 +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct CacheKey { + /// 协议列表(已排序) + pub protocols: Vec, + /// 事件类型过滤器(可选,已排序) + pub event_types: Option>, +} + +impl CacheKey { + /// 创建新的缓存键 + /// + /// # 参数 + /// - `protocols`: 协议列表 + /// - `filter`: 事件类型过滤器 + /// + /// # 优化 + /// 使用 `sort_by_cached_key` 避免重复调用 `format!` + pub fn new(mut protocols: Vec, filter: Option<&EventTypeFilter>) -> Self { + // 排序协议列表,确保相同协议组合生成相同的key + protocols.sort_by_cached_key(|p| format!("{:?}", p)); + + let event_types = filter.map(|f| { + let mut types = f.include.clone(); + types.sort_by_cached_key(|t| format!("{:?}", t)); + types + }); + + Self { protocols, event_types } + } +} + +/// 全局程序ID缓存(使用读写锁保护) +static GLOBAL_PROGRAM_IDS_CACHE: LazyLock< + parking_lot::RwLock>>>, +> = LazyLock::new(|| parking_lot::RwLock::new(HashMap::new())); + +/// 全局指令配置缓存(使用读写锁保护) +static GLOBAL_INSTRUCTION_CONFIGS_CACHE: LazyLock< + parking_lot::RwLock, Vec>>>>, +> = LazyLock::new(|| parking_lot::RwLock::new(HashMap::new())); + +/// 获取指定协议的程序ID列表 +/// +/// # 参数 +/// - `protocols`: 协议列表 +/// - `filter`: 事件类型过滤器(可选) +/// +/// # 返回 +/// Arc包装的程序ID向量,支持零成本共享 +/// +/// # 缓存策略 +/// - 快速路径:从缓存读取(读锁) +/// - 慢速路径:构建并缓存(写锁) +pub fn get_global_program_ids( + protocols: &[Protocol], + filter: Option<&EventTypeFilter>, +) -> Arc> { + let cache_key = CacheKey::new(protocols.to_vec(), filter); + + // 快速路径:尝试读取缓存 + { + let cache = GLOBAL_PROGRAM_IDS_CACHE.read(); + if let Some(program_ids) = cache.get(&cache_key) { + return program_ids.clone(); + } + } + + // 慢速路径:构建并缓存 + let mut program_ids = Vec::with_capacity(protocols.len()); + for protocol in protocols { + if let Some(parse) = EVENT_PARSERS.get(protocol) { + program_ids.push(parse.0); + } + } + let program_ids = Arc::new(program_ids); + + // 缓存结果(写锁) + GLOBAL_PROGRAM_IDS_CACHE.write().insert(cache_key, program_ids.clone()); + + program_ids +} + +/// 获取指定协议的指令配置 +/// +/// # 参数 +/// - `protocols`: 协议列表 +/// - `filter`: 事件类型过滤器(可选) +/// +/// # 返回 +/// Arc包装的指令配置映射表,按判别器(Discriminator)索引 +/// +/// # 缓存策略 +/// - 快速路径:从缓存读取(读锁) +/// - 慢速路径:构建并缓存(写锁) +pub fn get_global_instruction_configs( + protocols: &[Protocol], + filter: Option<&EventTypeFilter>, +) -> Arc, Vec>> { + let cache_key = CacheKey::new(protocols.to_vec(), filter); + + // 快速路径:尝试读取缓存 + { + let cache = GLOBAL_INSTRUCTION_CONFIGS_CACHE.read(); + if let Some(configs) = cache.get(&cache_key) { + return configs.clone(); + } + } + + // 慢速路径:构建并缓存 + let mut instruction_configs = HashMap::with_capacity(protocols.len() * 4); + for protocol in protocols { + if let Some(parse) = EVENT_PARSERS.get(protocol) { + parse + .1 + .iter() + .filter(|config| { + filter.as_ref().map(|f| f.include.contains(&config.event_type)).unwrap_or(true) + }) + .for_each(|config| { + instruction_configs + .entry(config.instruction_discriminator.to_vec()) + .or_insert_with(Vec::new) + .push(config.clone()); + }); + } + } + let instruction_configs = Arc::new(instruction_configs); + + // 缓存结果(写锁) + GLOBAL_INSTRUCTION_CONFIGS_CACHE.write().insert(cache_key, instruction_configs.clone()); + + instruction_configs +} + +// ============================================================================ +// 第二部分:账户公钥缓存工具(Account Pubkey Cache) +// ============================================================================ + +/// 高性能账户公钥缓存 +/// +/// 通过重用内存避免重复Vec分配,提升性能 +#[derive(Debug)] +pub struct AccountPubkeyCache { + /// 预分配的账户公钥向量 + cache: Vec, +} + +impl AccountPubkeyCache { + /// 创建新的账户公钥缓存 + /// + /// 预分配32个位置,覆盖大多数交易场景 + pub fn new() -> Self { + Self { + cache: Vec::with_capacity(32), + } + } + + /// 从指令账户索引构建账户公钥向量 + /// + /// # 参数 + /// - `instruction_accounts`: 指令账户索引列表 + /// - `all_accounts`: 所有账户公钥列表 + /// + /// # 返回 + /// 账户公钥切片引用 + /// + /// # 性能优化 + /// - 重用内部缓存,避免重新分配 + /// - 仅在必要时扩容 + #[inline] + pub fn build_account_pubkeys( + &mut self, + instruction_accounts: &[u8], + all_accounts: &[Pubkey], + ) -> &[Pubkey] { + self.cache.clear(); + + // 确保容量足够,避免动态扩容 + if self.cache.capacity() < instruction_accounts.len() { + self.cache.reserve(instruction_accounts.len() - self.cache.capacity()); + } + + // 快速填充账户公钥(带边界检查) + for &idx in instruction_accounts.iter() { + if (idx as usize) < all_accounts.len() { + self.cache.push(all_accounts[idx as usize]); + } + } + + &self.cache + } +} + +impl Default for AccountPubkeyCache { + fn default() -> Self { + Self::new() + } +} + +thread_local! { + static THREAD_LOCAL_ACCOUNT_CACHE: std::cell::RefCell = + std::cell::RefCell::new(AccountPubkeyCache::new()); +} + +/// 从线程局部缓存构建账户公钥列表 +/// +/// # 参数 +/// - `instruction_accounts`: 指令账户索引列表 +/// - `all_accounts`: 所有账户公钥列表 +/// +/// # 返回 +/// 账户公钥向量 +/// +/// # 线程安全 +/// 使用线程局部存储,每个线程独立缓存 +#[inline] +pub fn build_account_pubkeys_with_cache( + instruction_accounts: &[u8], + all_accounts: &[Pubkey], +) -> Vec { + THREAD_LOCAL_ACCOUNT_CACHE.with(|cache| { + let mut cache = cache.borrow_mut(); + cache.build_account_pubkeys(instruction_accounts, all_accounts).to_vec() + }) +} + +// ============================================================================ +// 第三部分:账户事件解析器配置(Account Event Parser) +// ============================================================================ + +/// 账户事件解析器函数类型 +/// +/// 用于解析账户状态变更生成的事件 +pub type AccountEventParserFn = + fn(account: &AccountPretty, metadata: EventMetadata) -> Option; + +/// 账户事件解析器配置 +/// +/// 定义如何解析特定协议的账户事件 +#[derive(Debug, Clone)] +pub struct AccountEventParseConfig { + /// 程序ID(Program ID) + pub program_id: Pubkey, + /// 协议类型 + pub protocol_type: ProtocolType, + /// 事件类型 + pub event_type: EventType, + /// 账户判别器(Account Discriminator) + pub account_discriminator: &'static [u8], + /// 账户解析器函数 + pub account_parser: AccountEventParserFn, +} + +/// 协议账户配置缓存 +/// +/// 存储各协议的账户解析器配置 +static PROTOCOL_CONFIGS_CACHE: OnceLock>> = + OnceLock::new(); + +/// Nonce 账户配置缓存 +static NONCE_CONFIG: OnceLock = OnceLock::new(); + +/// 通用 Token 账户配置缓存 +static COMMON_CONFIG: OnceLock = OnceLock::new(); + +/// 获取账户解析器配置列表 +/// +/// # 参数 +/// - `protocols`: 协议列表 +/// - `event_type_filter`: 事件类型过滤器(可选) +/// - `nonce_parser`: Nonce账户解析器 +/// - `token_parser`: Token账户解析器 +/// +/// # 返回 +/// 账户解析器配置向量 +/// +/// # 配置组成 +/// 1. 协议特定配置(根据protocols参数) +/// 2. Nonce账户配置(通用) +/// 3. Token账户配置(通用,作为兜底) +/// +/// # 示例 +/// ```ignore +/// let configs = get_account_configs( +/// &[Protocol::PumpFun, Protocol::Bonk], +/// None, +/// parse_nonce_account, +/// parse_token_account, +/// ); +/// ``` +pub fn get_account_configs( + protocols: &[Protocol], + event_type_filter: Option<&EventTypeFilter>, + nonce_parser: AccountEventParserFn, + token_parser: AccountEventParserFn, +) -> Vec { + // 初始化协议配置缓存(仅在首次调用时执行) + let protocols_map = PROTOCOL_CONFIGS_CACHE.get_or_init(|| { + let mut map = HashMap::new(); + + // PumpSwap 协议 + map.insert(Protocol::PumpSwap, vec![ + AccountEventParseConfig { + program_id: PUMPSWAP_PROGRAM_ID, + protocol_type: ProtocolType::PumpSwap, + event_type: EventType::AccountPumpSwapGlobalConfig, + account_discriminator: crate::streaming::event_parser::protocols::pumpswap::discriminators::GLOBAL_CONFIG_ACCOUNT, + account_parser: crate::streaming::event_parser::protocols::pumpswap::types::global_config_parser, + }, + AccountEventParseConfig { + program_id: PUMPSWAP_PROGRAM_ID, + protocol_type: ProtocolType::PumpSwap, + event_type: EventType::AccountPumpSwapPool, + account_discriminator: crate::streaming::event_parser::protocols::pumpswap::discriminators::POOL_ACCOUNT, + account_parser: crate::streaming::event_parser::protocols::pumpswap::types::pool_parser, + }, + ]); + + // PumpFun 协议 + map.insert(Protocol::PumpFun, vec![ + AccountEventParseConfig { + program_id: PUMPFUN_PROGRAM_ID, + protocol_type: ProtocolType::PumpFun, + event_type: EventType::AccountPumpFunBondingCurve, + account_discriminator: crate::streaming::event_parser::protocols::pumpfun::discriminators::BONDING_CURVE_ACCOUNT, + account_parser: crate::streaming::event_parser::protocols::pumpfun::types::bonding_curve_parser, + }, + AccountEventParseConfig { + program_id: PUMPFUN_PROGRAM_ID, + protocol_type: ProtocolType::PumpFun, + event_type: EventType::AccountPumpFunGlobal, + account_discriminator: crate::streaming::event_parser::protocols::pumpfun::discriminators::GLOBAL_ACCOUNT, + account_parser: crate::streaming::event_parser::protocols::pumpfun::types::global_parser, + }, + ]); + + // Bonk 协议 + map.insert(Protocol::Bonk, vec![ + AccountEventParseConfig { + program_id: BONK_PROGRAM_ID, + protocol_type: ProtocolType::Bonk, + event_type: EventType::AccountBonkPoolState, + account_discriminator: crate::streaming::event_parser::protocols::bonk::discriminators::POOL_STATE_ACCOUNT, + account_parser: crate::streaming::event_parser::protocols::bonk::types::pool_state_parser, + }, + AccountEventParseConfig { + program_id: BONK_PROGRAM_ID, + protocol_type: ProtocolType::Bonk, + event_type: EventType::AccountBonkGlobalConfig, + account_discriminator: crate::streaming::event_parser::protocols::bonk::discriminators::GLOBAL_CONFIG_ACCOUNT, + account_parser: crate::streaming::event_parser::protocols::bonk::types::global_config_parser, + }, + AccountEventParseConfig { + program_id: BONK_PROGRAM_ID, + protocol_type: ProtocolType::Bonk, + event_type: EventType::AccountBonkPlatformConfig, + account_discriminator: crate::streaming::event_parser::protocols::bonk::discriminators::PLATFORM_CONFIG_ACCOUNT, + account_parser: crate::streaming::event_parser::protocols::bonk::types::platform_config_parser, + }, + ]); + + // Raydium CPMM 协议 + map.insert(Protocol::RaydiumCpmm, vec![ + AccountEventParseConfig { + program_id: RAYDIUM_CPMM_PROGRAM_ID, + protocol_type: ProtocolType::RaydiumCpmm, + event_type: EventType::AccountRaydiumCpmmAmmConfig, + account_discriminator: crate::streaming::event_parser::protocols::raydium_cpmm::discriminators::AMM_CONFIG, + account_parser: crate::streaming::event_parser::protocols::raydium_cpmm::types::amm_config_parser, + }, + AccountEventParseConfig { + program_id: RAYDIUM_CPMM_PROGRAM_ID, + protocol_type: ProtocolType::RaydiumCpmm, + event_type: EventType::AccountRaydiumCpmmPoolState, + account_discriminator: crate::streaming::event_parser::protocols::raydium_cpmm::discriminators::POOL_STATE, + account_parser: crate::streaming::event_parser::protocols::raydium_cpmm::types::pool_state_parser, + }, + ]); + + // Raydium CLMM 协议 + map.insert(Protocol::RaydiumClmm, vec![ + AccountEventParseConfig { + program_id: RAYDIUM_CLMM_PROGRAM_ID, + protocol_type: ProtocolType::RaydiumClmm, + event_type: EventType::AccountRaydiumClmmAmmConfig, + account_discriminator: crate::streaming::event_parser::protocols::raydium_clmm::discriminators::AMM_CONFIG, + account_parser: crate::streaming::event_parser::protocols::raydium_clmm::types::amm_config_parser, + }, + AccountEventParseConfig { + program_id: RAYDIUM_CLMM_PROGRAM_ID, + protocol_type: ProtocolType::RaydiumClmm, + event_type: EventType::AccountRaydiumClmmPoolState, + account_discriminator: crate::streaming::event_parser::protocols::raydium_clmm::discriminators::POOL_STATE, + account_parser: crate::streaming::event_parser::protocols::raydium_clmm::types::pool_state_parser, + }, + AccountEventParseConfig { + program_id: RAYDIUM_CLMM_PROGRAM_ID, + protocol_type: ProtocolType::RaydiumClmm, + event_type: EventType::AccountRaydiumClmmTickArrayState, + account_discriminator: crate::streaming::event_parser::protocols::raydium_clmm::discriminators::TICK_ARRAY_STATE, + account_parser: crate::streaming::event_parser::protocols::raydium_clmm::types::tick_array_state_parser, + }, + ]); + + // Raydium AMM V4 协议 + map.insert(Protocol::RaydiumAmmV4, vec![ + AccountEventParseConfig { + program_id: RAYDIUM_AMM_V4_PROGRAM_ID, + protocol_type: ProtocolType::RaydiumAmmV4, + event_type: EventType::AccountRaydiumAmmV4AmmInfo, + account_discriminator: crate::streaming::event_parser::protocols::raydium_amm_v4::discriminators::AMM_INFO, + account_parser: crate::streaming::event_parser::protocols::raydium_amm_v4::types::amm_info_parser, + }, + ]); + + map + }); + + // 构建配置列表 + let mut configs = Vec::new(); + let empty_vec = Vec::new(); + + // 预估容量(大多数协议有2-3个配置) + let estimated_capacity = protocols.len() * 3; + configs.reserve(estimated_capacity); + + // 1. 添加协议特定配置 + for protocol in protocols { + let protocol_configs = protocols_map.get(protocol).unwrap_or(&empty_vec); + + if event_type_filter.is_none() { + // 无过滤器:添加所有配置 + configs.extend(protocol_configs.iter().cloned()); + } else { + // 有过滤器:仅添加匹配的配置 + let filter = event_type_filter.unwrap(); + configs.extend( + protocol_configs + .iter() + .filter(|config| filter.include.contains(&config.event_type)) + .cloned(), + ); + } + } + + // 2. 添加 Nonce 账户配置(通用配置) + if event_type_filter.is_none() + || event_type_filter.unwrap().include.contains(&EventType::NonceAccount) + { + let nonce_config = NONCE_CONFIG.get_or_init(|| AccountEventParseConfig { + program_id: Pubkey::default(), + protocol_type: ProtocolType::Common, + event_type: EventType::NonceAccount, + account_discriminator: &[1, 0, 0, 0, 1, 0, 0, 0], + account_parser: nonce_parser, + }); + configs.push(nonce_config.clone()); + } + + // 3. 添加通用 Token 账户配置(兜底配置,始终添加) + let common_config = COMMON_CONFIG.get_or_init(|| AccountEventParseConfig { + program_id: Pubkey::default(), + protocol_type: ProtocolType::Common, + event_type: EventType::TokenAccount, + account_discriminator: &[], + account_parser: token_parser, + }); + configs.push(common_config.clone()); + + configs +} diff --git a/src/streaming/event_parser/core/traits.rs b/src/streaming/event_parser/core/traits.rs index 850e1f3..a2f6f00 100755 --- a/src/streaming/event_parser/core/traits.rs +++ b/src/streaming/event_parser/core/traits.rs @@ -1,59 +1,181 @@ -use crate::streaming::event_parser::common::EventType; -use crate::streaming::event_parser::common::SwapData; -use solana_sdk::signature::Signature; +use crate::streaming::event_parser::common::EventMetadata; +use crate::streaming::event_parser::core::account_event_parser::{ + NonceAccountEvent, TokenAccountEvent, TokenInfoEvent, +}; +use crate::streaming::event_parser::protocols::block::block_meta_event::BlockMetaEvent; +use crate::streaming::event_parser::protocols::bonk::events::*; +use crate::streaming::event_parser::protocols::pumpfun::events::*; +use crate::streaming::event_parser::protocols::pumpswap::events::*; +use crate::streaming::event_parser::protocols::raydium_amm_v4::events::*; +use crate::streaming::event_parser::protocols::raydium_clmm::events::*; +use crate::streaming::event_parser::protocols::raydium_cpmm::events::*; +use serde::{Deserialize, Serialize}; use std::fmt::Debug; -/// Unified Event Interface - All protocol events must implement this trait -pub trait UnifiedEvent: Debug + Send + Sync { - /// Get event type - fn event_type(&self) -> EventType; +/// Unified Event Enum - Replaces the trait-based approach with a type-safe enum +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub enum DexEvent { + // Bonk events + BonkTradeEvent(BonkTradeEvent), + BonkPoolCreateEvent(BonkPoolCreateEvent), + BonkMigrateToAmmEvent(BonkMigrateToAmmEvent), + BonkMigrateToCpswapEvent(BonkMigrateToCpswapEvent), + BonkPoolStateAccountEvent(BonkPoolStateAccountEvent), + BonkGlobalConfigAccountEvent(BonkGlobalConfigAccountEvent), + BonkPlatformConfigAccountEvent(BonkPlatformConfigAccountEvent), - /// Get transaction signature - fn signature(&self) -> &Signature; + // PumpFun events + PumpFunCreateTokenEvent(PumpFunCreateTokenEvent), + PumpFunTradeEvent(PumpFunTradeEvent), + PumpFunMigrateEvent(PumpFunMigrateEvent), + PumpFunBondingCurveAccountEvent(PumpFunBondingCurveAccountEvent), + PumpFunGlobalAccountEvent(PumpFunGlobalAccountEvent), - /// Get slot number - fn slot(&self) -> u64; + // PumpSwap events + PumpSwapBuyEvent(PumpSwapBuyEvent), + PumpSwapSellEvent(PumpSwapSellEvent), + PumpSwapCreatePoolEvent(PumpSwapCreatePoolEvent), + PumpSwapDepositEvent(PumpSwapDepositEvent), + PumpSwapWithdrawEvent(PumpSwapWithdrawEvent), + PumpSwapGlobalConfigAccountEvent(PumpSwapGlobalConfigAccountEvent), + PumpSwapPoolAccountEvent(PumpSwapPoolAccountEvent), - /// Get program received timestamp (milliseconds) - fn recv_us(&self) -> i64; + // Raydium AMM V4 events + RaydiumAmmV4SwapEvent(RaydiumAmmV4SwapEvent), + RaydiumAmmV4DepositEvent(RaydiumAmmV4DepositEvent), + RaydiumAmmV4WithdrawEvent(RaydiumAmmV4WithdrawEvent), + RaydiumAmmV4WithdrawPnlEvent(RaydiumAmmV4WithdrawPnlEvent), + RaydiumAmmV4Initialize2Event(RaydiumAmmV4Initialize2Event), + RaydiumAmmV4AmmInfoAccountEvent(RaydiumAmmV4AmmInfoAccountEvent), - /// Processing time consumption (milliseconds) - fn handle_us(&self) -> i64; + // Raydium CLMM events + RaydiumClmmSwapEvent(RaydiumClmmSwapEvent), + RaydiumClmmSwapV2Event(RaydiumClmmSwapV2Event), + RaydiumClmmClosePositionEvent(RaydiumClmmClosePositionEvent), + RaydiumClmmIncreaseLiquidityV2Event(RaydiumClmmIncreaseLiquidityV2Event), + RaydiumClmmDecreaseLiquidityV2Event(RaydiumClmmDecreaseLiquidityV2Event), + RaydiumClmmCreatePoolEvent(RaydiumClmmCreatePoolEvent), + RaydiumClmmOpenPositionWithToken22NftEvent(RaydiumClmmOpenPositionWithToken22NftEvent), + RaydiumClmmOpenPositionV2Event(RaydiumClmmOpenPositionV2Event), + RaydiumClmmAmmConfigAccountEvent(RaydiumClmmAmmConfigAccountEvent), + RaydiumClmmPoolStateAccountEvent(RaydiumClmmPoolStateAccountEvent), + RaydiumClmmTickArrayStateAccountEvent(RaydiumClmmTickArrayStateAccountEvent), - /// Set processing time consumption (milliseconds) - fn set_handle_us(&mut self, handle_us: i64); + // Raydium CPMM events + RaydiumCpmmSwapEvent(RaydiumCpmmSwapEvent), + RaydiumCpmmDepositEvent(RaydiumCpmmDepositEvent), + RaydiumCpmmWithdrawEvent(RaydiumCpmmWithdrawEvent), + RaydiumCpmmInitializeEvent(RaydiumCpmmInitializeEvent), + RaydiumCpmmAmmConfigAccountEvent(RaydiumCpmmAmmConfigAccountEvent), + RaydiumCpmmPoolStateAccountEvent(RaydiumCpmmPoolStateAccountEvent), - /// Convert event to Any for downcasting - fn as_any(&self) -> &dyn std::any::Any; - - /// Convert event to mutable Any for downcasting - fn as_any_mut(&mut self) -> &mut dyn std::any::Any; - - /// Clone the event - fn clone_boxed(&self) -> Box; - - /// Merge events (optional implementation) - fn merge(&mut self, _other: &dyn UnifiedEvent) { - // Default implementation: no merging operation - } - - /// Set swap data - fn set_swap_data(&mut self, swap_data: SwapData); - - /// swap_data is parsed - fn swap_data_is_parsed(&self) -> bool; - - /// Get index - fn outer_index(&self) -> i64; - fn inner_index(&self) -> Option; - - /// Get transaction index in slot - fn transaction_index(&self) -> Option; + // Common events + TokenAccountEvent(TokenAccountEvent), + NonceAccountEvent(NonceAccountEvent), + TokenInfoEvent(TokenInfoEvent), + BlockMetaEvent(BlockMetaEvent), } -// 为Box实现Clone -impl Clone for Box { - fn clone(&self) -> Self { - self.clone_boxed() +impl DexEvent { + pub fn metadata(&self) -> &EventMetadata { + match self { + DexEvent::BonkTradeEvent(e) => &e.metadata, + DexEvent::BonkPoolCreateEvent(e) => &e.metadata, + DexEvent::BonkMigrateToAmmEvent(e) => &e.metadata, + DexEvent::BonkMigrateToCpswapEvent(e) => &e.metadata, + DexEvent::BonkPoolStateAccountEvent(e) => &e.metadata, + DexEvent::BonkGlobalConfigAccountEvent(e) => &e.metadata, + DexEvent::BonkPlatformConfigAccountEvent(e) => &e.metadata, + DexEvent::PumpFunCreateTokenEvent(e) => &e.metadata, + DexEvent::PumpFunTradeEvent(e) => &e.metadata, + DexEvent::PumpFunMigrateEvent(e) => &e.metadata, + DexEvent::PumpFunBondingCurveAccountEvent(e) => &e.metadata, + DexEvent::PumpFunGlobalAccountEvent(e) => &e.metadata, + DexEvent::PumpSwapBuyEvent(e) => &e.metadata, + DexEvent::PumpSwapSellEvent(e) => &e.metadata, + DexEvent::PumpSwapCreatePoolEvent(e) => &e.metadata, + DexEvent::PumpSwapDepositEvent(e) => &e.metadata, + DexEvent::PumpSwapWithdrawEvent(e) => &e.metadata, + DexEvent::PumpSwapGlobalConfigAccountEvent(e) => &e.metadata, + DexEvent::PumpSwapPoolAccountEvent(e) => &e.metadata, + DexEvent::RaydiumAmmV4SwapEvent(e) => &e.metadata, + DexEvent::RaydiumAmmV4DepositEvent(e) => &e.metadata, + DexEvent::RaydiumAmmV4WithdrawEvent(e) => &e.metadata, + DexEvent::RaydiumAmmV4WithdrawPnlEvent(e) => &e.metadata, + DexEvent::RaydiumAmmV4Initialize2Event(e) => &e.metadata, + DexEvent::RaydiumAmmV4AmmInfoAccountEvent(e) => &e.metadata, + DexEvent::RaydiumClmmSwapEvent(e) => &e.metadata, + DexEvent::RaydiumClmmSwapV2Event(e) => &e.metadata, + DexEvent::RaydiumClmmClosePositionEvent(e) => &e.metadata, + DexEvent::RaydiumClmmIncreaseLiquidityV2Event(e) => &e.metadata, + DexEvent::RaydiumClmmDecreaseLiquidityV2Event(e) => &e.metadata, + DexEvent::RaydiumClmmCreatePoolEvent(e) => &e.metadata, + DexEvent::RaydiumClmmOpenPositionWithToken22NftEvent(e) => &e.metadata, + DexEvent::RaydiumClmmOpenPositionV2Event(e) => &e.metadata, + DexEvent::RaydiumClmmAmmConfigAccountEvent(e) => &e.metadata, + DexEvent::RaydiumClmmPoolStateAccountEvent(e) => &e.metadata, + DexEvent::RaydiumClmmTickArrayStateAccountEvent(e) => &e.metadata, + DexEvent::RaydiumCpmmSwapEvent(e) => &e.metadata, + DexEvent::RaydiumCpmmDepositEvent(e) => &e.metadata, + DexEvent::RaydiumCpmmWithdrawEvent(e) => &e.metadata, + DexEvent::RaydiumCpmmInitializeEvent(e) => &e.metadata, + DexEvent::RaydiumCpmmAmmConfigAccountEvent(e) => &e.metadata, + DexEvent::RaydiumCpmmPoolStateAccountEvent(e) => &e.metadata, + DexEvent::TokenAccountEvent(e) => &e.metadata, + DexEvent::NonceAccountEvent(e) => &e.metadata, + DexEvent::TokenInfoEvent(e) => &e.metadata, + DexEvent::BlockMetaEvent(e) => &e.metadata, + } + } + + pub fn metadata_mut(&mut self) -> &mut EventMetadata { + match self { + DexEvent::BonkTradeEvent(e) => &mut e.metadata, + DexEvent::BonkPoolCreateEvent(e) => &mut e.metadata, + DexEvent::BonkMigrateToAmmEvent(e) => &mut e.metadata, + DexEvent::BonkMigrateToCpswapEvent(e) => &mut e.metadata, + DexEvent::BonkPoolStateAccountEvent(e) => &mut e.metadata, + DexEvent::BonkGlobalConfigAccountEvent(e) => &mut e.metadata, + DexEvent::BonkPlatformConfigAccountEvent(e) => &mut e.metadata, + DexEvent::PumpFunCreateTokenEvent(e) => &mut e.metadata, + DexEvent::PumpFunTradeEvent(e) => &mut e.metadata, + DexEvent::PumpFunMigrateEvent(e) => &mut e.metadata, + DexEvent::PumpFunBondingCurveAccountEvent(e) => &mut e.metadata, + DexEvent::PumpFunGlobalAccountEvent(e) => &mut e.metadata, + DexEvent::PumpSwapBuyEvent(e) => &mut e.metadata, + DexEvent::PumpSwapSellEvent(e) => &mut e.metadata, + DexEvent::PumpSwapCreatePoolEvent(e) => &mut e.metadata, + DexEvent::PumpSwapDepositEvent(e) => &mut e.metadata, + DexEvent::PumpSwapWithdrawEvent(e) => &mut e.metadata, + DexEvent::PumpSwapGlobalConfigAccountEvent(e) => &mut e.metadata, + DexEvent::PumpSwapPoolAccountEvent(e) => &mut e.metadata, + DexEvent::RaydiumAmmV4SwapEvent(e) => &mut e.metadata, + DexEvent::RaydiumAmmV4DepositEvent(e) => &mut e.metadata, + DexEvent::RaydiumAmmV4WithdrawEvent(e) => &mut e.metadata, + DexEvent::RaydiumAmmV4WithdrawPnlEvent(e) => &mut e.metadata, + DexEvent::RaydiumAmmV4Initialize2Event(e) => &mut e.metadata, + DexEvent::RaydiumAmmV4AmmInfoAccountEvent(e) => &mut e.metadata, + DexEvent::RaydiumClmmSwapEvent(e) => &mut e.metadata, + DexEvent::RaydiumClmmSwapV2Event(e) => &mut e.metadata, + DexEvent::RaydiumClmmClosePositionEvent(e) => &mut e.metadata, + DexEvent::RaydiumClmmIncreaseLiquidityV2Event(e) => &mut e.metadata, + DexEvent::RaydiumClmmDecreaseLiquidityV2Event(e) => &mut e.metadata, + DexEvent::RaydiumClmmCreatePoolEvent(e) => &mut e.metadata, + DexEvent::RaydiumClmmOpenPositionWithToken22NftEvent(e) => &mut e.metadata, + DexEvent::RaydiumClmmOpenPositionV2Event(e) => &mut e.metadata, + DexEvent::RaydiumClmmAmmConfigAccountEvent(e) => &mut e.metadata, + DexEvent::RaydiumClmmPoolStateAccountEvent(e) => &mut e.metadata, + DexEvent::RaydiumClmmTickArrayStateAccountEvent(e) => &mut e.metadata, + DexEvent::RaydiumCpmmSwapEvent(e) => &mut e.metadata, + DexEvent::RaydiumCpmmDepositEvent(e) => &mut e.metadata, + DexEvent::RaydiumCpmmWithdrawEvent(e) => &mut e.metadata, + DexEvent::RaydiumCpmmInitializeEvent(e) => &mut e.metadata, + DexEvent::RaydiumCpmmAmmConfigAccountEvent(e) => &mut e.metadata, + DexEvent::RaydiumCpmmPoolStateAccountEvent(e) => &mut e.metadata, + DexEvent::TokenAccountEvent(e) => &mut e.metadata, + DexEvent::NonceAccountEvent(e) => &mut e.metadata, + DexEvent::TokenInfoEvent(e) => &mut e.metadata, + DexEvent::BlockMetaEvent(e) => &mut e.metadata, + } } } diff --git a/src/streaming/event_parser/mod.rs b/src/streaming/event_parser/mod.rs index cb5a70f..b981df0 100755 --- a/src/streaming/event_parser/mod.rs +++ b/src/streaming/event_parser/mod.rs @@ -2,39 +2,5 @@ pub mod common; pub mod core; pub mod protocols; -pub use core::traits::UnifiedEvent; -pub use protocols::types::Protocol; - -/// 宏:简化 downcast_ref 模式匹配 -/// -/// # 使用示例 -/// ``` -/// use sol_trade_sdk::event_parser::match_event; -/// -/// match_event!(event, { -/// PumpSwapCreatePoolEvent => |typed_event| { -/// println!("CreatePool event: {:?}", typed_event); -/// }, -/// PumpSwapDepositEvent => |typed_event| { -/// // 处理存款事件 -/// }, -/// }); -/// ``` -#[macro_export] -macro_rules! match_event { - ($event:expr, { - $($event_type:ty => $handler:expr),* $(,)? - }) => { - $( - if let Some(typed_event) = $event.as_any().downcast_ref::<$event_type>() { - $handler(typed_event.clone()); - } else - )* - { - // 默认情况:什么都不做 - } - }; -} - -// 重新导出宏以便于使用 -pub use match_event; +pub use core::traits::DexEvent; +pub use protocols::types::Protocol; \ No newline at end of file diff --git a/src/streaming/event_parser/protocols/block/block_meta_event.rs b/src/streaming/event_parser/protocols/block/block_meta_event.rs index 59390ce..6d606c6 100644 --- a/src/streaming/event_parser/protocols/block/block_meta_event.rs +++ b/src/streaming/event_parser/protocols/block/block_meta_event.rs @@ -1,4 +1,3 @@ -use crate::impl_unified_event; use crate::streaming::event_parser::common::{types::EventType, EventMetadata}; use borsh::BorshDeserialize; use serde::{Deserialize, Serialize}; @@ -36,6 +35,3 @@ impl BlockMetaEvent { Self { metadata, slot, block_hash } } } - -// 使用macro生成UnifiedEvent实现 -impl_unified_event!(BlockMetaEvent,); diff --git a/src/streaming/event_parser/protocols/bonk/events.rs b/src/streaming/event_parser/protocols/bonk/events.rs index ed651d5..8172a59 100755 --- a/src/streaming/event_parser/protocols/bonk/events.rs +++ b/src/streaming/event_parser/protocols/bonk/events.rs @@ -1,4 +1,3 @@ -use crate::impl_unified_event; use crate::streaming::event_parser::common::EventMetadata; use crate::streaming::event_parser::protocols::bonk::types::{ CurveParams, MintParams, PoolStatus, TradeDirection, VestingParams, @@ -81,28 +80,6 @@ pub fn bonk_trade_event_log_decode(data: &[u8]) -> Option { 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, - pool_state, - total_base_sell, - virtual_base, - virtual_quote, - real_base_before, - real_quote_before, - real_base_after, - real_quote_after, - amount_in, - amount_out, - protocol_fee, - platform_fee, - creator_fee, - share_fee, - trade_direction, - pool_status, - exact_in -); - /// Create pool event #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)] pub struct BonkPoolCreateEvent { @@ -140,18 +117,6 @@ pub fn bonk_pool_create_event_log_decode(data: &[u8]) -> Option(&data[..BONK_POOL_CREATE_EVENT_LOG_SIZE]).ok() } -// Macro to generate UnifiedEvent implementation, specifying the fields to be merged -impl_unified_event!( - BonkPoolCreateEvent, - pool_state, - creator, - config, - base_mint_param, - curve_param, - vesting_param, - amm_fee_on -); - /// Create pool event #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)] pub struct BonkMigrateToAmmEvent { @@ -226,14 +191,6 @@ pub struct BonkMigrateToAmmEvent { pub rent_program: Pubkey, } -// Macro to generate UnifiedEvent implementation, specifying the fields to be merged -impl_unified_event!( - BonkMigrateToAmmEvent, - base_lot_size, - quote_lot_size, - market_vault_signer_nonce -); - // Migrate to CP Swap event #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)] pub struct BonkMigrateToCpswapEvent { @@ -270,9 +227,6 @@ pub struct BonkMigrateToCpswapEvent { pub remaining_accounts: Vec, } -// Macro to generate UnifiedEvent implementation, specifying the fields to be merged -impl_unified_event!(BonkMigrateToCpswapEvent,); - /// 池状态 #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct BonkPoolStateAccountEvent { @@ -284,7 +238,6 @@ pub struct BonkPoolStateAccountEvent { pub rent_epoch: u64, pub pool_state: PoolState, } -impl_unified_event!(BonkPoolStateAccountEvent,); /// 全局配置 #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] @@ -297,7 +250,6 @@ pub struct BonkGlobalConfigAccountEvent { pub rent_epoch: u64, pub global_config: GlobalConfig, } -impl_unified_event!(BonkGlobalConfigAccountEvent,); /// 平台配置 #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] @@ -310,7 +262,6 @@ pub struct BonkPlatformConfigAccountEvent { pub rent_epoch: u64, pub platform_config: PlatformConfig, } -impl_unified_event!(BonkPlatformConfigAccountEvent,); /// Event discriminator constants pub mod discriminators { diff --git a/src/streaming/event_parser/protocols/bonk/parser.rs b/src/streaming/event_parser/protocols/bonk/parser.rs index f64203c..07829d6 100755 --- a/src/streaming/event_parser/protocols/bonk/parser.rs +++ b/src/streaming/event_parser/protocols/bonk/parser.rs @@ -2,14 +2,14 @@ use solana_sdk::pubkey::Pubkey; use crate::streaming::event_parser::{ common::{utils::*, EventMetadata, EventType, ProtocolType}, - core::event_parser::GenericEventParseConfig, + core::GenericEventParseConfig, protocols::bonk::{ bonk_pool_create_event_log_decode, bonk_trade_event_log_decode, discriminators, AmmFeeOn, BonkMigrateToAmmEvent, BonkMigrateToCpswapEvent, BonkPoolCreateEvent, BonkTradeEvent, ConstantCurve, CurveParams, FixedCurve, LinearCurve, MintParams, TradeDirection, VestingParams, }, - UnifiedEvent, + DexEvent, }; /// Bonk Program ID @@ -113,19 +113,16 @@ pub const CONFIGS: &[GenericEventParseConfig] = &[ fn parse_pool_create_inner_instruction( data: &[u8], metadata: EventMetadata, -) -> Option> { +) -> Option { if let Some(event) = bonk_pool_create_event_log_decode(data) { - Some(Box::new(BonkPoolCreateEvent { metadata, ..event })) + Some(DexEvent::BonkPoolCreateEvent(BonkPoolCreateEvent { metadata, ..event })) } else { None } } /// Parse trade event -fn parse_trade_inner_instruction( - data: &[u8], - metadata: EventMetadata, -) -> Option> { +fn parse_trade_inner_instruction(data: &[u8], metadata: EventMetadata) -> Option { if let Some(event) = bonk_trade_event_log_decode(data) { if metadata.event_type == EventType::BonkBuyExactIn || metadata.event_type == EventType::BonkBuyExactOut @@ -139,7 +136,7 @@ fn parse_trade_inner_instruction( { return None; } - Some(Box::new(BonkTradeEvent { metadata, ..event })) + Some(DexEvent::BonkTradeEvent(BonkTradeEvent { metadata, ..event })) } else { None } @@ -150,7 +147,7 @@ fn parse_buy_exact_in_instruction( data: &[u8], accounts: &[Pubkey], metadata: EventMetadata, -) -> Option> { +) -> Option { if data.len() < 16 || accounts.len() < 18 { return None; } @@ -159,7 +156,7 @@ fn parse_buy_exact_in_instruction( let minimum_amount_out = read_u64_le(data, 8)?; let share_fee_rate = read_u64_le(data, 16)?; - Some(Box::new(BonkTradeEvent { + Some(DexEvent::BonkTradeEvent(BonkTradeEvent { metadata, amount_in, minimum_amount_out, @@ -188,7 +185,7 @@ fn parse_buy_exact_out_instruction( data: &[u8], accounts: &[Pubkey], metadata: EventMetadata, -) -> Option> { +) -> Option { if data.len() < 16 || accounts.len() < 18 { return None; } @@ -197,7 +194,7 @@ fn parse_buy_exact_out_instruction( let maximum_amount_in = read_u64_le(data, 8)?; let share_fee_rate = read_u64_le(data, 16)?; - Some(Box::new(BonkTradeEvent { + Some(DexEvent::BonkTradeEvent(BonkTradeEvent { metadata, amount_out, maximum_amount_in, @@ -226,7 +223,7 @@ fn parse_sell_exact_in_instruction( data: &[u8], accounts: &[Pubkey], metadata: EventMetadata, -) -> Option> { +) -> Option { if data.len() < 16 || accounts.len() < 18 { return None; } @@ -235,7 +232,7 @@ fn parse_sell_exact_in_instruction( let minimum_amount_out = read_u64_le(data, 8)?; let share_fee_rate = read_u64_le(data, 16)?; - Some(Box::new(BonkTradeEvent { + Some(DexEvent::BonkTradeEvent(BonkTradeEvent { metadata, amount_in, minimum_amount_out, @@ -264,7 +261,7 @@ fn parse_sell_exact_out_instruction( data: &[u8], accounts: &[Pubkey], metadata: EventMetadata, -) -> Option> { +) -> Option { if data.len() < 16 || accounts.len() < 18 { return None; } @@ -273,7 +270,7 @@ fn parse_sell_exact_out_instruction( let maximum_amount_in = read_u64_le(data, 8)?; let share_fee_rate = read_u64_le(data, 16)?; - Some(Box::new(BonkTradeEvent { + Some(DexEvent::BonkTradeEvent(BonkTradeEvent { metadata, amount_out, maximum_amount_in, @@ -303,7 +300,7 @@ fn parse_initialize_instruction( data: &[u8], accounts: &[Pubkey], metadata: EventMetadata, -) -> Option> { +) -> Option { if data.len() < 24 { return None; } @@ -313,7 +310,7 @@ fn parse_initialize_instruction( let curve_param = parse_curve_params(data, &mut offset)?; let vesting_param = parse_vesting_params(data, &mut offset)?; - Some(Box::new(BonkPoolCreateEvent { + Some(DexEvent::BonkPoolCreateEvent(BonkPoolCreateEvent { metadata, payer: accounts[0], creator: accounts[1], @@ -336,7 +333,7 @@ fn parse_initialize_v2_instruction( data: &[u8], accounts: &[Pubkey], metadata: EventMetadata, -) -> Option> { +) -> Option { if data.len() < 24 { return None; } @@ -347,7 +344,7 @@ fn parse_initialize_v2_instruction( let vesting_param = parse_vesting_params(data, &mut offset)?; let amm_fee_on = data[offset]; - Some(Box::new(BonkPoolCreateEvent { + Some(DexEvent::BonkPoolCreateEvent(BonkPoolCreateEvent { metadata, payer: accounts[0], creator: accounts[1], @@ -375,7 +372,7 @@ fn parse_initialize_with_token_2022_instruction( data: &[u8], accounts: &[Pubkey], metadata: EventMetadata, -) -> Option> { +) -> Option { if data.len() < 24 { return None; } @@ -386,7 +383,7 @@ fn parse_initialize_with_token_2022_instruction( let vesting_param = parse_vesting_params(data, &mut offset)?; let amm_fee_on = data[offset]; - Some(Box::new(BonkPoolCreateEvent { + Some(DexEvent::BonkPoolCreateEvent(BonkPoolCreateEvent { metadata, payer: accounts[0], creator: accounts[1], @@ -519,7 +516,7 @@ fn parse_migrate_to_amm_instruction( data: &[u8], accounts: &[Pubkey], metadata: EventMetadata, -) -> Option> { +) -> Option { if data.len() < 16 { return None; } @@ -528,7 +525,7 @@ fn parse_migrate_to_amm_instruction( let quote_lot_size = u64::from_le_bytes(data[8..16].try_into().unwrap()); let market_vault_signer_nonce = data[16]; - Some(Box::new(BonkMigrateToAmmEvent { + Some(DexEvent::BonkMigrateToAmmEvent(BonkMigrateToAmmEvent { metadata, base_lot_size, quote_lot_size, @@ -574,8 +571,8 @@ fn parse_migrate_to_cpswap_instruction( _data: &[u8], accounts: &[Pubkey], metadata: EventMetadata, -) -> Option> { - Some(Box::new(BonkMigrateToCpswapEvent { +) -> Option { + Some(DexEvent::BonkMigrateToCpswapEvent(BonkMigrateToCpswapEvent { metadata, payer: accounts[0], base_mint: accounts[1], diff --git a/src/streaming/event_parser/protocols/bonk/types.rs b/src/streaming/event_parser/protocols/bonk/types.rs index 2e82b12..5386852 100755 --- a/src/streaming/event_parser/protocols/bonk/types.rs +++ b/src/streaming/event_parser/protocols/bonk/types.rs @@ -8,7 +8,7 @@ use crate::streaming::{ protocols::bonk::{ BonkGlobalConfigAccountEvent, BonkPlatformConfigAccountEvent, BonkPoolStateAccountEvent, }, - UnifiedEvent, + DexEvent, }, grpc::AccountPretty, }; @@ -132,15 +132,12 @@ pub fn pool_state_decode(data: &[u8]) -> Option { borsh::from_slice::(&data[..POOL_STATE_SIZE]).ok() } -pub fn pool_state_parser( - account: &AccountPretty, - metadata: EventMetadata, -) -> Option> { +pub fn pool_state_parser(account: &AccountPretty, metadata: EventMetadata) -> Option { if account.data.len() < POOL_STATE_SIZE + 8 { return None; } if let Some(pool_state) = pool_state_decode(&account.data[8..POOL_STATE_SIZE + 8]) { - Some(Box::new(BonkPoolStateAccountEvent { + Some(DexEvent::BonkPoolStateAccountEvent(BonkPoolStateAccountEvent { metadata, pubkey: account.pubkey, executable: account.executable, @@ -186,12 +183,12 @@ pub fn global_config_decode(data: &[u8]) -> Option { pub fn global_config_parser( account: &AccountPretty, metadata: EventMetadata, -) -> Option> { +) -> Option { if account.data.len() < GLOBAL_CONFIG_SIZE + 8 { return None; } if let Some(global_config) = global_config_decode(&account.data[8..GLOBAL_CONFIG_SIZE + 8]) { - Some(Box::new(BonkGlobalConfigAccountEvent { + Some(DexEvent::BonkGlobalConfigAccountEvent(BonkGlobalConfigAccountEvent { metadata, pubkey: account.pubkey, executable: account.executable, @@ -232,14 +229,14 @@ pub fn platform_config_decode(data: &[u8]) -> Option { pub fn platform_config_parser( account: &AccountPretty, metadata: EventMetadata, -) -> Option> { +) -> Option { if account.data.len() < PLATFORM_CONFIG_SIZE + 8 { return None; } if let Some(platform_config) = platform_config_decode(&account.data[8..PLATFORM_CONFIG_SIZE + 8]) { - Some(Box::new(BonkPlatformConfigAccountEvent { + Some(DexEvent::BonkPlatformConfigAccountEvent(BonkPlatformConfigAccountEvent { metadata, pubkey: account.pubkey, executable: account.executable, diff --git a/src/streaming/event_parser/protocols/pumpfun/events.rs b/src/streaming/event_parser/protocols/pumpfun/events.rs index 32212e2..1e176ce 100755 --- a/src/streaming/event_parser/protocols/pumpfun/events.rs +++ b/src/streaming/event_parser/protocols/pumpfun/events.rs @@ -2,7 +2,6 @@ use borsh::BorshDeserialize; use serde::{Deserialize, Serialize}; use solana_sdk::pubkey::Pubkey; -use crate::impl_unified_event; use crate::streaming::event_parser::common::EventMetadata; use crate::streaming::event_parser::protocols::pumpfun::types::{BondingCurve, Global}; @@ -37,19 +36,6 @@ pub fn pumpfun_create_token_event_log_decode(data: &[u8]) -> Option(&data[..PUMPFUN_CREATE_TOKEN_EVENT_LOG_SIZE]).ok() } -impl_unified_event!( - PumpFunCreateTokenEvent, - mint, - bonding_curve, - user, - creator, - timestamp, - virtual_token_reserves, - virtual_sol_reserves, - real_token_reserves, - token_total_supply -); - #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)] pub struct PumpFunTradeEvent { #[borsh(skip)] @@ -126,26 +112,6 @@ pub fn pumpfun_trade_event_log_decode(data: &[u8]) -> Option borsh::from_slice::(&data[..PUMPFUN_TRADE_EVENT_LOG_SIZE]).ok() } -impl_unified_event!( - PumpFunTradeEvent, - mint, - sol_amount, - token_amount, - is_buy, - user, - timestamp, - virtual_sol_reserves, - virtual_token_reserves, - real_sol_reserves, - real_token_reserves, - fee_recipient, - fee_basis_points, - fee, - creator, - creator_fee_basis_points, - creator_fee -); - #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)] pub struct PumpFunMigrateEvent { #[borsh(skip)] @@ -211,18 +177,6 @@ pub fn pumpfun_migrate_event_log_decode(data: &[u8]) -> Option(&data[..PUMPFUN_MIGRATE_EVENT_LOG_SIZE]).ok() } -impl_unified_event!( - PumpFunMigrateEvent, - user, - mint, - mint_amount, - sol_amount, - pool_migration_fee, - bonding_curve, - timestamp, - pool -); - /// 铸币曲线 #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)] pub struct PumpFunBondingCurveAccountEvent { @@ -236,8 +190,6 @@ pub struct PumpFunBondingCurveAccountEvent { pub bonding_curve: BondingCurve, } -impl_unified_event!(PumpFunBondingCurveAccountEvent,); - /// 全局配置 #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)] pub struct PumpFunGlobalAccountEvent { @@ -250,7 +202,6 @@ pub struct PumpFunGlobalAccountEvent { pub rent_epoch: u64, pub global: Global, } -impl_unified_event!(PumpFunGlobalAccountEvent,); /// 事件鉴别器常量 pub mod discriminators { diff --git a/src/streaming/event_parser/protocols/pumpfun/parser.rs b/src/streaming/event_parser/protocols/pumpfun/parser.rs index 9f81879..d92decd 100755 --- a/src/streaming/event_parser/protocols/pumpfun/parser.rs +++ b/src/streaming/event_parser/protocols/pumpfun/parser.rs @@ -1,12 +1,12 @@ use crate::streaming::event_parser::{ common::{EventMetadata, EventType, ProtocolType}, - core::event_parser::GenericEventParseConfig, + core::GenericEventParseConfig, protocols::pumpfun::{ discriminators, pumpfun_create_token_event_log_decode, pumpfun_migrate_event_log_decode, pumpfun_trade_event_log_decode, PumpFunCreateTokenEvent, PumpFunMigrateEvent, PumpFunTradeEvent, }, - UnifiedEvent, + DexEvent, }; use solana_sdk::pubkey::Pubkey; @@ -60,12 +60,9 @@ pub const CONFIGS: &[GenericEventParseConfig] = &[ ]; /// 解析迁移事件 -fn parse_migrate_inner_instruction( - data: &[u8], - metadata: EventMetadata, -) -> Option> { +fn parse_migrate_inner_instruction(data: &[u8], metadata: EventMetadata) -> Option { if let Some(event) = pumpfun_migrate_event_log_decode(data) { - Some(Box::new(PumpFunMigrateEvent { metadata, ..event })) + Some(DexEvent::PumpFunMigrateEvent(PumpFunMigrateEvent { metadata, ..event })) } else { None } @@ -75,21 +72,18 @@ fn parse_migrate_inner_instruction( fn parse_create_token_inner_instruction( data: &[u8], metadata: EventMetadata, -) -> Option> { +) -> Option { if let Some(event) = pumpfun_create_token_event_log_decode(data) { - Some(Box::new(PumpFunCreateTokenEvent { metadata, ..event })) + Some(DexEvent::PumpFunCreateTokenEvent(PumpFunCreateTokenEvent { metadata, ..event })) } else { None } } /// 解析交易事件 -fn parse_trade_inner_instruction( - data: &[u8], - metadata: EventMetadata, -) -> Option> { +fn parse_trade_inner_instruction(data: &[u8], metadata: EventMetadata) -> Option { if let Some(event) = pumpfun_trade_event_log_decode(data) { - Some(Box::new(PumpFunTradeEvent { metadata, ..event })) + Some(DexEvent::PumpFunTradeEvent(PumpFunTradeEvent { metadata, ..event })) } else { None } @@ -100,7 +94,7 @@ fn parse_create_token_instruction( data: &[u8], accounts: &[Pubkey], metadata: EventMetadata, -) -> Option> { +) -> Option { if data.len() < 16 || accounts.len() < 11 { return None; } @@ -141,7 +135,7 @@ fn parse_create_token_instruction( Pubkey::default() }; - Some(Box::new(PumpFunCreateTokenEvent { + Some(DexEvent::PumpFunCreateTokenEvent(PumpFunCreateTokenEvent { metadata, name: name.to_string(), symbol: symbol.to_string(), @@ -161,13 +155,13 @@ fn parse_buy_instruction( data: &[u8], accounts: &[Pubkey], metadata: EventMetadata, -) -> Option> { +) -> Option { if data.len() < 16 || accounts.len() < 13 { return None; } let amount = u64::from_le_bytes(data[0..8].try_into().unwrap()); let max_sol_cost = u64::from_le_bytes(data[8..16].try_into().unwrap()); - Some(Box::new(PumpFunTradeEvent { + Some(DexEvent::PumpFunTradeEvent(PumpFunTradeEvent { metadata, global: accounts[0], fee_recipient: accounts[1], @@ -195,13 +189,13 @@ fn parse_sell_instruction( data: &[u8], accounts: &[Pubkey], metadata: EventMetadata, -) -> Option> { +) -> Option { if data.len() < 16 || accounts.len() < 11 { return None; } let amount = u64::from_le_bytes(data[0..8].try_into().unwrap()); let min_sol_output = u64::from_le_bytes(data[8..16].try_into().unwrap()); - Some(Box::new(PumpFunTradeEvent { + Some(DexEvent::PumpFunTradeEvent(PumpFunTradeEvent { metadata, global: accounts[0], fee_recipient: accounts[1], @@ -229,11 +223,11 @@ fn parse_migrate_instruction( _data: &[u8], accounts: &[Pubkey], metadata: EventMetadata, -) -> Option> { +) -> Option { if accounts.len() < 24 { return None; } - Some(Box::new(PumpFunMigrateEvent { + Some(DexEvent::PumpFunMigrateEvent(PumpFunMigrateEvent { metadata, global: accounts[0], withdraw_authority: accounts[1], diff --git a/src/streaming/event_parser/protocols/pumpfun/types.rs b/src/streaming/event_parser/protocols/pumpfun/types.rs index d419996..15a0e23 100644 --- a/src/streaming/event_parser/protocols/pumpfun/types.rs +++ b/src/streaming/event_parser/protocols/pumpfun/types.rs @@ -6,7 +6,7 @@ use crate::streaming::{ event_parser::{ common::EventMetadata, protocols::pumpfun::{PumpFunBondingCurveAccountEvent, PumpFunGlobalAccountEvent}, - UnifiedEvent, + DexEvent, }, grpc::AccountPretty, }; @@ -34,12 +34,12 @@ pub fn bonding_curve_decode(data: &[u8]) -> Option { pub fn bonding_curve_parser( account: &AccountPretty, metadata: EventMetadata, -) -> Option> { +) -> Option { if account.data.len() < BONDING_CURVE_SIZE + 8 { return None; } if let Some(bonding_curve) = bonding_curve_decode(&account.data[8..BONDING_CURVE_SIZE + 8]) { - Some(Box::new(PumpFunBondingCurveAccountEvent { + Some(DexEvent::PumpFunBondingCurveAccountEvent(PumpFunBondingCurveAccountEvent { metadata, pubkey: account.pubkey, executable: account.executable, @@ -81,15 +81,12 @@ pub fn global_decode(data: &[u8]) -> Option { borsh::from_slice::(&data[..GLOBAL_SIZE]).ok() } -pub fn global_parser( - account: &AccountPretty, - metadata: EventMetadata, -) -> Option> { +pub fn global_parser(account: &AccountPretty, metadata: EventMetadata) -> Option { if account.data.len() < GLOBAL_SIZE + 8 { return None; } if let Some(global) = global_decode(&account.data[8..GLOBAL_SIZE + 8]) { - Some(Box::new(PumpFunGlobalAccountEvent { + Some(DexEvent::PumpFunGlobalAccountEvent(PumpFunGlobalAccountEvent { metadata, pubkey: account.pubkey, executable: account.executable, diff --git a/src/streaming/event_parser/protocols/pumpswap/events.rs b/src/streaming/event_parser/protocols/pumpswap/events.rs index c3d2322..67b1188 100755 --- a/src/streaming/event_parser/protocols/pumpswap/events.rs +++ b/src/streaming/event_parser/protocols/pumpswap/events.rs @@ -2,7 +2,6 @@ use borsh::BorshDeserialize; use serde::{Deserialize, Serialize}; use solana_sdk::pubkey::Pubkey; -use crate::impl_unified_event; use crate::streaming::event_parser::common::EventMetadata; use crate::streaming::event_parser::protocols::pumpswap::types::{GlobalConfig, Pool}; @@ -66,34 +65,6 @@ pub fn pump_swap_buy_event_log_decode(data: &[u8]) -> Option { borsh::from_slice::(&data[..PUMP_SWAP_BUY_EVENT_LOG_SIZE]).ok() } -// 使用宏生成UnifiedEvent实现,指定需要合并的字段 -impl_unified_event!( - PumpSwapBuyEvent, - timestamp, - base_amount_out, - max_quote_amount_in, - user_base_token_reserves, - user_quote_token_reserves, - pool_base_token_reserves, - pool_quote_token_reserves, - quote_amount_in, - lp_fee_basis_points, - lp_fee, - protocol_fee_basis_points, - protocol_fee, - quote_amount_in_with_lp_fee, - user_quote_amount_in, - pool, - user, - user_base_token_account, - user_quote_token_account, - protocol_fee_recipient, - protocol_fee_recipient_token_account, - coin_creator, - coin_creator_fee_basis_points, - coin_creator_fee -); - /// 卖出事件 #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)] pub struct PumpSwapSellEvent { @@ -149,34 +120,6 @@ pub fn pump_swap_sell_event_log_decode(data: &[u8]) -> Option borsh::from_slice::(&data[..PUMP_SWAP_SELL_EVENT_LOG_SIZE]).ok() } -// 使用宏生成UnifiedEvent实现,指定需要合并的字段 -impl_unified_event!( - PumpSwapSellEvent, - timestamp, - base_amount_in, - min_quote_amount_out, - user_base_token_reserves, - user_quote_token_reserves, - pool_base_token_reserves, - pool_quote_token_reserves, - quote_amount_out, - lp_fee_basis_points, - lp_fee, - protocol_fee_basis_points, - protocol_fee, - quote_amount_out_without_lp_fee, - user_quote_amount_out, - pool, - user, - user_base_token_account, - user_quote_token_account, - protocol_fee_recipient, - protocol_fee_recipient_token_account, - coin_creator, - coin_creator_fee_basis_points, - coin_creator_fee -); - /// 创建池子事件 #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)] pub struct PumpSwapCreatePoolEvent { @@ -219,30 +162,6 @@ pub fn pump_swap_create_pool_event_log_decode(data: &[u8]) -> Option(&data[..PUMP_SWAP_CREATE_POOL_EVENT_LOG_SIZE]).ok() } -impl_unified_event!( - PumpSwapCreatePoolEvent, - timestamp, - index, - creator, - base_mint, - quote_mint, - base_mint_decimals, - quote_mint_decimals, - base_amount_in, - quote_amount_in, - pool_base_amount, - pool_quote_amount, - minimum_liquidity, - initial_liquidity, - lp_token_amount_out, - pool_bump, - pool, - lp_mint, - user_base_token_account, - user_quote_token_account, - coin_creator -); - /// 存款事件 #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)] pub struct PumpSwapDepositEvent { @@ -283,26 +202,6 @@ pub fn pump_swap_deposit_event_log_decode(data: &[u8]) -> Option(&data[..PUMP_SWAP_DEPOSIT_EVENT_LOG_SIZE]).ok() } -impl_unified_event!( - PumpSwapDepositEvent, - timestamp, - lp_token_amount_out, - max_base_amount_in, - max_quote_amount_in, - user_base_token_reserves, - user_quote_token_reserves, - pool_base_token_reserves, - pool_quote_token_reserves, - base_amount_in, - quote_amount_in, - lp_mint_supply, - pool, - user, - user_base_token_account, - user_quote_token_account, - user_pool_token_account -); - /// 提款事件 #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)] pub struct PumpSwapWithdrawEvent { @@ -343,26 +242,6 @@ pub fn pump_swap_withdraw_event_log_decode(data: &[u8]) -> Option(&data[..PUMP_SWAP_WITHDRAW_EVENT_LOG_SIZE]).ok() } -impl_unified_event!( - PumpSwapWithdrawEvent, - timestamp, - lp_token_amount_in, - min_base_amount_out, - min_quote_amount_out, - user_base_token_reserves, - user_quote_token_reserves, - pool_base_token_reserves, - pool_quote_token_reserves, - base_amount_out, - quote_amount_out, - lp_mint_supply, - pool, - user, - user_base_token_account, - user_quote_token_account, - user_pool_token_account -); - /// 全局配置 #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)] pub struct PumpSwapGlobalConfigAccountEvent { @@ -375,7 +254,6 @@ pub struct PumpSwapGlobalConfigAccountEvent { pub rent_epoch: u64, pub global_config: GlobalConfig, } -impl_unified_event!(PumpSwapGlobalConfigAccountEvent,); /// 池 #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)] @@ -389,7 +267,6 @@ pub struct PumpSwapPoolAccountEvent { pub rent_epoch: u64, pub pool: Pool, } -impl_unified_event!(PumpSwapPoolAccountEvent,); /// 事件鉴别器常量 pub mod discriminators { diff --git a/src/streaming/event_parser/protocols/pumpswap/parser.rs b/src/streaming/event_parser/protocols/pumpswap/parser.rs index 9969008..7b9d2e8 100755 --- a/src/streaming/event_parser/protocols/pumpswap/parser.rs +++ b/src/streaming/event_parser/protocols/pumpswap/parser.rs @@ -1,13 +1,13 @@ use crate::streaming::event_parser::{ common::{read_u64_le, EventMetadata, EventType, ProtocolType}, - core::event_parser::GenericEventParseConfig, + core::GenericEventParseConfig, protocols::pumpswap::{ 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, }, - UnifiedEvent, + DexEvent, }; use solana_sdk::pubkey::Pubkey; @@ -70,24 +70,18 @@ pub const CONFIGS: &[GenericEventParseConfig] = &[ ]; /// 解析买入日志事件 -fn parse_buy_inner_instruction( - data: &[u8], - metadata: EventMetadata, -) -> Option> { +fn parse_buy_inner_instruction(data: &[u8], metadata: EventMetadata) -> Option { if let Some(event) = pump_swap_buy_event_log_decode(data) { - Some(Box::new(PumpSwapBuyEvent { metadata, ..event })) + Some(DexEvent::PumpSwapBuyEvent(PumpSwapBuyEvent { metadata, ..event })) } else { None } } /// 解析卖出日志事件 -fn parse_sell_inner_instruction( - data: &[u8], - metadata: EventMetadata, -) -> Option> { +fn parse_sell_inner_instruction(data: &[u8], metadata: EventMetadata) -> Option { if let Some(event) = pump_swap_sell_event_log_decode(data) { - Some(Box::new(PumpSwapSellEvent { metadata, ..event })) + Some(DexEvent::PumpSwapSellEvent(PumpSwapSellEvent { metadata, ..event })) } else { None } @@ -97,33 +91,27 @@ fn parse_sell_inner_instruction( fn parse_create_pool_inner_instruction( data: &[u8], metadata: EventMetadata, -) -> Option> { +) -> Option { if let Some(event) = pump_swap_create_pool_event_log_decode(data) { - Some(Box::new(PumpSwapCreatePoolEvent { metadata, ..event })) + Some(DexEvent::PumpSwapCreatePoolEvent(PumpSwapCreatePoolEvent { metadata, ..event })) } else { None } } /// 解析存款日志事件 -fn parse_deposit_inner_instruction( - data: &[u8], - metadata: EventMetadata, -) -> Option> { +fn parse_deposit_inner_instruction(data: &[u8], metadata: EventMetadata) -> Option { if let Some(event) = pump_swap_deposit_event_log_decode(data) { - Some(Box::new(PumpSwapDepositEvent { metadata, ..event })) + Some(DexEvent::PumpSwapDepositEvent(PumpSwapDepositEvent { metadata, ..event })) } else { None } } /// 解析提款日志事件 -fn parse_withdraw_inner_instruction( - data: &[u8], - metadata: EventMetadata, -) -> Option> { +fn parse_withdraw_inner_instruction(data: &[u8], metadata: EventMetadata) -> Option { if let Some(event) = pump_swap_withdraw_event_log_decode(data) { - Some(Box::new(PumpSwapWithdrawEvent { metadata, ..event })) + Some(DexEvent::PumpSwapWithdrawEvent(PumpSwapWithdrawEvent { metadata, ..event })) } else { None } @@ -134,7 +122,7 @@ fn parse_buy_instruction( data: &[u8], accounts: &[Pubkey], metadata: EventMetadata, -) -> Option> { +) -> Option { if data.len() < 16 || accounts.len() < 11 { return None; } @@ -142,7 +130,7 @@ fn parse_buy_instruction( let base_amount_out = read_u64_le(data, 0)?; let max_quote_amount_in = read_u64_le(data, 8)?; - Some(Box::new(PumpSwapBuyEvent { + Some(DexEvent::PumpSwapBuyEvent(PumpSwapBuyEvent { metadata, base_amount_out, max_quote_amount_in, @@ -169,7 +157,7 @@ fn parse_sell_instruction( data: &[u8], accounts: &[Pubkey], metadata: EventMetadata, -) -> Option> { +) -> Option { if data.len() < 16 || accounts.len() < 11 { return None; } @@ -177,7 +165,7 @@ fn parse_sell_instruction( let base_amount_in = read_u64_le(data, 0)?; let min_quote_amount_out = read_u64_le(data, 8)?; - Some(Box::new(PumpSwapSellEvent { + Some(DexEvent::PumpSwapSellEvent(PumpSwapSellEvent { metadata, base_amount_in, min_quote_amount_out, @@ -204,7 +192,7 @@ fn parse_create_pool_instruction( data: &[u8], accounts: &[Pubkey], metadata: EventMetadata, -) -> Option> { +) -> Option { if data.len() < 18 || accounts.len() < 11 { return None; } @@ -218,7 +206,7 @@ fn parse_create_pool_instruction( Pubkey::default() }; - Some(Box::new(PumpSwapCreatePoolEvent { + Some(DexEvent::PumpSwapCreatePoolEvent(PumpSwapCreatePoolEvent { metadata, index, base_amount_in, @@ -243,7 +231,7 @@ fn parse_deposit_instruction( data: &[u8], accounts: &[Pubkey], metadata: EventMetadata, -) -> Option> { +) -> Option { if data.len() < 24 || accounts.len() < 11 { return None; } @@ -252,7 +240,7 @@ fn parse_deposit_instruction( let max_base_amount_in = u64::from_le_bytes(data[8..16].try_into().ok()?); let max_quote_amount_in = u64::from_le_bytes(data[16..24].try_into().ok()?); - Some(Box::new(PumpSwapDepositEvent { + Some(DexEvent::PumpSwapDepositEvent(PumpSwapDepositEvent { metadata, lp_token_amount_out, max_base_amount_in, @@ -275,7 +263,7 @@ fn parse_withdraw_instruction( data: &[u8], accounts: &[Pubkey], metadata: EventMetadata, -) -> Option> { +) -> Option { if data.len() < 24 || accounts.len() < 11 { return None; } @@ -284,7 +272,7 @@ fn parse_withdraw_instruction( let min_base_amount_out = u64::from_le_bytes(data[8..16].try_into().ok()?); let min_quote_amount_out = u64::from_le_bytes(data[16..24].try_into().ok()?); - Some(Box::new(PumpSwapWithdrawEvent { + Some(DexEvent::PumpSwapWithdrawEvent(PumpSwapWithdrawEvent { metadata, lp_token_amount_in, min_base_amount_out, diff --git a/src/streaming/event_parser/protocols/pumpswap/types.rs b/src/streaming/event_parser/protocols/pumpswap/types.rs index 64f6039..e66d007 100644 --- a/src/streaming/event_parser/protocols/pumpswap/types.rs +++ b/src/streaming/event_parser/protocols/pumpswap/types.rs @@ -5,10 +5,8 @@ use solana_sdk::pubkey::Pubkey; use crate::streaming::{ event_parser::{ common::EventMetadata, - protocols::pumpswap::{ - PumpSwapGlobalConfigAccountEvent, PumpSwapPoolAccountEvent, - }, - UnifiedEvent, + protocols::pumpswap::{PumpSwapGlobalConfigAccountEvent, PumpSwapPoolAccountEvent}, + DexEvent, }, grpc::AccountPretty, }; @@ -36,12 +34,12 @@ pub fn global_config_decode(data: &[u8]) -> Option { pub fn global_config_parser( account: &AccountPretty, metadata: EventMetadata, -) -> Option> { +) -> Option { if account.data.len() < GLOBAL_CONFIG_SIZE + 8 { return None; } if let Some(config) = global_config_decode(&account.data[8..GLOBAL_CONFIG_SIZE + 8]) { - Some(Box::new(PumpSwapGlobalConfigAccountEvent { + Some(DexEvent::PumpSwapGlobalConfigAccountEvent(PumpSwapGlobalConfigAccountEvent { metadata, pubkey: account.pubkey, executable: account.executable, @@ -78,15 +76,12 @@ pub fn pool_decode(data: &[u8]) -> Option { borsh::from_slice::(&data[..POOL_SIZE]).ok() } -pub fn pool_parser( - account: &AccountPretty, - metadata: EventMetadata, -) -> Option> { +pub fn pool_parser(account: &AccountPretty, metadata: EventMetadata) -> Option { if account.data.len() < POOL_SIZE + 8 { return None; } if let Some(pool) = pool_decode(&account.data[8..POOL_SIZE + 8]) { - Some(Box::new(PumpSwapPoolAccountEvent { + Some(DexEvent::PumpSwapPoolAccountEvent(PumpSwapPoolAccountEvent { metadata, pubkey: account.pubkey, executable: account.executable, diff --git a/src/streaming/event_parser/protocols/raydium_amm_v4/events.rs b/src/streaming/event_parser/protocols/raydium_amm_v4/events.rs index 9f82f6f..e28e1f1 100755 --- a/src/streaming/event_parser/protocols/raydium_amm_v4/events.rs +++ b/src/streaming/event_parser/protocols/raydium_amm_v4/events.rs @@ -1,6 +1,6 @@ use crate::streaming::event_parser::common::EventMetadata; use crate::{ - impl_unified_event, streaming::event_parser::protocols::raydium_amm_v4::types::AmmInfo, + streaming::event_parser::protocols::raydium_amm_v4::types::AmmInfo, }; use borsh::BorshDeserialize; use serde::{Deserialize, Serialize}; @@ -38,8 +38,6 @@ pub struct RaydiumAmmV4SwapEvent { pub user_source_owner: Pubkey, } -impl_unified_event!(RaydiumAmmV4SwapEvent,); - /// 添加流动性 #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)] pub struct RaydiumAmmV4DepositEvent { @@ -64,7 +62,6 @@ pub struct RaydiumAmmV4DepositEvent { pub user_owner: Pubkey, pub serum_event_queue: Pubkey, } -impl_unified_event!(RaydiumAmmV4DepositEvent,); /// 初始化 #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)] @@ -98,7 +95,6 @@ pub struct RaydiumAmmV4Initialize2Event { pub user_token_pc: Pubkey, pub user_lp_token_account: Pubkey, } -impl_unified_event!(RaydiumAmmV4Initialize2Event,); /// 移除流动性 #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)] @@ -130,7 +126,6 @@ pub struct RaydiumAmmV4WithdrawEvent { pub serum_bids: Pubkey, pub serum_asks: Pubkey, } -impl_unified_event!(RaydiumAmmV4WithdrawEvent,); /// 提现 #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)] @@ -156,7 +151,6 @@ pub struct RaydiumAmmV4WithdrawPnlEvent { pub serum_pc_vault_account: Pubkey, pub serum_vault_signer: Pubkey, } -impl_unified_event!(RaydiumAmmV4WithdrawPnlEvent,); /// 池信息 #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)] @@ -170,7 +164,6 @@ pub struct RaydiumAmmV4AmmInfoAccountEvent { pub rent_epoch: u64, pub amm_info: AmmInfo, } -impl_unified_event!(RaydiumAmmV4AmmInfoAccountEvent,); /// 事件鉴别器常量 pub mod discriminators { diff --git a/src/streaming/event_parser/protocols/raydium_amm_v4/parser.rs b/src/streaming/event_parser/protocols/raydium_amm_v4/parser.rs index 832e6d8..d19fb2c 100755 --- a/src/streaming/event_parser/protocols/raydium_amm_v4/parser.rs +++ b/src/streaming/event_parser/protocols/raydium_amm_v4/parser.rs @@ -1,11 +1,11 @@ use crate::streaming::event_parser::{ common::{read_u64_le, EventMetadata, EventType, ProtocolType}, - core::event_parser::GenericEventParseConfig, + core::GenericEventParseConfig, protocols::raydium_amm_v4::{ discriminators, RaydiumAmmV4DepositEvent, RaydiumAmmV4Initialize2Event, RaydiumAmmV4SwapEvent, RaydiumAmmV4WithdrawEvent, RaydiumAmmV4WithdrawPnlEvent, }, - UnifiedEvent, + DexEvent, }; use solana_sdk::pubkey::Pubkey; @@ -82,12 +82,12 @@ fn parse_withdraw_pnl_instruction( _data: &[u8], accounts: &[Pubkey], metadata: EventMetadata, -) -> Option> { +) -> Option { if accounts.len() < 17 { return None; } - Some(Box::new(RaydiumAmmV4WithdrawPnlEvent { + Some(DexEvent::RaydiumAmmV4WithdrawPnlEvent(RaydiumAmmV4WithdrawPnlEvent { metadata, token_program: accounts[0], amm: accounts[1], @@ -114,13 +114,13 @@ fn parse_withdraw_instruction( data: &[u8], accounts: &[Pubkey], metadata: EventMetadata, -) -> Option> { +) -> Option { if data.len() < 8 || accounts.len() < 22 { return None; } let amount = read_u64_le(data, 0)?; - Some(Box::new(RaydiumAmmV4WithdrawEvent { + Some(DexEvent::RaydiumAmmV4WithdrawEvent(RaydiumAmmV4WithdrawEvent { metadata, amount, @@ -154,7 +154,7 @@ fn parse_initialize2_instruction( data: &[u8], accounts: &[Pubkey], metadata: EventMetadata, -) -> Option> { +) -> Option { if data.len() < 25 || accounts.len() < 21 { return None; } @@ -163,7 +163,7 @@ fn parse_initialize2_instruction( let init_pc_amount = read_u64_le(data, 9)?; let init_coin_amount = read_u64_le(data, 17)?; - Some(Box::new(RaydiumAmmV4Initialize2Event { + Some(DexEvent::RaydiumAmmV4Initialize2Event(RaydiumAmmV4Initialize2Event { metadata, nonce, open_time, @@ -199,7 +199,7 @@ fn parse_deposit_instruction( data: &[u8], accounts: &[Pubkey], metadata: EventMetadata, -) -> Option> { +) -> Option { if data.len() < 24 || accounts.len() < 14 { return None; } @@ -207,7 +207,7 @@ fn parse_deposit_instruction( let max_pc_amount = read_u64_le(data, 8)?; let base_side = read_u64_le(data, 16)?; - Some(Box::new(RaydiumAmmV4DepositEvent { + Some(DexEvent::RaydiumAmmV4DepositEvent(RaydiumAmmV4DepositEvent { metadata, max_coin_amount, max_pc_amount, @@ -235,7 +235,7 @@ fn parse_swap_base_output_instruction( data: &[u8], accounts: &[Pubkey], metadata: EventMetadata, -) -> Option> { +) -> Option { if data.len() < 16 || accounts.len() < 17 { return None; } @@ -249,7 +249,7 @@ fn parse_swap_base_output_instruction( accounts.insert(4, Pubkey::default()); } - Some(Box::new(RaydiumAmmV4SwapEvent { + Some(DexEvent::RaydiumAmmV4SwapEvent(RaydiumAmmV4SwapEvent { metadata, max_amount_in, amount_out, @@ -282,7 +282,7 @@ fn parse_swap_base_input_instruction( data: &[u8], accounts: &[Pubkey], metadata: EventMetadata, -) -> Option> { +) -> Option { if data.len() < 16 || accounts.len() < 17 { return None; } @@ -296,7 +296,7 @@ fn parse_swap_base_input_instruction( accounts.insert(4, Pubkey::default()); } - Some(Box::new(RaydiumAmmV4SwapEvent { + Some(DexEvent::RaydiumAmmV4SwapEvent(RaydiumAmmV4SwapEvent { metadata, amount_in, minimum_amount_out, diff --git a/src/streaming/event_parser/protocols/raydium_amm_v4/types.rs b/src/streaming/event_parser/protocols/raydium_amm_v4/types.rs index e2f3078..ba8d7ce 100644 --- a/src/streaming/event_parser/protocols/raydium_amm_v4/types.rs +++ b/src/streaming/event_parser/protocols/raydium_amm_v4/types.rs @@ -5,7 +5,7 @@ use solana_sdk::pubkey::Pubkey; use crate::streaming::{ event_parser::{ common::EventMetadata, protocols::raydium_amm_v4::RaydiumAmmV4AmmInfoAccountEvent, - UnifiedEvent, + DexEvent, }, grpc::AccountPretty, }; @@ -86,15 +86,12 @@ pub fn amm_info_decode(data: &[u8]) -> Option { borsh::from_slice::(&data[..AMM_INFO_SIZE]).ok() } -pub fn amm_info_parser( - account: &AccountPretty, - metadata: EventMetadata, -) -> Option> { +pub fn amm_info_parser(account: &AccountPretty, metadata: EventMetadata) -> Option { if account.data.len() < AMM_INFO_SIZE { return None; } if let Some(amm_info) = amm_info_decode(&account.data[..AMM_INFO_SIZE]) { - Some(Box::new(RaydiumAmmV4AmmInfoAccountEvent { + Some(DexEvent::RaydiumAmmV4AmmInfoAccountEvent(RaydiumAmmV4AmmInfoAccountEvent { metadata, pubkey: account.pubkey, executable: account.executable, diff --git a/src/streaming/event_parser/protocols/raydium_clmm/events.rs b/src/streaming/event_parser/protocols/raydium_clmm/events.rs index dd405cf..492fa57 100755 --- a/src/streaming/event_parser/protocols/raydium_clmm/events.rs +++ b/src/streaming/event_parser/protocols/raydium_clmm/events.rs @@ -1,7 +1,7 @@ use crate::streaming::event_parser::common::EventMetadata; use crate::streaming::event_parser::protocols::raydium_clmm::types::{PoolState, TickArrayState}; use crate::{ - impl_unified_event, streaming::event_parser::protocols::raydium_clmm::types::AmmConfig, + streaming::event_parser::protocols::raydium_clmm::types::AmmConfig, }; use serde::{Deserialize, Serialize}; use solana_sdk::pubkey::Pubkey; @@ -27,7 +27,6 @@ pub struct RaydiumClmmSwapEvent { pub remaining_accounts: Vec, } -impl_unified_event!(RaydiumClmmSwapEvent,); /// 交易v2 #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] @@ -52,7 +51,6 @@ pub struct RaydiumClmmSwapV2Event { pub output_vault_mint: Pubkey, pub remaining_accounts: Vec, } -impl_unified_event!(RaydiumClmmSwapV2Event,); /// 关闭仓位 #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] @@ -65,7 +63,6 @@ pub struct RaydiumClmmClosePositionEvent { pub system_program: Pubkey, pub token_program: Pubkey, } -impl_unified_event!(RaydiumClmmClosePositionEvent,); /// 减少流动性v2 #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] @@ -92,7 +89,6 @@ pub struct RaydiumClmmDecreaseLiquidityV2Event { pub vault1_mint: Pubkey, pub remaining_accounts: Vec, } -impl_unified_event!(RaydiumClmmDecreaseLiquidityV2Event,); /// 创建池 #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] @@ -114,7 +110,6 @@ pub struct RaydiumClmmCreatePoolEvent { pub system_program: Pubkey, pub rent: Pubkey, } -impl_unified_event!(RaydiumClmmCreatePoolEvent,); /// 增加流动性v2 #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] @@ -140,7 +135,6 @@ pub struct RaydiumClmmIncreaseLiquidityV2Event { pub vault0_mint: Pubkey, pub vault1_mint: Pubkey, } -impl_unified_event!(RaydiumClmmIncreaseLiquidityV2Event,); /// 打开仓位v2 #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] @@ -177,7 +171,6 @@ pub struct RaydiumClmmOpenPositionWithToken22NftEvent { pub vault0_mint: Pubkey, pub vault1_mint: Pubkey, } -impl_unified_event!(RaydiumClmmOpenPositionWithToken22NftEvent,); /// 打开仓位V2 #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] @@ -217,7 +210,6 @@ pub struct RaydiumClmmOpenPositionV2Event { pub vault1_mint: Pubkey, pub remaining_accounts: Vec, } -impl_unified_event!(RaydiumClmmOpenPositionV2Event,); /// 池配置 #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] @@ -230,7 +222,6 @@ pub struct RaydiumClmmAmmConfigAccountEvent { pub rent_epoch: u64, pub amm_config: AmmConfig, } -impl_unified_event!(RaydiumClmmAmmConfigAccountEvent,); /// 池状态 #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] @@ -243,7 +234,6 @@ pub struct RaydiumClmmPoolStateAccountEvent { pub rent_epoch: u64, pub pool_state: PoolState, } -impl_unified_event!(RaydiumClmmPoolStateAccountEvent,); /// 池状态 #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] @@ -256,7 +246,6 @@ pub struct RaydiumClmmTickArrayStateAccountEvent { pub rent_epoch: u64, pub tick_array_state: TickArrayState, } -impl_unified_event!(RaydiumClmmTickArrayStateAccountEvent,); /// 事件鉴别器常量 pub mod discriminators { diff --git a/src/streaming/event_parser/protocols/raydium_clmm/parser.rs b/src/streaming/event_parser/protocols/raydium_clmm/parser.rs index 7636119..8d7e9cf 100755 --- a/src/streaming/event_parser/protocols/raydium_clmm/parser.rs +++ b/src/streaming/event_parser/protocols/raydium_clmm/parser.rs @@ -3,14 +3,14 @@ use crate::streaming::event_parser::{ read_i32_le, read_option_bool, read_u128_le, read_u64_le, read_u8_le, EventMetadata, EventType, ProtocolType, }, - core::event_parser::GenericEventParseConfig, + core::GenericEventParseConfig, protocols::raydium_clmm::{ discriminators, RaydiumClmmClosePositionEvent, RaydiumClmmCreatePoolEvent, RaydiumClmmDecreaseLiquidityV2Event, RaydiumClmmIncreaseLiquidityV2Event, RaydiumClmmOpenPositionV2Event, RaydiumClmmOpenPositionWithToken22NftEvent, RaydiumClmmSwapEvent, RaydiumClmmSwapV2Event, }, - UnifiedEvent, + DexEvent, }; use solana_sdk::pubkey::Pubkey; @@ -107,11 +107,11 @@ fn parse_open_position_v2_instruction( data: &[u8], accounts: &[Pubkey], metadata: EventMetadata, -) -> Option> { +) -> Option { if data.len() < 51 || accounts.len() < 22 { return None; } - Some(Box::new(RaydiumClmmOpenPositionV2Event { + Some(DexEvent::RaydiumClmmOpenPositionV2Event(RaydiumClmmOpenPositionV2Event { metadata, tick_lower_index: read_i32_le(data, 0)?, tick_upper_index: read_i32_le(data, 4)?, @@ -153,42 +153,44 @@ fn parse_open_position_with_token_22_nft_instruction( data: &[u8], accounts: &[Pubkey], metadata: EventMetadata, -) -> Option> { +) -> Option { if data.len() < 51 || accounts.len() < 20 { return None; } - Some(Box::new(RaydiumClmmOpenPositionWithToken22NftEvent { - metadata, - tick_lower_index: read_i32_le(data, 0)?, - tick_upper_index: read_i32_le(data, 4)?, - tick_array_lower_start_index: read_i32_le(data, 8)?, - tick_array_upper_start_index: read_i32_le(data, 12)?, - liquidity: read_u128_le(data, 16)?, - amount0_max: read_u64_le(data, 32)?, - amount1_max: read_u64_le(data, 40)?, - with_metadata: read_u8_le(data, 48)? == 1, - base_flag: read_option_bool(data, &mut 49)?, - payer: accounts[0], - position_nft_owner: accounts[1], - position_nft_mint: accounts[2], - position_nft_account: accounts[3], - pool_state: accounts[4], - protocol_position: accounts[5], - tick_array_lower: accounts[6], - tick_array_upper: accounts[7], - personal_position: accounts[8], - token_account0: accounts[9], - token_account1: accounts[10], - token_vault0: accounts[11], - token_vault1: accounts[12], - rent: accounts[13], - system_program: accounts[14], - token_program: accounts[15], - associated_token_program: accounts[16], - token_program2022: accounts[17], - vault0_mint: accounts[18], - vault1_mint: accounts[19], - })) + Some(DexEvent::RaydiumClmmOpenPositionWithToken22NftEvent( + RaydiumClmmOpenPositionWithToken22NftEvent { + metadata, + tick_lower_index: read_i32_le(data, 0)?, + tick_upper_index: read_i32_le(data, 4)?, + tick_array_lower_start_index: read_i32_le(data, 8)?, + tick_array_upper_start_index: read_i32_le(data, 12)?, + liquidity: read_u128_le(data, 16)?, + amount0_max: read_u64_le(data, 32)?, + amount1_max: read_u64_le(data, 40)?, + with_metadata: read_u8_le(data, 48)? == 1, + base_flag: read_option_bool(data, &mut 49)?, + payer: accounts[0], + position_nft_owner: accounts[1], + position_nft_mint: accounts[2], + position_nft_account: accounts[3], + pool_state: accounts[4], + protocol_position: accounts[5], + tick_array_lower: accounts[6], + tick_array_upper: accounts[7], + personal_position: accounts[8], + token_account0: accounts[9], + token_account1: accounts[10], + token_vault0: accounts[11], + token_vault1: accounts[12], + rent: accounts[13], + system_program: accounts[14], + token_program: accounts[15], + associated_token_program: accounts[16], + token_program2022: accounts[17], + vault0_mint: accounts[18], + vault1_mint: accounts[19], + }, + )) } /// 解析增加流动性v2指令事件 @@ -196,11 +198,11 @@ fn parse_increase_liquidity_v2_instruction( data: &[u8], accounts: &[Pubkey], metadata: EventMetadata, -) -> Option> { +) -> Option { if data.len() < 34 || accounts.len() < 15 { return None; } - Some(Box::new(RaydiumClmmIncreaseLiquidityV2Event { + Some(DexEvent::RaydiumClmmIncreaseLiquidityV2Event(RaydiumClmmIncreaseLiquidityV2Event { metadata, liquidity: read_u128_le(data, 0)?, amount0_max: read_u64_le(data, 16)?, @@ -229,11 +231,11 @@ fn parse_create_pool_instruction( data: &[u8], accounts: &[Pubkey], metadata: EventMetadata, -) -> Option> { +) -> Option { if data.len() < 24 || accounts.len() < 13 { return None; } - Some(Box::new(RaydiumClmmCreatePoolEvent { + Some(DexEvent::RaydiumClmmCreatePoolEvent(RaydiumClmmCreatePoolEvent { metadata, sqrt_price_x64: read_u128_le(data, 0)?, open_time: read_u64_le(data, 16)?, @@ -258,11 +260,11 @@ fn parse_decrease_liquidity_v2_instruction( data: &[u8], accounts: &[Pubkey], metadata: EventMetadata, -) -> Option> { +) -> Option { if data.len() < 32 || accounts.len() < 16 { return None; } - Some(Box::new(RaydiumClmmDecreaseLiquidityV2Event { + Some(DexEvent::RaydiumClmmDecreaseLiquidityV2Event(RaydiumClmmDecreaseLiquidityV2Event { metadata, liquidity: read_u128_le(data, 0)?, amount0_min: read_u64_le(data, 16)?, @@ -292,11 +294,11 @@ fn parse_close_position_instruction( _data: &[u8], accounts: &[Pubkey], metadata: EventMetadata, -) -> Option> { +) -> Option { if accounts.len() < 6 { return None; } - Some(Box::new(RaydiumClmmClosePositionEvent { + Some(DexEvent::RaydiumClmmClosePositionEvent(RaydiumClmmClosePositionEvent { metadata, nft_owner: accounts[0], position_nft_mint: accounts[1], @@ -312,7 +314,7 @@ fn parse_swap_instruction( data: &[u8], accounts: &[Pubkey], metadata: EventMetadata, -) -> Option> { +) -> Option { if data.len() < 33 || accounts.len() < 10 { return None; } @@ -322,7 +324,7 @@ fn parse_swap_instruction( let sqrt_price_limit_x64 = read_u128_le(data, 16)?; let is_base_input = read_u8_le(data, 32)?; - Some(Box::new(RaydiumClmmSwapEvent { + Some(DexEvent::RaydiumClmmSwapEvent(RaydiumClmmSwapEvent { metadata, amount, other_amount_threshold, @@ -346,7 +348,7 @@ fn parse_swap_v2_instruction( data: &[u8], accounts: &[Pubkey], metadata: EventMetadata, -) -> Option> { +) -> Option { if data.len() < 33 || accounts.len() < 13 { return None; } @@ -356,7 +358,7 @@ fn parse_swap_v2_instruction( let sqrt_price_limit_x64 = read_u128_le(data, 16)?; let is_base_input = read_u8_le(data, 32)?; - Some(Box::new(RaydiumClmmSwapV2Event { + Some(DexEvent::RaydiumClmmSwapV2Event(RaydiumClmmSwapV2Event { metadata, amount, other_amount_threshold, diff --git a/src/streaming/event_parser/protocols/raydium_clmm/types.rs b/src/streaming/event_parser/protocols/raydium_clmm/types.rs index c8a8803..a3731b5 100644 --- a/src/streaming/event_parser/protocols/raydium_clmm/types.rs +++ b/src/streaming/event_parser/protocols/raydium_clmm/types.rs @@ -9,7 +9,7 @@ use crate::streaming::{ RaydiumClmmAmmConfigAccountEvent, RaydiumClmmPoolStateAccountEvent, RaydiumClmmTickArrayStateAccountEvent, }, - UnifiedEvent, + DexEvent, }, grpc::AccountPretty, }; @@ -37,15 +37,12 @@ pub fn amm_config_decode(data: &[u8]) -> Option { borsh::from_slice::(&data[..AMM_CONFIG_SIZE]).ok() } -pub fn amm_config_parser( - account: &AccountPretty, - metadata: EventMetadata, -) -> Option> { +pub fn amm_config_parser(account: &AccountPretty, metadata: EventMetadata) -> Option { if account.data.len() < AMM_CONFIG_SIZE + 8 { return None; } if let Some(amm_config) = amm_config_decode(&account.data[8..AMM_CONFIG_SIZE + 8]) { - Some(Box::new(RaydiumClmmAmmConfigAccountEvent { + Some(DexEvent::RaydiumClmmAmmConfigAccountEvent(RaydiumClmmAmmConfigAccountEvent { metadata, pubkey: account.pubkey, executable: account.executable, @@ -125,15 +122,12 @@ pub fn pool_state_decode(data: &[u8]) -> Option { borsh::from_slice::(&data[..POOL_STATE_SIZE]).ok() } -pub fn pool_state_parser( - account: &AccountPretty, - metadata: EventMetadata, -) -> Option> { +pub fn pool_state_parser(account: &AccountPretty, metadata: EventMetadata) -> Option { if account.data.len() < POOL_STATE_SIZE + 8 { return None; } if let Some(pool_state) = pool_state_decode(&account.data[8..POOL_STATE_SIZE + 8]) { - Some(Box::new(RaydiumClmmPoolStateAccountEvent { + Some(DexEvent::RaydiumClmmPoolStateAccountEvent(RaydiumClmmPoolStateAccountEvent { metadata, pubkey: account.pubkey, executable: account.executable, @@ -209,22 +203,24 @@ pub fn tick_array_state_decode(data: &[u8]) -> Option { pub fn tick_array_state_parser( account: &AccountPretty, metadata: EventMetadata, -) -> Option> { +) -> Option { if account.data.len() < TICK_ARRAY_STATE_SIZE + 8 { return None; } if let Some(tick_array_state) = tick_array_state_decode(&account.data[8..TICK_ARRAY_STATE_SIZE + 8]) { - Some(Box::new(RaydiumClmmTickArrayStateAccountEvent { - metadata, - pubkey: account.pubkey, - executable: account.executable, - lamports: account.lamports, - owner: account.owner, - rent_epoch: account.rent_epoch, - tick_array_state: tick_array_state, - })) + Some(DexEvent::RaydiumClmmTickArrayStateAccountEvent( + RaydiumClmmTickArrayStateAccountEvent { + metadata, + pubkey: account.pubkey, + executable: account.executable, + lamports: account.lamports, + owner: account.owner, + rent_epoch: account.rent_epoch, + tick_array_state: tick_array_state, + }, + )) } else { None } diff --git a/src/streaming/event_parser/protocols/raydium_cpmm/events.rs b/src/streaming/event_parser/protocols/raydium_cpmm/events.rs index d8c1b7a..c131f23 100755 --- a/src/streaming/event_parser/protocols/raydium_cpmm/events.rs +++ b/src/streaming/event_parser/protocols/raydium_cpmm/events.rs @@ -1,7 +1,7 @@ use crate::streaming::event_parser::common::EventMetadata; use crate::streaming::event_parser::protocols::raydium_cpmm::types::PoolState; use crate::{ - impl_unified_event, streaming::event_parser::protocols::raydium_cpmm::types::AmmConfig, + streaming::event_parser::protocols::raydium_cpmm::types::AmmConfig, }; use borsh::BorshDeserialize; use serde::{Deserialize, Serialize}; @@ -31,7 +31,6 @@ pub struct RaydiumCpmmSwapEvent { pub observation_state: Pubkey, } -impl_unified_event!(RaydiumCpmmSwapEvent,); /// 存款 #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)] @@ -56,7 +55,6 @@ pub struct RaydiumCpmmDepositEvent { pub vault1_mint: Pubkey, pub lp_mint: Pubkey, } -impl_unified_event!(RaydiumCpmmDepositEvent,); /// 初始化 #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)] @@ -88,7 +86,6 @@ pub struct RaydiumCpmmInitializeEvent { pub system_program: Pubkey, pub rent: Pubkey, } -impl_unified_event!(RaydiumCpmmInitializeEvent,); /// 提款 #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)] @@ -114,7 +111,6 @@ pub struct RaydiumCpmmWithdrawEvent { pub lp_mint: Pubkey, pub memo_program: Pubkey, } -impl_unified_event!(RaydiumCpmmWithdrawEvent,); /// 池配置 #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)] @@ -128,7 +124,6 @@ pub struct RaydiumCpmmAmmConfigAccountEvent { pub rent_epoch: u64, pub amm_config: AmmConfig, } -impl_unified_event!(RaydiumCpmmAmmConfigAccountEvent,); /// 池状态 #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)] @@ -142,7 +137,6 @@ pub struct RaydiumCpmmPoolStateAccountEvent { pub rent_epoch: u64, pub pool_state: PoolState, } -impl_unified_event!(RaydiumCpmmPoolStateAccountEvent,); /// 事件鉴别器常量 pub mod discriminators { diff --git a/src/streaming/event_parser/protocols/raydium_cpmm/parser.rs b/src/streaming/event_parser/protocols/raydium_cpmm/parser.rs index cb852a8..2e59eb1 100755 --- a/src/streaming/event_parser/protocols/raydium_cpmm/parser.rs +++ b/src/streaming/event_parser/protocols/raydium_cpmm/parser.rs @@ -2,12 +2,12 @@ use solana_sdk::pubkey::Pubkey; use crate::streaming::event_parser::{ common::{read_u64_le, EventMetadata, EventType, ProtocolType}, - core::event_parser::GenericEventParseConfig, + core::GenericEventParseConfig, protocols::raydium_cpmm::{ discriminators, RaydiumCpmmDepositEvent, RaydiumCpmmInitializeEvent, RaydiumCpmmSwapEvent, RaydiumCpmmWithdrawEvent, }, - UnifiedEvent, + DexEvent, }; /// Raydium CPMM程序ID @@ -73,11 +73,11 @@ fn parse_withdraw_instruction( data: &[u8], accounts: &[Pubkey], metadata: EventMetadata, -) -> Option> { +) -> Option { if data.len() < 24 || accounts.len() < 14 { return None; } - Some(Box::new(RaydiumCpmmWithdrawEvent { + Some(DexEvent::RaydiumCpmmWithdrawEvent(RaydiumCpmmWithdrawEvent { metadata, lp_token_amount: read_u64_le(data, 0)?, minimum_token0_amount: read_u64_le(data, 8)?, @@ -104,11 +104,11 @@ fn parse_initialize_instruction( data: &[u8], accounts: &[Pubkey], metadata: EventMetadata, -) -> Option> { +) -> Option { if data.len() < 24 || accounts.len() < 20 { return None; } - Some(Box::new(RaydiumCpmmInitializeEvent { + Some(DexEvent::RaydiumCpmmInitializeEvent(RaydiumCpmmInitializeEvent { metadata, init_amount0: read_u64_le(data, 0)?, init_amount1: read_u64_le(data, 8)?, @@ -141,11 +141,11 @@ fn parse_deposit_instruction( data: &[u8], accounts: &[Pubkey], metadata: EventMetadata, -) -> Option> { +) -> Option { if data.len() < 24 || accounts.len() < 13 { return None; } - Some(Box::new(RaydiumCpmmDepositEvent { + Some(DexEvent::RaydiumCpmmDepositEvent(RaydiumCpmmDepositEvent { metadata, lp_token_amount: read_u64_le(data, 0)?, maximum_token0_amount: read_u64_le(data, 8)?, @@ -171,7 +171,7 @@ fn parse_swap_base_input_instruction( data: &[u8], accounts: &[Pubkey], metadata: EventMetadata, -) -> Option> { +) -> Option { if data.len() < 16 || accounts.len() < 13 { return None; } @@ -179,7 +179,7 @@ fn parse_swap_base_input_instruction( let amount_in = read_u64_le(data, 0)?; let minimum_amount_out = read_u64_le(data, 8)?; - Some(Box::new(RaydiumCpmmSwapEvent { + Some(DexEvent::RaydiumCpmmSwapEvent(RaydiumCpmmSwapEvent { metadata, amount_in, minimum_amount_out, @@ -204,7 +204,7 @@ fn parse_swap_base_output_instruction( data: &[u8], accounts: &[Pubkey], metadata: EventMetadata, -) -> Option> { +) -> Option { if data.len() < 16 || accounts.len() < 13 { return None; } @@ -212,7 +212,7 @@ fn parse_swap_base_output_instruction( let max_amount_in = read_u64_le(data, 0)?; let amount_out = read_u64_le(data, 8)?; - Some(Box::new(RaydiumCpmmSwapEvent { + Some(DexEvent::RaydiumCpmmSwapEvent(RaydiumCpmmSwapEvent { metadata, max_amount_in, amount_out, diff --git a/src/streaming/event_parser/protocols/raydium_cpmm/types.rs b/src/streaming/event_parser/protocols/raydium_cpmm/types.rs index 532690f..21601a2 100644 --- a/src/streaming/event_parser/protocols/raydium_cpmm/types.rs +++ b/src/streaming/event_parser/protocols/raydium_cpmm/types.rs @@ -8,7 +8,7 @@ use crate::streaming::{ protocols::raydium_cpmm::{ RaydiumCpmmAmmConfigAccountEvent, RaydiumCpmmPoolStateAccountEvent, }, - UnifiedEvent, + DexEvent, }, grpc::AccountPretty, }; @@ -36,15 +36,12 @@ pub fn amm_config_decode(data: &[u8]) -> Option { borsh::from_slice::(&data[..AMM_CONFIG_SIZE]).ok() } -pub fn amm_config_parser( - account: &AccountPretty, - metadata: EventMetadata, -) -> Option> { +pub fn amm_config_parser(account: &AccountPretty, metadata: EventMetadata) -> Option { if account.data.len() < AMM_CONFIG_SIZE + 8 { return None; } if let Some(amm_config) = amm_config_decode(&account.data[8..AMM_CONFIG_SIZE + 8]) { - Some(Box::new(RaydiumCpmmAmmConfigAccountEvent { + Some(DexEvent::RaydiumCpmmAmmConfigAccountEvent(RaydiumCpmmAmmConfigAccountEvent { metadata, pubkey: account.pubkey, executable: account.executable, @@ -94,15 +91,12 @@ pub fn pool_state_decode(data: &[u8]) -> Option { borsh::from_slice::(&data[..POOL_STATE_SIZE]).ok() } -pub fn pool_state_parser( - account: &AccountPretty, - metadata: EventMetadata, -) -> Option> { +pub fn pool_state_parser(account: &AccountPretty, metadata: EventMetadata) -> Option { if account.data.len() < POOL_STATE_SIZE + 8 { return None; } if let Some(pool_state) = pool_state_decode(&account.data[8..POOL_STATE_SIZE + 8]) { - Some(Box::new(RaydiumCpmmPoolStateAccountEvent { + Some(DexEvent::RaydiumCpmmPoolStateAccountEvent(RaydiumCpmmPoolStateAccountEvent { metadata, pubkey: account.pubkey, executable: account.executable, diff --git a/src/streaming/grpc/mod.rs b/src/streaming/grpc/mod.rs index 6618f4e..842e798 100644 --- a/src/streaming/grpc/mod.rs +++ b/src/streaming/grpc/mod.rs @@ -12,6 +12,5 @@ pub use types::*; // 从公用模块重新导出 pub use crate::streaming::common::{ - BackpressureConfig, BackpressureStrategy, ConnectionConfig, MetricsManager, PerformanceMetrics, - StreamClientConfig as ClientConfig, + ConnectionConfig, MetricsManager, PerformanceMetrics, StreamClientConfig as ClientConfig, }; diff --git a/src/streaming/shred/connection.rs b/src/streaming/shred/connection.rs index 951993c..8013930 100644 --- a/src/streaming/shred/connection.rs +++ b/src/streaming/shred/connection.rs @@ -1,5 +1,4 @@ use std::sync::Arc; -use std::sync::RwLock; use tokio::sync::Mutex; use tonic::transport::Channel; @@ -14,8 +13,6 @@ use crate::streaming::common::{ pub struct ShredStreamGrpc { pub shredstream_client: Arc>, pub config: StreamClientConfig, - pub metrics: Arc>, - pub metrics_manager: MetricsManager, pub subscription_handle: Arc>>, } @@ -28,38 +25,14 @@ impl ShredStreamGrpc { /// 创建客户端,使用自定义配置 pub async fn new_with_config(endpoint: String, config: StreamClientConfig) -> AnyResult { let shredstream_client = ShredstreamProxyClient::connect(endpoint.clone()).await?; - let metrics = Arc::new(RwLock::new(PerformanceMetrics::new())); - - let metrics_manager = MetricsManager::new(config.enable_metrics, "ShredStream".to_string()); - + MetricsManager::init(config.enable_metrics); Ok(Self { shredstream_client: Arc::new(shredstream_client), config, - metrics: metrics.clone(), - metrics_manager, subscription_handle: Arc::new(Mutex::new(None)), }) } - /// Creates a new ShredStreamClient with high-throughput configuration. - /// - /// This is a convenience method that creates a client optimized for high-concurrency scenarios - /// where throughput is prioritized over latency. See `StreamClientConfig::high_throughput()` - /// for detailed configuration information. - pub async fn new_high_throughput(endpoint: String) -> AnyResult { - Self::new_with_config(endpoint, StreamClientConfig::high_throughput()).await - } - - /// Creates a new ShredStreamClient with low-latency configuration. - /// - /// This is a convenience method that creates a client optimized for real-time scenarios - /// where latency is prioritized over throughput. See `StreamClientConfig::low_latency()` - /// for detailed configuration information. - pub async fn new_low_latency(endpoint: String) -> AnyResult { - Self::new_with_config(endpoint, StreamClientConfig::low_latency()).await - } - - /// 获取当前配置 pub fn get_config(&self) -> &StreamClientConfig { &self.config @@ -72,7 +45,7 @@ impl ShredStreamGrpc { /// 获取性能指标 pub fn get_metrics(&self) -> PerformanceMetrics { - self.metrics_manager.get_metrics() + MetricsManager::global().get_metrics() } /// 启用或禁用性能监控 @@ -82,12 +55,12 @@ impl ShredStreamGrpc { /// 打印性能指标 pub fn print_metrics(&self) { - self.metrics_manager.print_metrics(); + MetricsManager::global().print_metrics(); } /// 启动自动性能监控任务 pub async fn start_auto_metrics_monitoring(&self) { - self.metrics_manager.start_auto_monitoring().await; + MetricsManager::global().start_auto_monitoring().await; } /// 停止当前订阅 diff --git a/src/streaming/shred/mod.rs b/src/streaming/shred/mod.rs index cd7ce22..622225b 100644 --- a/src/streaming/shred/mod.rs +++ b/src/streaming/shred/mod.rs @@ -10,6 +10,5 @@ pub use types::*; // 从公用模块重新导出 pub use crate::streaming::common::{ - BackpressureConfig, BackpressureStrategy, ConnectionConfig, MetricsEventType, MetricsManager, - PerformanceMetrics, StreamClientConfig, + ConnectionConfig, MetricsEventType, MetricsManager, PerformanceMetrics, StreamClientConfig, }; diff --git a/src/streaming/shred_stream.rs b/src/streaming/shred_stream.rs index 4aa7bd5..f98a412 100755 --- a/src/streaming/shred_stream.rs +++ b/src/streaming/shred_stream.rs @@ -5,10 +5,11 @@ use solana_sdk::pubkey::Pubkey; use crate::common::AnyResult; use crate::protos::shredstream::SubscribeEntriesRequest; -use crate::streaming::common::{EventProcessor, SubscriptionHandle}; +use crate::streaming::common::{process_shred_transaction, SubscriptionHandle}; use crate::streaming::event_parser::common::filter::EventTypeFilter; use crate::streaming::event_parser::common::high_performance_clock::get_high_perf_clock; -use crate::streaming::event_parser::{Protocol, UnifiedEvent}; +use crate::streaming::event_parser::{Protocol, DexEvent}; +use crate::streaming::grpc::MetricsManager; use crate::streaming::shred::pool::factory; use log::error; use solana_entry::entry::Entry; @@ -25,7 +26,7 @@ impl ShredStreamGrpc { callback: F, ) -> AnyResult<()> where - F: Fn(Box) + Send + Sync + 'static, + F: Fn(DexEvent) + Send + Sync + 'static, { // 如果已有活跃订阅,先停止它 self.stop().await; @@ -33,25 +34,17 @@ impl ShredStreamGrpc { let mut metrics_handle = None; // 启动自动性能监控(如果启用) if self.config.enable_metrics { - metrics_handle = self.metrics_manager.start_auto_monitoring().await; + metrics_handle = MetricsManager::global().start_auto_monitoring().await; } - // 创建事件处理器 - let mut event_processor = - EventProcessor::new(self.metrics_manager.clone(), self.config.clone()); - event_processor.set_protocols_and_event_type_filter( - super::common::EventSource::Shred, - protocols, - event_type_filter, - self.config.backpressure.clone(), - Some(Arc::new(callback)), - ); - // 启动流处理 let mut client = (*self.shredstream_client).clone(); let request = tonic::Request::new(SubscribeEntriesRequest {}); let mut stream = client.subscribe_entries(request).await?.into_inner(); - let event_processor_clone = event_processor.clone(); + + // Wrap callback once before the async block + let callback = Arc::new(callback); + let stream_task = tokio::spawn(async move { while let Some(message) = stream.next().await { match message { @@ -65,13 +58,15 @@ impl ShredStreamGrpc { msg.slot, get_high_perf_clock(), ); - // 直接处理,背压控制在 EventProcessor 内部处理 - if let Err(e) = event_processor_clone - .process_shred_transaction_with_metrics( - transaction_with_slot, - bot_wallet, - ) - .await + // Process transaction - clone Arc and Vec for each call + if let Err(e) = process_shred_transaction( + transaction_with_slot, + &protocols, + event_type_filter.as_ref(), + callback.clone(), + bot_wallet, + ) + .await { error!("Error handling message: {e:?}"); } diff --git a/src/streaming/yellowstone_grpc.rs b/src/streaming/yellowstone_grpc.rs index bc499f3..2a6907f 100644 --- a/src/streaming/yellowstone_grpc.rs +++ b/src/streaming/yellowstone_grpc.rs @@ -1,9 +1,10 @@ use crate::common::AnyResult; use crate::streaming::common::{ - EventProcessor, MetricsManager, PerformanceMetrics, StreamClientConfig, SubscriptionHandle, + process_grpc_transaction, MetricsManager, PerformanceMetrics, StreamClientConfig, + SubscriptionHandle, }; use crate::streaming::event_parser::common::filter::EventTypeFilter; -use crate::streaming::event_parser::{Protocol, UnifiedEvent}; +use crate::streaming::event_parser::{Protocol, DexEvent}; use crate::streaming::grpc::pool::factory; use crate::streaming::grpc::{EventPretty, SubscriptionManager}; use anyhow::anyhow; @@ -13,7 +14,7 @@ use futures::{SinkExt, StreamExt}; use log::error; use solana_sdk::pubkey::Pubkey; use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::{Arc, RwLock}; +use std::sync::Arc; use tokio::sync::Mutex; use yellowstone_grpc_proto::geyser::subscribe_update::UpdateOneof; use yellowstone_grpc_proto::geyser::{ @@ -40,10 +41,7 @@ pub struct YellowstoneGrpc { pub endpoint: String, pub x_token: Option, pub config: StreamClientConfig, - pub metrics: Arc>, pub subscription_manager: SubscriptionManager, - pub metrics_manager: MetricsManager, - pub event_processor: EventProcessor, pub subscription_handle: Arc>>, // Dynamic subscription management fields pub active_subscription: Arc, @@ -66,25 +64,15 @@ impl YellowstoneGrpc { config: StreamClientConfig, ) -> AnyResult { let _ = rustls::crypto::ring::default_provider().install_default().ok(); - let metrics = Arc::new(RwLock::new(PerformanceMetrics::new())); - let subscription_manager = SubscriptionManager::new(endpoint.clone(), x_token.clone(), config.clone()); - let metrics_manager = MetricsManager::new_with_metrics( - metrics.clone(), - config.enable_metrics, - "YellowstoneGrpc".to_string(), - ); - let event_processor = EventProcessor::new(metrics_manager.clone(), config.clone()); + MetricsManager::init(config.enable_metrics); Ok(Self { endpoint, x_token, config, - metrics: metrics.clone(), subscription_manager, - metrics_manager, - event_processor, subscription_handle: Arc::new(Mutex::new(None)), active_subscription: Arc::new(AtomicBool::new(false)), control_tx: Arc::new(tokio::sync::Mutex::new(None)), @@ -93,24 +81,6 @@ impl YellowstoneGrpc { }) } - /// Creates a new YellowstoneGrpcClient with high-throughput configuration. - /// - /// This is a convenience method that creates a client optimized for high-concurrency scenarios - /// where throughput is prioritized over latency. See `StreamClientConfig::high_throughput()` - /// for detailed configuration information. - pub fn new_high_throughput(endpoint: String, x_token: Option) -> AnyResult { - Self::new_with_config(endpoint, x_token, StreamClientConfig::high_throughput()) - } - - /// Creates a new YellowstoneGrpcClient with low-latency configuration. - /// - /// This is a convenience method that creates a client optimized for real-time scenarios - /// where latency is prioritized over throughput. See `StreamClientConfig::low_latency()` - /// for detailed configuration information. - pub fn new_low_latency(endpoint: String, x_token: Option) -> AnyResult { - Self::new_with_config(endpoint, x_token, StreamClientConfig::low_latency()) - } - /// 获取配置 pub fn get_config(&self) -> &StreamClientConfig { &self.config @@ -123,12 +93,12 @@ impl YellowstoneGrpc { /// 获取性能指标 pub fn get_metrics(&self) -> PerformanceMetrics { - self.metrics_manager.get_metrics() + MetricsManager::global().get_metrics() } /// 打印性能指标 pub fn print_metrics(&self) { - self.metrics_manager.print_metrics(); + MetricsManager::global().print_metrics(); } /// 启用或禁用性能监控 @@ -171,7 +141,7 @@ impl YellowstoneGrpc { callback: F, ) -> AnyResult<()> where - F: Fn(Box) + Send + Sync + 'static, + F: Fn(DexEvent) + Send + Sync + 'static, { *self.event_type_filter.write().await = event_type_filter.clone(); if self @@ -185,7 +155,7 @@ impl YellowstoneGrpc { let mut metrics_handle = None; // 启动自动性能监控(如果启用) if self.config.enable_metrics { - metrics_handle = self.metrics_manager.start_auto_monitoring().await; + metrics_handle = MetricsManager::global().start_auto_monitoring().await; } let transactions = self @@ -207,15 +177,9 @@ impl YellowstoneGrpc { let (control_tx, mut control_rx) = mpsc::channel(100); *self.control_tx.lock().await = Some(control_tx); - // 启动流处理任务 - let mut event_processor = self.event_processor.clone(); - event_processor.set_protocols_and_event_type_filter( - super::common::EventSource::Grpc, - protocols, - event_type_filter, - self.config.backpressure.clone(), - Some(Arc::new(callback)), - ); + // Wrap callback once before the async block + let callback = Arc::new(callback); + let stream_handle = tokio::spawn(async move { loop { tokio::select! { @@ -227,12 +191,14 @@ impl YellowstoneGrpc { Some(UpdateOneof::Account(account)) => { let account_pretty = factory::create_account_pretty_pooled(account); log::debug!("Received account: {:?}", account_pretty); - if let Err(e) = event_processor - .process_grpc_event_transaction_with_metrics( - EventPretty::Account(account_pretty), - bot_wallet, - ) - .await + if let Err(e) = process_grpc_transaction( + EventPretty::Account(account_pretty), + &protocols, + event_type_filter.as_ref(), + callback.clone(), + bot_wallet, + ) + .await { error!("Error processing account event: {e:?}"); } @@ -240,12 +206,14 @@ impl YellowstoneGrpc { Some(UpdateOneof::BlockMeta(sut)) => { let block_meta_pretty = factory::create_block_meta_pretty_pooled(sut, created_at); log::debug!("Received block meta: {:?}", block_meta_pretty); - if let Err(e) = event_processor - .process_grpc_event_transaction_with_metrics( - EventPretty::BlockMeta(block_meta_pretty), - bot_wallet, - ) - .await + if let Err(e) = process_grpc_transaction( + EventPretty::BlockMeta(block_meta_pretty), + &protocols, + event_type_filter.as_ref(), + callback.clone(), + bot_wallet, + ) + .await { error!("Error processing block meta event: {e:?}"); } @@ -257,12 +225,14 @@ impl YellowstoneGrpc { transaction_pretty.signature, transaction_pretty.slot ); - if let Err(e) = event_processor - .process_grpc_event_transaction_with_metrics( - EventPretty::Transaction(transaction_pretty), - bot_wallet, - ) - .await + if let Err(e) = process_grpc_transaction( + EventPretty::Transaction(transaction_pretty), + &protocols, + event_type_filter.as_ref(), + callback.clone(), + bot_wallet, + ) + .await { error!("Error processing transaction event: {e:?}"); } @@ -380,10 +350,7 @@ impl Clone for YellowstoneGrpc { endpoint: self.endpoint.clone(), x_token: self.x_token.clone(), config: self.config.clone(), - metrics: self.metrics.clone(), subscription_manager: self.subscription_manager.clone(), - metrics_manager: self.metrics_manager.clone(), - event_processor: self.event_processor.clone(), subscription_handle: self.subscription_handle.clone(), // 共享同一个 Arc> active_subscription: self.active_subscription.clone(), control_tx: self.control_tx.clone(),