refactor: restructure event handling from trait objects to enum pattern

- Convert UnifiedEvent from trait object (Box<dyn UnifiedEvent>) to concrete enum type
  - Remove match_event! macro in favor of native match expressions
  - Simplify event metadata access: event.event_type() -> event.metadata().event_type
  - Unify event callback signatures: Fn(Box<dyn UnifiedEvent>) -> Fn(UnifiedEvent)
  - Update all example code to use the new enum-based event system
  - Optimize type system to reduce runtime overhead and improve type safety

  Affected scope:
  - Core event parser and processor
  - All protocol event definitions (PumpFun, PumpSwap, Bonk, Raydium series, etc.)
  - gRPC and Shred streaming modules
  - All example code
This commit is contained in:
ysq
2025-10-10 18:19:44 +08:00
parent 77caa39f0c
commit 312a675a40
43 changed files with 1013 additions and 1839 deletions
@@ -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};
@@ -67,32 +66,32 @@ pub fn pump_swap_buy_event_log_decode(data: &[u8]) -> Option<PumpSwapBuyEvent> {
}
// 使用宏生成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
);
// 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)]
@@ -150,32 +149,32 @@ pub fn pump_swap_sell_event_log_decode(data: &[u8]) -> Option<PumpSwapSellEvent>
}
// 使用宏生成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
);
// 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)]
@@ -219,29 +218,29 @@ 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
);
// 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)]
@@ -283,25 +282,25 @@ 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
);
// 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)]
@@ -343,25 +342,25 @@ 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
);
// 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)]
@@ -375,7 +374,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 +387,6 @@ pub struct PumpSwapPoolAccountEvent {
pub rent_epoch: u64,
pub pool: Pool,
}
impl_unified_event!(PumpSwapPoolAccountEvent,);
/// 事件鉴别器常量
pub mod discriminators {
@@ -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<UnifiedEvent> {
if let Some(event) = pump_swap_buy_event_log_decode(data) {
Some(Box::new(PumpSwapBuyEvent { metadata, ..event }))
Some(UnifiedEvent::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<UnifiedEvent> {
if let Some(event) = pump_swap_sell_event_log_decode(data) {
Some(Box::new(PumpSwapSellEvent { metadata, ..event }))
Some(UnifiedEvent::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<UnifiedEvent> {
if let Some(event) = pump_swap_create_pool_event_log_decode(data) {
Some(Box::new(PumpSwapCreatePoolEvent { metadata, ..event }))
Some(UnifiedEvent::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<UnifiedEvent> {
if let Some(event) = pump_swap_deposit_event_log_decode(data) {
Some(Box::new(PumpSwapDepositEvent { metadata, ..event }))
Some(UnifiedEvent::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<UnifiedEvent> {
if let Some(event) = pump_swap_withdraw_event_log_decode(data) {
Some(Box::new(PumpSwapWithdrawEvent { metadata, ..event }))
Some(UnifiedEvent::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<UnifiedEvent> {
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(UnifiedEvent::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<UnifiedEvent> {
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(UnifiedEvent::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<UnifiedEvent> {
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(UnifiedEvent::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<UnifiedEvent> {
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(UnifiedEvent::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<UnifiedEvent> {
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(UnifiedEvent::PumpSwapWithdrawEvent(PumpSwapWithdrawEvent {
metadata,
lp_token_amount_in,
min_base_amount_out,
@@ -5,9 +5,7 @@ use solana_sdk::pubkey::Pubkey;
use crate::streaming::{
event_parser::{
common::EventMetadata,
protocols::pumpswap::{
PumpSwapGlobalConfigAccountEvent, PumpSwapPoolAccountEvent,
},
protocols::pumpswap::{PumpSwapGlobalConfigAccountEvent, PumpSwapPoolAccountEvent},
UnifiedEvent,
},
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<UnifiedEvent> {
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(UnifiedEvent::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<UnifiedEvent> {
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(UnifiedEvent::PumpSwapPoolAccountEvent(PumpSwapPoolAccountEvent {
metadata,
pubkey: account.pubkey,
executable: account.executable,