mirror of
https://github.com/0xfnzero/solana-streamer.git
synced 2026-08-16 18:38:05 +00:00
refactor: convert EventParser to static methods and optimize caching
- Replace instance-based EventParser with static method design - Introduce ParserCache module for protocol config caching - Simplify EventProcessor to pure functional handlers - Optimize metrics: remove min/max tracking, keep last + avg only - Reduce atomic operations in hot path for better performance
This commit is contained in:
@@ -1,14 +1,9 @@
|
||||
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::common::EventMetadata;
|
||||
use crate::streaming::event_parser::core::parser_cache::get_account_configs;
|
||||
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::Protocol;
|
||||
use crate::streaming::grpc::AccountPretty;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -20,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)]
|
||||
@@ -72,188 +55,21 @@ pub struct TokenInfoEvent {
|
||||
pub decimals: u8,
|
||||
}
|
||||
|
||||
/// 账户事件解析器
|
||||
pub type AccountEventParserFn =
|
||||
fn(account: &AccountPretty, metadata: EventMetadata) -> Option<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<UnifiedEvent> {
|
||||
let configs = Self::configs(protocols, event_type_filter);
|
||||
// 直接从 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,7 +91,9 @@ impl AccountEventParser {
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
return event;
|
||||
if event.is_some() {
|
||||
return event;
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
|
||||
@@ -5,18 +5,17 @@ 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,
|
||||
},
|
||||
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,
|
||||
core::{
|
||||
global_state::{
|
||||
add_bonk_dev_address, add_dev_address, is_bonk_dev_address_in_signature,
|
||||
is_dev_address_in_signature,
|
||||
},
|
||||
parser_cache::{
|
||||
build_account_pubkeys_with_cache, get_global_instruction_configs,
|
||||
get_global_program_ids, GenericEventParseConfig,
|
||||
},
|
||||
},
|
||||
Protocol, UnifiedEvent,
|
||||
},
|
||||
@@ -29,170 +28,16 @@ use solana_sdk::{
|
||||
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<UnifiedEvent>;
|
||||
|
||||
/// 指令事件解析器
|
||||
pub type InstructionEventParser =
|
||||
fn(data: &[u8], accounts: &[Pubkey], metadata: EventMetadata) -> Option<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>,
|
||||
@@ -207,7 +52,9 @@ impl EventParser {
|
||||
// 获取交易的指令和账户
|
||||
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() {
|
||||
@@ -221,8 +68,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,
|
||||
@@ -250,7 +99,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,
|
||||
@@ -275,7 +126,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>,
|
||||
@@ -291,7 +143,9 @@ impl EventParser {
|
||||
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() {
|
||||
@@ -300,13 +154,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,
|
||||
@@ -326,7 +182,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,
|
||||
@@ -349,7 +207,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>,
|
||||
@@ -364,7 +223,9 @@ impl EventParser {
|
||||
let adapter_callback = Arc::new(move |event: &UnifiedEvent| {
|
||||
callback(event.clone());
|
||||
});
|
||||
self.parse_versioned_transaction(
|
||||
Self::parse_versioned_transaction(
|
||||
protocols,
|
||||
event_type_filter,
|
||||
&versioned_tx,
|
||||
signature,
|
||||
slot,
|
||||
@@ -380,7 +241,8 @@ impl EventParser {
|
||||
}
|
||||
|
||||
async fn parse_versioned_transaction(
|
||||
&self,
|
||||
protocols: &[Protocol],
|
||||
event_type_filter: Option<&EventTypeFilter>,
|
||||
versioned_tx: &VersionedTransaction,
|
||||
signature: Signature,
|
||||
slot: Option<u64>,
|
||||
@@ -392,7 +254,9 @@ impl EventParser {
|
||||
callback: Arc<dyn for<'a> Fn(&'a UnifiedEvent) + 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,
|
||||
@@ -409,7 +273,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>,
|
||||
@@ -424,7 +289,9 @@ impl EventParser {
|
||||
callback(event.clone());
|
||||
});
|
||||
// 调用原始方法
|
||||
self.parse_grpc_transaction(
|
||||
Self::parse_grpc_transaction(
|
||||
protocols,
|
||||
event_type_filter,
|
||||
grpc_tx,
|
||||
signature,
|
||||
slot,
|
||||
@@ -438,7 +305,8 @@ impl EventParser {
|
||||
}
|
||||
|
||||
async fn parse_grpc_transaction(
|
||||
&self,
|
||||
protocols: &[Protocol],
|
||||
event_type_filter: Option<&EventTypeFilter>,
|
||||
grpc_tx: SubscribeUpdateTransactionInfo,
|
||||
signature: Signature,
|
||||
slot: Option<u64>,
|
||||
@@ -458,7 +326,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;
|
||||
@@ -487,7 +355,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,
|
||||
@@ -507,7 +377,8 @@ 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 UnifiedEvent) + Send + Sync>,
|
||||
@@ -600,7 +471,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),
|
||||
@@ -620,7 +493,6 @@ impl EventParser {
|
||||
/// 通用的内联指令解析方法
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn parse_inner_instruction_event(
|
||||
&self,
|
||||
config: &GenericEventParseConfig,
|
||||
data: &[u8],
|
||||
signature: Signature,
|
||||
@@ -656,7 +528,6 @@ impl EventParser {
|
||||
/// 通用的指令解析方法
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn parse_instruction_event(
|
||||
&self,
|
||||
config: &GenericEventParseConfig,
|
||||
data: &[u8],
|
||||
account_pubkeys: &[Pubkey],
|
||||
@@ -693,7 +564,6 @@ impl EventParser {
|
||||
/// 从内联指令中解析事件数据
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn parse_events_from_inner_instruction(
|
||||
&self,
|
||||
inner_instruction: &CompiledInstruction,
|
||||
signature: Signature,
|
||||
slot: u64,
|
||||
@@ -724,7 +594,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,
|
||||
@@ -743,7 +613,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,
|
||||
@@ -774,7 +643,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,
|
||||
@@ -793,7 +662,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,
|
||||
@@ -807,13 +677,18 @@ impl EventParser {
|
||||
inner_instructions: Option<&InnerInstructions>,
|
||||
callback: Arc<dyn for<'a> Fn(&'a UnifiedEvent) + 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
|
||||
@@ -833,18 +708,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,
|
||||
@@ -870,7 +742,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,
|
||||
@@ -881,7 +753,7 @@ impl EventParser {
|
||||
transaction_index,
|
||||
&config,
|
||||
);
|
||||
if result.len() > 0 {
|
||||
if !result.is_empty() {
|
||||
return Some(result[0].clone());
|
||||
}
|
||||
}
|
||||
@@ -922,7 +794,7 @@ impl EventParser {
|
||||
}
|
||||
// 设置处理时间(使用高性能时钟)
|
||||
event.metadata_mut().handle_us = elapsed_micros_since(recv_us);
|
||||
event = process_event(event, bot_wallet);
|
||||
event = Self::process_event(event, bot_wallet);
|
||||
callback(&event);
|
||||
}
|
||||
Ok(())
|
||||
@@ -932,7 +804,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,
|
||||
@@ -946,13 +819,18 @@ impl EventParser {
|
||||
inner_instructions: Option<&yellowstone_grpc_proto::prelude::InnerInstructions>,
|
||||
callback: Arc<dyn for<'a> Fn(&'a UnifiedEvent) + 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
|
||||
@@ -972,18 +850,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,
|
||||
@@ -1009,7 +884,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,
|
||||
@@ -1020,7 +895,7 @@ impl EventParser {
|
||||
transaction_index,
|
||||
&config,
|
||||
);
|
||||
if result.len() > 0 {
|
||||
if !result.is_empty() {
|
||||
return Some(result[0].clone());
|
||||
}
|
||||
}
|
||||
@@ -1061,81 +936,90 @@ impl EventParser {
|
||||
}
|
||||
// 设置处理时间(使用高性能时钟)
|
||||
event.metadata_mut().handle_us = elapsed_micros_since(recv_us);
|
||||
event = process_event(event, bot_wallet);
|
||||
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: UnifiedEvent, bot_wallet: Option<Pubkey>) -> UnifiedEvent {
|
||||
let signature = event.metadata().signature; // Copy the signature to avoid borrowing issues
|
||||
match event {
|
||||
UnifiedEvent::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);
|
||||
fn process_event(mut event: UnifiedEvent, bot_wallet: Option<Pubkey>) -> UnifiedEvent {
|
||||
let signature = event.metadata().signature; // Copy the signature to avoid borrowing issues
|
||||
match event {
|
||||
UnifiedEvent::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);
|
||||
}
|
||||
UnifiedEvent::PumpFunCreateTokenEvent(token_info)
|
||||
}
|
||||
UnifiedEvent::PumpFunCreateTokenEvent(token_info)
|
||||
}
|
||||
UnifiedEvent::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;
|
||||
UnifiedEvent::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
|
||||
};
|
||||
}
|
||||
UnifiedEvent::PumpFunTradeEvent(trade_info)
|
||||
}
|
||||
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 };
|
||||
UnifiedEvent::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;
|
||||
}
|
||||
UnifiedEvent::PumpSwapBuyEvent(trade_info)
|
||||
}
|
||||
UnifiedEvent::PumpFunTradeEvent(trade_info)
|
||||
}
|
||||
UnifiedEvent::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;
|
||||
UnifiedEvent::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;
|
||||
}
|
||||
UnifiedEvent::PumpSwapSellEvent(trade_info)
|
||||
}
|
||||
UnifiedEvent::PumpSwapBuyEvent(trade_info)
|
||||
}
|
||||
UnifiedEvent::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;
|
||||
UnifiedEvent::BonkPoolCreateEvent(pool_info) => {
|
||||
add_bonk_dev_address(&signature, pool_info.creator);
|
||||
UnifiedEvent::BonkPoolCreateEvent(pool_info)
|
||||
}
|
||||
UnifiedEvent::PumpSwapSellEvent(trade_info)
|
||||
}
|
||||
UnifiedEvent::BonkPoolCreateEvent(pool_info) => {
|
||||
add_bonk_dev_address(&signature, pool_info.creator);
|
||||
UnifiedEvent::BonkPoolCreateEvent(pool_info)
|
||||
}
|
||||
UnifiedEvent::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;
|
||||
UnifiedEvent::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;
|
||||
}
|
||||
UnifiedEvent::BonkTradeEvent(trade_info)
|
||||
}
|
||||
UnifiedEvent::BonkTradeEvent(trade_info)
|
||||
_ => event,
|
||||
}
|
||||
_ => event,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
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 parser_cache::{
|
||||
AccountEventParseConfig, AccountEventParserFn, GenericEventParseConfig,
|
||||
InnerInstructionEventParser, InstructionEventParser, get_account_configs,
|
||||
};
|
||||
|
||||
pub mod event_parser;
|
||||
|
||||
@@ -0,0 +1,604 @@
|
||||
//! # 事件解析器配置缓存模块
|
||||
//!
|
||||
//! 本模块统一管理所有事件解析器的配置和缓存,包括:
|
||||
//! - 指令事件解析器(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, UnifiedEvent,
|
||||
},
|
||||
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<UnifiedEvent>;
|
||||
|
||||
/// 指令事件解析器函数类型
|
||||
///
|
||||
/// 用于解析普通指令(Instruction)生成的事件,需要账户信息
|
||||
pub type InstructionEventParser =
|
||||
fn(data: &[u8], accounts: &[Pubkey], metadata: EventMetadata) -> Option<UnifiedEvent>;
|
||||
|
||||
/// 指令事件解析器配置
|
||||
///
|
||||
/// 定义如何解析特定协议的指令事件
|
||||
#[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<UnifiedEvent>;
|
||||
|
||||
/// 账户事件解析器配置
|
||||
///
|
||||
/// 定义如何解析特定协议的账户事件
|
||||
#[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
|
||||
}
|
||||
Reference in New Issue
Block a user