Merge commit 'fd180935f8619362fb852918ac686e9b94fa03a4'

This commit is contained in:
ysq
2025-10-12 22:12:21 +08:00
49 changed files with 1917 additions and 2546 deletions
+1 -1
View File
@@ -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
+1 -1
View File
@@ -295,7 +295,7 @@ grpc.update_subscription(
### 统一事件接口
- **UnifiedEvent Trait**: 所有协议事件实现通用接口
- **DexEvent Trait**: 所有协议事件实现通用接口
- **Protocol Enum**: 轻松识别事件来源
- **Event Factory**: 自动事件解析和分类
+8 -8
View File
@@ -31,10 +31,10 @@ async fn main() -> Result<()> {
let counter = event_counter.clone();
let callback =
move |event: Box<dyn solana_streamer_sdk::streaming::event_parser::UnifiedEvent>| {
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<dyn solana_streamer_sdk::streaming::event_parser::UnifiedEvent>| {
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<dyn solana_streamer_sdk::streaming::event_parser::UnifiedEvent>| {};
|_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<dyn solana_streamer_sdk::streaming::event_parser::UnifiedEvent>| {
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<dyn solana_streamer_sdk::streaming::event_parser::UnifiedEvent>| {};
|_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<dyn solana_streamer_sdk::streaming::event_parser::UnifiedEvent>| {
move |_event: solana_streamer_sdk::streaming::event_parser::DexEvent| {
counter4.fetch_add(1, Ordering::Relaxed);
};
+28 -203
View File
@@ -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<dyn std::error::Error>> {
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<dyn std::error::Error>> {
};
// 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<dyn std::error::Error>> {
Ok(())
}
fn create_event_callback() -> impl Fn(Box<dyn UnifiedEvent>) {
|event: Box<dyn UnifiedEvent>| {
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);
}
}
}
}
+14 -19
View File
@@ -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<dyn std::error::Error>> {
async fn test_grpc() -> Result<(), Box<dyn std::error::Error>> {
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<dyn std::error::Error>> {
Ok(())
}
fn create_event_callback() -> impl Fn(Box<dyn UnifiedEvent>) {
|event: Box<dyn UnifiedEvent>| {
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);
}
_ => {}
}
}
+4 -4
View File
@@ -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<dyn std::error::Error>> {
async fn test_grpc() -> Result<(), Box<dyn std::error::Error>> {
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<dyn std::error::Error>> {
Ok(())
}
fn create_event_callback() -> impl Fn(Box<dyn UnifiedEvent>) {
|event: Box<dyn UnifiedEvent>| {
fn create_event_callback() -> impl Fn(DexEvent) {
|event: DexEvent| {
println!("🎉 Event received! {:?}", event);
}
}
+11 -11
View File
@@ -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<EventParser> = Arc::new(EventParser::new(protocols, None));
parser
.parse_encoded_confirmed_transaction_with_status_meta(
signature,
transaction,
Arc::new(move |event: &Box<dyn UnifiedEvent>| {
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);
@@ -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<dyn std::error::Error>> {
Ok(())
}
fn create_event_callback() -> impl Fn(Box<dyn UnifiedEvent>) {
|event: Box<dyn UnifiedEvent>| {
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);
}
_ => {}
}
}
+14 -196
View File
@@ -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<dyn std::error::Error>> {
Ok(())
}
fn create_event_callback() -> impl Fn(Box<dyn UnifiedEvent>) {
|event: Box<dyn UnifiedEvent>| {
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:?}");
},
});
}
_ => {}
}
}
}
}
+3 -3
View File
@@ -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<dyn std::error::Error>> {
Ok(())
}
fn create_event_callback() -> impl Fn(Box<dyn UnifiedEvent>) {
|event: Box<dyn UnifiedEvent>| {
fn create_event_callback() -> impl Fn(DexEvent) {
|event: DexEvent| {
println!("🎉 Event received! {:?}", event);
}
}
+13 -18
View File
@@ -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<dyn std::error::Error>> {
Ok(())
}
fn create_event_callback() -> impl Fn(Box<dyn UnifiedEvent>) {
|event: Box<dyn UnifiedEvent>| {
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);
}
_ => {}
}
}
+5 -71
View File
@@ -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()
}
}
+116 -396
View File
@@ -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<dyn Fn(DexEvent) + Send + Sync>,
) -> Arc<dyn Fn(DexEvent) + Send + Sync> {
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<Arc<EventParser>>,
pub(crate) protocols: Vec<Protocol>,
pub(crate) event_type_filter: Option<EventTypeFilter>,
pub(crate) callback: Option<Arc<dyn Fn(Box<dyn UnifiedEvent>) + Send + Sync>>,
pub(crate) backpressure_config: BackpressureConfig,
pub(crate) grpc_queue: Arc<SegQueue<(EventPretty, Option<Pubkey>)>>,
pub(crate) shred_queue: Arc<SegQueue<(TransactionWithSlot, Option<Pubkey>)>>,
pub(crate) grpc_pending_count: Arc<AtomicUsize>,
pub(crate) shred_pending_count: Arc<AtomicUsize>,
pub(crate) processing_shutdown: Arc<AtomicBool>,
}
/// Process GRPC transaction events
pub async fn process_grpc_transaction(
event_pretty: EventPretty,
protocols: &[Protocol],
event_type_filter: Option<&EventTypeFilter>,
callback: Arc<dyn Fn(DexEvent) + Send + Sync>,
bot_wallet: Option<Pubkey>,
) -> 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<Protocol>,
event_type_filter: Option<EventTypeFilter>,
backpressure_config: BackpressureConfig,
callback: Option<Arc<dyn Fn(Box<dyn UnifiedEvent>) + 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<EventParser> {
self.parser_cache.get().unwrap().clone()
}
fn create_adapter_callback(&self) -> Arc<dyn Fn(Box<dyn UnifiedEvent>) + Send + Sync> {
let callback = self.callback.clone().unwrap();
let metrics_manager = self.metrics_manager.clone();
Arc::new(move |event: Box<dyn UnifiedEvent>| {
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<Pubkey>,
) -> AnyResult<()> {
self.apply_backpressure_control(event_pretty, bot_wallet).await
}
async fn apply_backpressure_control(
&self,
event_pretty: EventPretty,
bot_wallet: Option<Pubkey>,
) -> 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<Pubkey>,
) -> 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<dyn UnifiedEvent>) {
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<Pubkey>,
) -> 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<Pubkey>,
) -> 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<Pubkey>,
) -> 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<Pubkey>,
) -> 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<dyn Fn(DexEvent) + Send + Sync>,
bot_wallet: Option<Pubkey>,
) -> 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);
}
+108 -209
View File
@@ -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<HighPerformanceMetrics>,
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<tokio::task::JoinHandle<()>> {
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<std::sync::RwLock<PerformanceMetrics>>,
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);
}
}
}
+1 -1
View File
@@ -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<EventType>,
}
+2 -75
View File
@@ -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<dyn $crate::streaming::event_parser::core::traits::UnifiedEvent> {
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<i64> {
self.metadata.inner_index
}
fn transaction_index(&self) -> Option<u64> {
self.metadata.transaction_index
}
}
};
}
pub mod types;
pub mod utils;
pub use types::*;
pub use utils::*;
+88 -98
View File
@@ -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<Pubkey> = None;
// let mut user: Option<Pubkey> = None;
let mut from_mint: Option<Pubkey> = None;
let mut to_mint: Option<Pubkey> = None;
let mut user_from_token: Option<Pubkey> = None;
@@ -386,63 +370,66 @@ pub fn parse_swap_data_from_next_instructions(
let mut from_vault: Option<Pubkey> = None;
let mut to_vault: Option<Pubkey> = 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<Pubkey> = None;
// let mut user: Option<Pubkey> = None;
let mut from_mint: Option<Pubkey> = None;
let mut to_mint: Option<Pubkey> = None;
let mut user_from_token: Option<Pubkey> = None;
@@ -553,63 +540,66 @@ pub fn parse_swap_data_from_next_grpc_instructions(
let mut from_vault: Option<Pubkey> = None;
let mut to_vault: Option<Pubkey> = 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();
@@ -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<u64>,
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<Box<dyn UnifiedEvent>>;
static PROTOCOL_CONFIGS_CACHE: OnceLock<HashMap<Protocol, Vec<AccountEventParseConfig>>> =
OnceLock::new();
// 通用账户解析配置的静态缓存
static COMMON_CONFIG: OnceLock<AccountEventParseConfig> = OnceLock::new();
// Nonce account config
static NONCE_CONFIG: OnceLock<AccountEventParseConfig> = OnceLock::new();
pub struct AccountEventParser {}
impl AccountEventParser {
pub fn configs(
protocols: &[Protocol],
event_type_filter: Option<&EventTypeFilter>,
) -> Vec<AccountEventParseConfig> {
let protocols_map = PROTOCOL_CONFIGS_CACHE.get_or_init(|| {
let mut map: HashMap<Protocol, Vec<AccountEventParseConfig>> = 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<Box<dyn UnifiedEvent>> {
let configs = Self::configs(protocols, event_type_filter);
) -> Option<DexEvent> {
// 直接从 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<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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 => {}
}
@@ -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<dyn UnifiedEvent> {
) -> 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)
}
}
+209 -310
View File
@@ -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<Pubkey>,
}
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<Box<dyn UnifiedEvent>>;
/// 指令事件解析器
pub type InstructionEventParser =
fn(data: &[u8], accounts: &[Pubkey], metadata: EventMetadata) -> Option<Box<dyn UnifiedEvent>>;
/// 通用事件解析器配置
#[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<InnerInstructionEventParser>,
pub instruction_parser: Option<InstructionEventParser>,
pub requires_inner_instruction: bool,
}
pub static EVENT_PARSERS: LazyLock<HashMap<Protocol, (Pubkey, &[GenericEventParseConfig])>> =
LazyLock::new(|| {
// 预分配容量,避免动态扩容
let mut parsers: HashMap<Protocol, (Pubkey, &[GenericEventParseConfig])> =
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<Pubkey>,
// pub inner_instruction_configs: HashMap<Vec<u8>, Vec<GenericEventParseConfig>>,
pub instruction_configs: HashMap<Vec<u8>, Vec<GenericEventParseConfig>>,
/// 账户公钥缓存,避免重复分配
pub account_cache: parking_lot::Mutex<AccountPubkeyCache>,
}
pub struct EventParser {}
impl EventParser {
pub fn new(protocols: Vec<Protocol>, event_type_filter: Option<EventTypeFilter>) -> 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<u64>,
@@ -200,12 +48,14 @@ impl EventParser {
inner_instructions: &[yellowstone_grpc_proto::prelude::InnerInstructions],
bot_wallet: Option<Pubkey>,
transaction_index: Option<u64>,
callback: Arc<dyn for<'a> Fn(&'a Box<dyn UnifiedEvent>) + Send + Sync>,
callback: Arc<dyn for<'a> 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<u64>,
@@ -283,13 +138,15 @@ impl EventParser {
inner_instructions: &[InnerInstructions],
bot_wallet: Option<Pubkey>,
transaction_index: Option<u64>,
callback: Arc<dyn for<'a> Fn(&'a Box<dyn UnifiedEvent>) + Send + Sync>,
callback: Arc<dyn for<'a> Fn(&'a DexEvent) + Send + Sync>,
) -> anyhow::Result<()> {
// 获取交易的指令和账户
let compiled_instructions = transaction.message.instructions();
let mut accounts: Vec<Pubkey> = 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<u64>,
@@ -356,13 +218,15 @@ impl EventParser {
bot_wallet: Option<Pubkey>,
transaction_index: Option<u64>,
inner_instructions: &[InnerInstructions],
callback: Arc<dyn Fn(Box<dyn UnifiedEvent>) + Send + Sync>,
callback: Arc<dyn Fn(DexEvent) + Send + Sync>,
) -> anyhow::Result<()> {
// 创建适配器回调,将所有权回调转换为引用回调
let adapter_callback = Arc::new(move |event: &Box<dyn UnifiedEvent>| {
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<u64>,
@@ -387,10 +252,12 @@ impl EventParser {
bot_wallet: Option<Pubkey>,
transaction_index: Option<u64>,
inner_instructions: &[InnerInstructions],
callback: Arc<dyn for<'a> Fn(&'a Box<dyn UnifiedEvent>) + Send + Sync>,
callback: Arc<dyn for<'a> Fn(&'a DexEvent) + Send + Sync>,
) -> anyhow::Result<()> {
let accounts: Vec<Pubkey> = 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<u64>,
@@ -415,14 +283,16 @@ impl EventParser {
recv_us: i64,
bot_wallet: Option<Pubkey>,
transaction_index: Option<u64>,
callback: Arc<dyn Fn(Box<dyn UnifiedEvent>) + Send + Sync>,
callback: Arc<dyn Fn(DexEvent) + Send + Sync>,
) -> anyhow::Result<()> {
// 创建适配器回调,将所有权回调转换为引用回调
let adapter_callback = Arc::new(move |event: &Box<dyn UnifiedEvent>| {
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<u64>,
@@ -444,7 +315,7 @@ impl EventParser {
recv_us: i64,
bot_wallet: Option<Pubkey>,
transaction_index: Option<u64>,
callback: Arc<dyn for<'a> Fn(&'a Box<dyn UnifiedEvent>) + Send + Sync>,
callback: Arc<dyn for<'a> 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<dyn for<'a> Fn(&'a Box<dyn UnifiedEvent>) + Send + Sync>,
callback: Arc<dyn for<'a> 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<i64>,
transaction_index: Option<u64>,
) -> Option<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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<i64>,
transaction_index: Option<u64>,
) -> Option<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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<i64>,
transaction_index: Option<u64>,
config: &GenericEventParseConfig,
) -> Vec<Box<dyn UnifiedEvent>> {
) -> Vec<DexEvent> {
// 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<i64>,
transaction_index: Option<u64>,
config: &GenericEventParseConfig,
) -> Vec<Box<dyn UnifiedEvent>> {
) -> Vec<DexEvent> {
// 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<Pubkey>,
transaction_index: Option<u64>,
inner_instructions: Option<&InnerInstructions>,
callback: Arc<dyn for<'a> Fn(&'a Box<dyn UnifiedEvent>) + Send + Sync>,
callback: Arc<dyn for<'a> 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<Box<dyn UnifiedEvent>> = None;
let mut inner_instruction_event: Option<DexEvent> = 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<Pubkey>,
transaction_index: Option<u64>,
inner_instructions: Option<&yellowstone_grpc_proto::prelude::InnerInstructions>,
callback: Arc<dyn for<'a> Fn(&'a Box<dyn UnifiedEvent>) + Send + Sync>,
callback: Arc<dyn for<'a> 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<Box<dyn UnifiedEvent>> = None;
let mut inner_instruction_event: Option<DexEvent> = 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<Pubkey> {
// self.program_ids.clone()
// }
}
fn process_event(
mut event: Box<dyn UnifiedEvent>,
bot_wallet: Option<Pubkey>,
) -> Box<dyn UnifiedEvent> {
let signature = *event.signature(); // Copy the signature to avoid borrowing issues
if let Some(token_info) = event.as_any().downcast_ref::<PumpFunCreateTokenEvent>() {
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::<PumpFunTradeEvent>() {
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::<PumpSwapBuyEvent>() {
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::<PumpSwapSellEvent>() {
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::<BonkPoolCreateEvent>() {
add_bonk_dev_address(&signature, pool_info.creator);
} else if let Some(trade_info) = event.as_any_mut().downcast_mut::<BonkTradeEvent>() {
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<Pubkey>) -> 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
}
@@ -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;
}
_ => {}
},
_ => {}
}
}
+7 -1
View File
@@ -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;
@@ -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<DexEvent>;
/// 指令事件解析器函数类型
///
/// 用于解析普通指令(Instruction)生成的事件,需要账户信息
pub type InstructionEventParser =
fn(data: &[u8], accounts: &[Pubkey], metadata: EventMetadata) -> Option<DexEvent>;
/// 指令事件解析器配置
///
/// 定义如何解析特定协议的指令事件
#[derive(Debug, Clone)]
pub struct GenericEventParseConfig {
/// 程序IDProgram 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<InnerInstructionEventParser>,
/// 普通指令解析器
pub instruction_parser: Option<InstructionEventParser>,
/// 是否需要内联指令数据
pub requires_inner_instruction: bool,
}
/// 全局指令事件解析器注册表
///
/// 存储所有协议的指令解析器配置,启动时初始化一次
pub static EVENT_PARSERS: LazyLock<HashMap<Protocol, (Pubkey, &[GenericEventParseConfig])>> =
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<Protocol>,
/// 事件类型过滤器(可选,已排序)
pub event_types: Option<Vec<EventType>>,
}
impl CacheKey {
/// 创建新的缓存键
///
/// # 参数
/// - `protocols`: 协议列表
/// - `filter`: 事件类型过滤器
///
/// # 优化
/// 使用 `sort_by_cached_key` 避免重复调用 `format!`
pub fn new(mut protocols: Vec<Protocol>, 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<HashMap<CacheKey, Arc<Vec<Pubkey>>>>,
> = LazyLock::new(|| parking_lot::RwLock::new(HashMap::new()));
/// 全局指令配置缓存(使用读写锁保护)
static GLOBAL_INSTRUCTION_CONFIGS_CACHE: LazyLock<
parking_lot::RwLock<HashMap<CacheKey, Arc<HashMap<Vec<u8>, Vec<GenericEventParseConfig>>>>>,
> = 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<Vec<Pubkey>> {
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<HashMap<Vec<u8>, Vec<GenericEventParseConfig>>> {
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<Pubkey>,
}
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<AccountPubkeyCache> =
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<Pubkey> {
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<DexEvent>;
/// 账户事件解析器配置
///
/// 定义如何解析特定协议的账户事件
#[derive(Debug, Clone)]
pub struct AccountEventParseConfig {
/// 程序IDProgram 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<HashMap<Protocol, Vec<AccountEventParseConfig>>> =
OnceLock::new();
/// Nonce 账户配置缓存
static NONCE_CONFIG: OnceLock<AccountEventParseConfig> = OnceLock::new();
/// 通用 Token 账户配置缓存
static COMMON_CONFIG: OnceLock<AccountEventParseConfig> = 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<AccountEventParseConfig> {
// 初始化协议配置缓存(仅在首次调用时执行)
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
}
+169 -47
View File
@@ -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<dyn UnifiedEvent>;
/// 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<i64>;
/// Get transaction index in slot
fn transaction_index(&self) -> Option<u64>;
// Common events
TokenAccountEvent(TokenAccountEvent),
NonceAccountEvent(NonceAccountEvent),
TokenInfoEvent(TokenInfoEvent),
BlockMetaEvent(BlockMetaEvent),
}
// 为Box<dyn UnifiedEvent>实现Clone
impl Clone for Box<dyn UnifiedEvent> {
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,
}
}
}
+2 -36
View File
@@ -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;
@@ -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,);
@@ -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<BonkTradeEvent> {
borsh::from_slice::<BonkTradeEvent>(&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<BonkPoolCreateEv
borsh::from_slice::<BonkPoolCreateEvent>(&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<Pubkey>,
}
// 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 {
@@ -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<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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<Box<dyn UnifiedEvent>> {
fn parse_trade_inner_instruction(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
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<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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<Box<dyn UnifiedEvent>> {
Some(Box::new(BonkMigrateToCpswapEvent {
) -> Option<DexEvent> {
Some(DexEvent::BonkMigrateToCpswapEvent(BonkMigrateToCpswapEvent {
metadata,
payer: accounts[0],
base_mint: accounts[1],
@@ -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<PoolState> {
borsh::from_slice::<PoolState>(&data[..POOL_STATE_SIZE]).ok()
}
pub fn pool_state_parser(
account: &AccountPretty,
metadata: EventMetadata,
) -> Option<Box<dyn UnifiedEvent>> {
pub fn pool_state_parser(account: &AccountPretty, metadata: EventMetadata) -> Option<DexEvent> {
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<GlobalConfig> {
pub fn global_config_parser(
account: &AccountPretty,
metadata: EventMetadata,
) -> Option<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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<PlatformConfig> {
pub fn platform_config_parser(
account: &AccountPretty,
metadata: EventMetadata,
) -> Option<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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,
@@ -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<PumpFunCreat
borsh::from_slice::<PumpFunCreateTokenEvent>(&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<PumpFunTradeEvent>
borsh::from_slice::<PumpFunTradeEvent>(&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<PumpFunMigrateEve
borsh::from_slice::<PumpFunMigrateEvent>(&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 {
@@ -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<Box<dyn UnifiedEvent>> {
fn parse_migrate_inner_instruction(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
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<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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<Box<dyn UnifiedEvent>> {
fn parse_trade_inner_instruction(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
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<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
if accounts.len() < 24 {
return None;
}
Some(Box::new(PumpFunMigrateEvent {
Some(DexEvent::PumpFunMigrateEvent(PumpFunMigrateEvent {
metadata,
global: accounts[0],
withdraw_authority: accounts[1],
@@ -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<BondingCurve> {
pub fn bonding_curve_parser(
account: &AccountPretty,
metadata: EventMetadata,
) -> Option<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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<Global> {
borsh::from_slice::<Global>(&data[..GLOBAL_SIZE]).ok()
}
pub fn global_parser(
account: &AccountPretty,
metadata: EventMetadata,
) -> Option<Box<dyn UnifiedEvent>> {
pub fn global_parser(account: &AccountPretty, metadata: EventMetadata) -> Option<DexEvent> {
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,
@@ -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<PumpSwapBuyEvent> {
borsh::from_slice::<PumpSwapBuyEvent>(&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<PumpSwapSellEvent>
borsh::from_slice::<PumpSwapSellEvent>(&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<PumpSwapCre
borsh::from_slice::<PumpSwapCreatePoolEvent>(&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<PumpSwapDeposit
borsh::from_slice::<PumpSwapDepositEvent>(&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<PumpSwapWithdr
borsh::from_slice::<PumpSwapWithdrawEvent>(&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 {
@@ -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<Box<dyn UnifiedEvent>> {
fn parse_buy_inner_instruction(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
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<Box<dyn UnifiedEvent>> {
fn parse_sell_inner_instruction(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
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<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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<Box<dyn UnifiedEvent>> {
fn parse_deposit_inner_instruction(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
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<Box<dyn UnifiedEvent>> {
fn parse_withdraw_inner_instruction(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
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<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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,
@@ -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<GlobalConfig> {
pub fn global_config_parser(
account: &AccountPretty,
metadata: EventMetadata,
) -> Option<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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<Pool> {
borsh::from_slice::<Pool>(&data[..POOL_SIZE]).ok()
}
pub fn pool_parser(
account: &AccountPretty,
metadata: EventMetadata,
) -> Option<Box<dyn UnifiedEvent>> {
pub fn pool_parser(account: &AccountPretty, metadata: EventMetadata) -> Option<DexEvent> {
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,
@@ -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 {
@@ -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<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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,
@@ -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<AmmInfo> {
borsh::from_slice::<AmmInfo>(&data[..AMM_INFO_SIZE]).ok()
}
pub fn amm_info_parser(
account: &AccountPretty,
metadata: EventMetadata,
) -> Option<Box<dyn UnifiedEvent>> {
pub fn amm_info_parser(account: &AccountPretty, metadata: EventMetadata) -> Option<DexEvent> {
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,
@@ -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<Pubkey>,
}
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<Pubkey>,
}
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<Pubkey>,
}
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<Pubkey>,
}
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 {
@@ -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<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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,
@@ -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<AmmConfig> {
borsh::from_slice::<AmmConfig>(&data[..AMM_CONFIG_SIZE]).ok()
}
pub fn amm_config_parser(
account: &AccountPretty,
metadata: EventMetadata,
) -> Option<Box<dyn UnifiedEvent>> {
pub fn amm_config_parser(account: &AccountPretty, metadata: EventMetadata) -> Option<DexEvent> {
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<PoolState> {
borsh::from_slice::<PoolState>(&data[..POOL_STATE_SIZE]).ok()
}
pub fn pool_state_parser(
account: &AccountPretty,
metadata: EventMetadata,
) -> Option<Box<dyn UnifiedEvent>> {
pub fn pool_state_parser(account: &AccountPretty, metadata: EventMetadata) -> Option<DexEvent> {
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<TickArrayState> {
pub fn tick_array_state_parser(
account: &AccountPretty,
metadata: EventMetadata,
) -> Option<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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
}
@@ -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 {
@@ -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<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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<Box<dyn UnifiedEvent>> {
) -> Option<DexEvent> {
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,
@@ -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<AmmConfig> {
borsh::from_slice::<AmmConfig>(&data[..AMM_CONFIG_SIZE]).ok()
}
pub fn amm_config_parser(
account: &AccountPretty,
metadata: EventMetadata,
) -> Option<Box<dyn UnifiedEvent>> {
pub fn amm_config_parser(account: &AccountPretty, metadata: EventMetadata) -> Option<DexEvent> {
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<PoolState> {
borsh::from_slice::<PoolState>(&data[..POOL_STATE_SIZE]).ok()
}
pub fn pool_state_parser(
account: &AccountPretty,
metadata: EventMetadata,
) -> Option<Box<dyn UnifiedEvent>> {
pub fn pool_state_parser(account: &AccountPretty, metadata: EventMetadata) -> Option<DexEvent> {
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,
+1 -2
View File
@@ -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,
};
+4 -31
View File
@@ -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<ShredstreamProxyClient<Channel>>,
pub config: StreamClientConfig,
pub metrics: Arc<RwLock<PerformanceMetrics>>,
pub metrics_manager: MetricsManager,
pub subscription_handle: Arc<Mutex<Option<SubscriptionHandle>>>,
}
@@ -28,38 +25,14 @@ impl ShredStreamGrpc {
/// 创建客户端,使用自定义配置
pub async fn new_with_config(endpoint: String, config: StreamClientConfig) -> AnyResult<Self> {
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> {
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> {
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;
}
/// 停止当前订阅
+1 -2
View File
@@ -10,6 +10,5 @@ pub use types::*;
// 从公用模块重新导出
pub use crate::streaming::common::{
BackpressureConfig, BackpressureStrategy, ConnectionConfig, MetricsEventType, MetricsManager,
PerformanceMetrics, StreamClientConfig,
ConnectionConfig, MetricsEventType, MetricsManager, PerformanceMetrics, StreamClientConfig,
};
+18 -23
View File
@@ -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<dyn UnifiedEvent>) + 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:?}");
}
+36 -69
View File
@@ -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<String>,
pub config: StreamClientConfig,
pub metrics: Arc<RwLock<PerformanceMetrics>>,
pub subscription_manager: SubscriptionManager,
pub metrics_manager: MetricsManager,
pub event_processor: EventProcessor,
pub subscription_handle: Arc<Mutex<Option<SubscriptionHandle>>>,
// Dynamic subscription management fields
pub active_subscription: Arc<AtomicBool>,
@@ -66,25 +64,15 @@ impl YellowstoneGrpc {
config: StreamClientConfig,
) -> AnyResult<Self> {
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<String>) -> AnyResult<Self> {
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<String>) -> AnyResult<Self> {
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<dyn UnifiedEvent>) + 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<Mutex<>>
active_subscription: self.active_subscription.clone(),
control_tx: self.control_tx.clone(),