mirror of
https://github.com/0xfnzero/solana-streamer.git
synced 2026-07-28 01:47:43 +00:00
Merge commit 'fd180935f8619362fb852918ac686e9b94fa03a4'
This commit is contained in:
@@ -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
@@ -295,7 +295,7 @@ grpc.update_subscription(
|
||||
|
||||
### 统一事件接口
|
||||
|
||||
- **UnifiedEvent Trait**: 所有协议事件实现通用接口
|
||||
- **DexEvent Trait**: 所有协议事件实现通用接口
|
||||
- **Protocol Enum**: 轻松识别事件来源
|
||||
- **Event Factory**: 自动事件解析和分类
|
||||
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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:?}");
|
||||
},
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>,
|
||||
}
|
||||
|
||||
@@ -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::*;
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
/// 程序ID(Program ID)
|
||||
pub program_id: Pubkey,
|
||||
/// 协议类型
|
||||
pub protocol_type: ProtocolType,
|
||||
/// 内联指令判别器(8字节)
|
||||
pub inner_instruction_discriminator: &'static [u8],
|
||||
/// 普通指令判别器(8字节)
|
||||
pub instruction_discriminator: &'static [u8],
|
||||
/// 事件类型
|
||||
pub event_type: EventType,
|
||||
/// 内联指令解析器
|
||||
pub inner_instruction_parser: Option<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 {
|
||||
/// 程序ID(Program ID)
|
||||
pub program_id: Pubkey,
|
||||
/// 协议类型
|
||||
pub protocol_type: ProtocolType,
|
||||
/// 事件类型
|
||||
pub event_type: EventType,
|
||||
/// 账户判别器(Account Discriminator)
|
||||
pub account_discriminator: &'static [u8],
|
||||
/// 账户解析器函数
|
||||
pub account_parser: AccountEventParserFn,
|
||||
}
|
||||
|
||||
/// 协议账户配置缓存
|
||||
///
|
||||
/// 存储各协议的账户解析器配置
|
||||
static PROTOCOL_CONFIGS_CACHE: OnceLock<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
|
||||
}
|
||||
@@ -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,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,
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/// 停止当前订阅
|
||||
|
||||
@@ -10,6 +10,5 @@ pub use types::*;
|
||||
|
||||
// 从公用模块重新导出
|
||||
pub use crate::streaming::common::{
|
||||
BackpressureConfig, BackpressureStrategy, ConnectionConfig, MetricsEventType, MetricsManager,
|
||||
PerformanceMetrics, StreamClientConfig,
|
||||
ConnectionConfig, MetricsEventType, MetricsManager, PerformanceMetrics, StreamClientConfig,
|
||||
};
|
||||
|
||||
@@ -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:?}");
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
|
||||
Reference in New Issue
Block a user