Skip to main content

sol_parser_sdk/grpc/
types.rs

1use serde::{Deserialize, Serialize};
2use yellowstone_grpc_proto::geyser::{
3    subscribe_request_filter_accounts_filter::Filter as AccountsFilterOneof,
4    subscribe_request_filter_accounts_filter_memcmp::Data as MemcmpDataOneof,
5    SubscribeRequestFilterAccountsFilter, SubscribeRequestFilterAccountsFilterMemcmp,
6};
7
8/// 事件输出顺序模式
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
10pub enum OrderMode {
11    /// 无序模式:收到即输出,超低延迟 (10-20μs)
12    #[default]
13    Unordered,
14    /// 有序模式:按 slot + tx_index 排序后输出
15    /// 同一 slot 内的交易会等待收齐后按 tx_index 排序
16    /// 延迟增加约 1-50ms(取决于 slot 内交易数量)
17    Ordered,
18    /// 流式有序模式:连续序列立即释放,低延迟 + 顺序保证
19    /// 只要收到从 0 开始的连续 tx_index 序列,立即释放
20    /// 延迟约 0.1-5ms,比 Ordered 低 5-50 倍
21    StreamingOrdered,
22    /// 微批次模式:极短时间窗口内收集事件,窗口结束后排序释放
23    /// 窗口大小由 micro_batch_us 配置(默认 100μs)
24    /// 延迟约 50-200μs,接近 Unordered 但保证顺序
25    MicroBatch,
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
29pub struct ClientConfig {
30    /// 是否启用性能监控
31    pub enable_metrics: bool,
32    /// 连接超时时间(毫秒)
33    pub connection_timeout_ms: u64,
34    /// 请求超时时间(毫秒)
35    pub request_timeout_ms: u64,
36    /// 是否启用TLS
37    pub enable_tls: bool,
38    pub max_retries: u32,
39    pub retry_delay_ms: u64,
40    pub max_concurrent_streams: u32,
41    pub keep_alive_interval_ms: u64,
42    pub keep_alive_timeout_ms: u64,
43    pub buffer_size: usize,
44    /// 事件输出顺序模式
45    pub order_mode: OrderMode,
46    /// 有序模式下,slot 超时时间(毫秒)
47    /// 超过此时间未收到新 slot 信号,强制输出当前缓冲的事件
48    pub order_timeout_ms: u64,
49    /// MicroBatch 模式下的时间窗口大小(微秒)
50    /// 默认 100μs,可根据网络状况调整
51    pub micro_batch_us: u64,
52}
53
54impl Default for ClientConfig {
55    fn default() -> Self {
56        Self {
57            enable_metrics: false,
58            connection_timeout_ms: 8000,
59            request_timeout_ms: 15000,
60            enable_tls: true,
61            max_retries: 3,
62            retry_delay_ms: 1000,
63            max_concurrent_streams: 100,
64            keep_alive_interval_ms: 30000,
65            keep_alive_timeout_ms: 5000,
66            buffer_size: 100_000,
67            order_mode: OrderMode::Unordered,
68            order_timeout_ms: 100,
69            micro_batch_us: 100, // 100μs 默认窗口
70        }
71    }
72}
73
74impl ClientConfig {
75    pub fn low_latency() -> Self {
76        Self {
77            enable_metrics: false,
78            connection_timeout_ms: 5000,
79            request_timeout_ms: 10000,
80            enable_tls: true,
81            max_retries: 1,
82            retry_delay_ms: 100,
83            max_concurrent_streams: 200,
84            keep_alive_interval_ms: 10000,
85            keep_alive_timeout_ms: 2000,
86            buffer_size: 100_000,
87            order_mode: OrderMode::Unordered,
88            order_timeout_ms: 50,
89            micro_batch_us: 50, // 50μs 更激进的窗口
90        }
91    }
92
93    pub fn high_throughput() -> Self {
94        Self {
95            enable_metrics: true,
96            connection_timeout_ms: 10000,
97            request_timeout_ms: 30000,
98            enable_tls: true,
99            max_retries: 5,
100            retry_delay_ms: 2000,
101            max_concurrent_streams: 500,
102            keep_alive_interval_ms: 60000,
103            keep_alive_timeout_ms: 10000,
104            buffer_size: 200_000,
105            order_mode: OrderMode::Unordered,
106            order_timeout_ms: 200,
107            micro_batch_us: 200, // 200μs 高吞吐模式
108        }
109    }
110}
111
112#[derive(Debug, Clone)]
113pub struct TransactionFilter {
114    pub account_include: Vec<String>,
115    pub account_exclude: Vec<String>,
116    pub account_required: Vec<String>,
117}
118
119impl TransactionFilter {
120    pub fn new() -> Self {
121        Self {
122            account_include: Vec::new(),
123            account_exclude: Vec::new(),
124            account_required: Vec::new(),
125        }
126    }
127
128    pub fn include_account(mut self, account: impl Into<String>) -> Self {
129        self.account_include.push(account.into());
130        self
131    }
132
133    pub fn exclude_account(mut self, account: impl Into<String>) -> Self {
134        self.account_exclude.push(account.into());
135        self
136    }
137
138    pub fn require_account(mut self, account: impl Into<String>) -> Self {
139        self.account_required.push(account.into());
140        self
141    }
142
143    /// 从程序ID列表创建过滤器
144    pub fn from_program_ids(program_ids: Vec<String>) -> Self {
145        Self {
146            account_include: program_ids,
147            account_exclude: Vec::new(),
148            account_required: Vec::new(),
149        }
150    }
151}
152
153impl Default for TransactionFilter {
154    fn default() -> Self {
155        Self::new()
156    }
157}
158
159#[derive(Debug, Clone)]
160pub struct AccountFilter {
161    pub account: Vec<String>,
162    pub owner: Vec<String>,
163    pub filters: Vec<SubscribeRequestFilterAccountsFilter>,
164}
165
166impl AccountFilter {
167    pub fn new() -> Self {
168        Self { account: Vec::new(), owner: Vec::new(), filters: Vec::new() }
169    }
170
171    pub fn add_account(mut self, account: impl Into<String>) -> Self {
172        self.account.push(account.into());
173        self
174    }
175
176    pub fn add_owner(mut self, owner: impl Into<String>) -> Self {
177        self.owner.push(owner.into());
178        self
179    }
180
181    pub fn add_filter(mut self, filter: SubscribeRequestFilterAccountsFilter) -> Self {
182        self.filters.push(filter);
183        self
184    }
185
186    /// 从程序ID列表创建所有者过滤器
187    pub fn from_program_owners(program_ids: Vec<String>) -> Self {
188        Self { account: Vec::new(), owner: program_ids, filters: Vec::new() }
189    }
190}
191
192impl Default for AccountFilter {
193    fn default() -> Self {
194        Self::new()
195    }
196}
197
198/// Build a memcmp account filter for use in `AccountFilter::filters`.
199/// ATA accounts have mint at offset 0; PumpSwap pool accounts often use offset 32 for mint/pubkey.
200#[inline]
201pub fn account_filter_memcmp(offset: u64, bytes: Vec<u8>) -> SubscribeRequestFilterAccountsFilter {
202    SubscribeRequestFilterAccountsFilter {
203        filter: Some(AccountsFilterOneof::Memcmp(SubscribeRequestFilterAccountsFilterMemcmp {
204            offset,
205            data: Some(MemcmpDataOneof::Bytes(bytes)),
206        })),
207    }
208}
209
210#[derive(Debug, Clone)]
211pub struct AccountFilterData {
212    pub memcmp: Option<AccountFilterMemcmp>,
213    pub datasize: Option<u64>,
214}
215
216#[derive(Debug, Clone)]
217pub struct AccountFilterMemcmp {
218    pub offset: u64,
219    pub bytes: Vec<u8>,
220}
221
222#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
223pub enum Protocol {
224    PumpFun,
225    PumpSwap,
226    PumpFees,
227    RaydiumLaunchlab,
228    RaydiumCpmm,
229    RaydiumClmm,
230    RaydiumAmmV4,
231    OrcaWhirlpool,
232    MeteoraPools,
233    MeteoraDammV2,
234    MeteoraDlmm,
235    MeteoraDbc,
236}
237
238#[derive(Debug, Clone, Copy, PartialEq, Eq)]
239#[non_exhaustive]
240pub enum EventType {
241    // Block events
242    BlockMeta,
243
244    // RaydiumLaunchlab events
245    RaydiumLaunchlabTrade,
246    RaydiumLaunchlabPoolCreate,
247    RaydiumLaunchlabMigrateAmm,
248
249    // PumpFun events
250    PumpFunTrade,         // All trade events (backward compatible)
251    PumpFunBuy,           // Buy events only (filter by ix_name)
252    PumpFunSell,          // Sell events only (filter by ix_name)
253    PumpFunBuyExactSolIn, // BuyExactSolIn events only (filter by ix_name)
254    PumpFunCreate,
255    PumpFunCreateV2, // SPL-22 / Mayhem create
256    PumpFunComplete,
257    PumpFunMigrate,
258    /// Pump fees(`pfeeUx...`,`idls/pump_fees.json` Program data events)
259    PumpFeesCreateFeeSharingConfig,
260    PumpFeesInitializeFeeConfig,
261    PumpFeesResetFeeSharingConfig,
262    PumpFeesRevokeFeeSharingAuthority,
263    PumpFeesTransferFeeSharingAuthority,
264    PumpFeesUpdateAdmin,
265    PumpFeesUpdateFeeConfig,
266    PumpFeesUpdateFeeShares,
267    PumpFeesUpsertFeeTiers,
268    /// Pump.fun:`migrateBondingCurveCreatorEvent`
269    PumpFunMigrateBondingCurveCreator,
270
271    // PumpSwap events
272    PumpSwapTrade,
273    PumpSwapBuy,
274    PumpSwapSell,
275    PumpSwapCreatePool,
276    PumpSwapLiquidityAdded,
277    PumpSwapLiquidityRemoved,
278    // PumpSwapPoolUpdated,
279    // PumpSwapFeesClaimed,
280
281    // Raydium CPMM events
282    RaydiumCpmmSwap,
283    RaydiumCpmmDeposit,
284    RaydiumCpmmWithdraw,
285    RaydiumCpmmInitialize,
286
287    // Raydium CLMM events
288    RaydiumClmmSwap,
289    RaydiumClmmCreatePool,
290    RaydiumClmmOpenPosition,
291    RaydiumClmmClosePosition,
292    RaydiumClmmIncreaseLiquidity,
293    RaydiumClmmDecreaseLiquidity,
294    RaydiumClmmLiquidityChange,
295    RaydiumClmmConfigChange,
296    RaydiumClmmCreatePersonalPosition,
297    RaydiumClmmLiquidityCalculate,
298    RaydiumClmmOpenLimitOrder,
299    RaydiumClmmIncreaseLimitOrder,
300    RaydiumClmmDecreaseLimitOrder,
301    RaydiumClmmSettleLimitOrder,
302    RaydiumClmmUpdateRewardInfos,
303    RaydiumClmmOpenPositionWithTokenExtNft,
304    RaydiumClmmCollectFee,
305
306    // Raydium AMM V4 events
307    RaydiumAmmV4Swap,
308    RaydiumAmmV4Deposit,
309    RaydiumAmmV4Withdraw,
310    RaydiumAmmV4Initialize2,
311    RaydiumAmmV4WithdrawPnl,
312
313    // Orca Whirlpool events
314    OrcaWhirlpoolSwap,
315    OrcaWhirlpoolLiquidityIncreased,
316    OrcaWhirlpoolLiquidityDecreased,
317    OrcaWhirlpoolPoolInitialized,
318
319    // Meteora events
320    MeteoraPoolsSwap,
321    MeteoraPoolsAddLiquidity,
322    MeteoraPoolsRemoveLiquidity,
323    MeteoraPoolsBootstrapLiquidity,
324    MeteoraPoolsPoolCreated,
325    MeteoraPoolsSetPoolFees,
326
327    // Meteora DAMM V2 events
328    MeteoraDammV2Swap,
329    MeteoraDammV2AddLiquidity,
330    MeteoraDammV2RemoveLiquidity,
331    MeteoraDammV2InitializePool,
332    MeteoraDammV2CreatePosition,
333    MeteoraDammV2ClosePosition,
334    // MeteoraDammV2ClaimPositionFee,
335    // MeteoraDammV2InitializeReward,
336    // MeteoraDammV2FundReward,
337    // MeteoraDammV2ClaimReward,
338
339    // Meteora DBC events
340    MeteoraDbcSwap,
341    MeteoraDbcInitializePool,
342    MeteoraDbcCurveComplete,
343
344    // Meteora DLMM events
345    MeteoraDlmmSwap,
346    MeteoraDlmmAddLiquidity,
347    MeteoraDlmmRemoveLiquidity,
348    MeteoraDlmmInitializePool,
349    MeteoraDlmmInitializeBinArray,
350    MeteoraDlmmCreatePosition,
351    MeteoraDlmmClosePosition,
352    MeteoraDlmmClaimFee,
353
354    // Account events
355    TokenAccount,
356    TokenInfo,
357    NonceAccount,
358    AccountPumpFunGlobal,
359    AccountPumpFunBondingCurve,
360    AccountPumpFunFeeConfig,
361    AccountPumpFunSharingConfig,
362    AccountPumpFunGlobalVolumeAccumulator,
363    AccountPumpFunUserVolumeAccumulator,
364
365    AccountPumpSwapGlobalConfig,
366    AccountPumpSwapPool,
367    AccountRaydiumClmmAmmConfig,
368    AccountRaydiumClmmPoolState,
369    AccountRaydiumClmmTickArrayState,
370    AccountRaydiumCpmmAmmConfig,
371    AccountRaydiumCpmmPoolState,
372    AccountOrcaWhirlpool,
373    AccountOrcaPosition,
374    AccountOrcaTickArray,
375    AccountOrcaFeeTier,
376    AccountOrcaWhirlpoolsConfig,
377}
378
379#[derive(Debug, Clone)]
380pub struct EventTypeFilter {
381    pub include_only: Option<Vec<EventType>>,
382    pub exclude_types: Option<Vec<EventType>>,
383}
384
385impl EventTypeFilter {
386    pub fn include_only(types: Vec<EventType>) -> Self {
387        Self { include_only: Some(types), exclude_types: None }
388    }
389
390    pub fn exclude_types(types: Vec<EventType>) -> Self {
391        Self { include_only: None, exclude_types: Some(types) }
392    }
393
394    #[inline]
395    fn includes_any(&self, event_types: &[EventType]) -> bool {
396        event_types.iter().any(|event_type| self.should_include(*event_type))
397    }
398
399    pub fn should_include(&self, event_type: EventType) -> bool {
400        if let Some(ref include_only) = self.include_only {
401            // Direct match
402            if include_only.contains(&event_type) {
403                return true;
404            }
405            // Special case: PumpFunTrade discriminator is shared by Buy/Sell/BuyExactSolIn
406            // If filter includes any of these specific types, allow PumpFunTrade through
407            // (secondary filtering will happen after parsing)
408            if event_type == EventType::PumpFunTrade {
409                return include_only.iter().any(|t| {
410                    matches!(
411                        t,
412                        EventType::PumpFunBuy
413                            | EventType::PumpFunSell
414                            | EventType::PumpFunBuyExactSolIn
415                    )
416                });
417            }
418            if matches!(
419                event_type,
420                EventType::PumpFunBuy | EventType::PumpFunSell | EventType::PumpFunBuyExactSolIn
421            ) {
422                if include_only.contains(&EventType::PumpFunTrade) {
423                    return true;
424                }
425                if is_pumpfun_buy_family(event_type) {
426                    return include_only.iter().any(|t| is_pumpfun_buy_family(*t));
427                }
428                return false;
429            }
430            if is_pumpfun_create_family(event_type) {
431                return include_only.iter().any(|t| is_pumpfun_create_family(*t));
432            }
433            if matches!(event_type, EventType::PumpSwapBuy | EventType::PumpSwapSell) {
434                return include_only.contains(&EventType::PumpSwapTrade);
435            }
436            return false;
437        }
438
439        if let Some(ref exclude_types) = self.exclude_types {
440            if exclude_types.contains(&event_type) {
441                return false;
442            }
443            if matches!(
444                event_type,
445                EventType::PumpFunBuy | EventType::PumpFunSell | EventType::PumpFunBuyExactSolIn
446            ) && exclude_types.contains(&EventType::PumpFunTrade)
447            {
448                return false;
449            }
450            if is_pumpfun_buy_family(event_type)
451                && exclude_types.iter().any(|t| is_pumpfun_buy_family(*t))
452            {
453                return false;
454            }
455            if is_pumpfun_create_family(event_type)
456                && exclude_types.iter().any(|t| is_pumpfun_create_family(*t))
457            {
458                return false;
459            }
460            if matches!(event_type, EventType::PumpSwapBuy | EventType::PumpSwapSell)
461                && exclude_types.contains(&EventType::PumpSwapTrade)
462            {
463                return false;
464            }
465            return true;
466        }
467
468        true
469    }
470
471    pub fn should_include_dex_event(&self, event: &crate::core::events::DexEvent) -> bool {
472        let Some(event_type) = event_type_from_dex_event(event) else { return true };
473        self.should_include(event_type)
474    }
475
476    #[inline]
477    pub fn includes_block_meta(&self) -> bool {
478        if let Some(ref include_only) = self.include_only {
479            return include_only.contains(&EventType::BlockMeta);
480        }
481        false
482    }
483
484    #[inline]
485    pub fn normalize_dex_event(
486        &self,
487        event: crate::core::events::DexEvent,
488    ) -> crate::core::events::DexEvent {
489        use crate::core::events::DexEvent;
490
491        let Some(ref include_only) = self.include_only else { return event };
492        if !include_only.contains(&EventType::PumpFunTrade) {
493            return event;
494        }
495
496        let has_specific_trade_filter = include_only.iter().any(|t| {
497            matches!(
498                t,
499                EventType::PumpFunBuy | EventType::PumpFunSell | EventType::PumpFunBuyExactSolIn
500            )
501        });
502        if has_specific_trade_filter {
503            return event;
504        }
505
506        match event {
507            DexEvent::PumpFunBuy(t)
508            | DexEvent::PumpFunSell(t)
509            | DexEvent::PumpFunBuyExactSolIn(t) => DexEvent::PumpFunTrade(t),
510            other => other,
511        }
512    }
513
514    #[inline]
515    pub fn includes_pumpfun(&self) -> bool {
516        self.includes_any(&[
517            EventType::PumpFunTrade,
518            EventType::PumpFunBuy,
519            EventType::PumpFunSell,
520            EventType::PumpFunBuyExactSolIn,
521            EventType::PumpFunCreate,
522            EventType::PumpFunCreateV2,
523            EventType::PumpFunComplete,
524            EventType::PumpFunMigrate,
525            EventType::PumpFunMigrateBondingCurveCreator,
526        ])
527    }
528
529    #[inline]
530    pub fn includes_meteora_damm_v2(&self) -> bool {
531        self.includes_any(&[
532            EventType::MeteoraDammV2Swap,
533            EventType::MeteoraDammV2AddLiquidity,
534            EventType::MeteoraDammV2CreatePosition,
535            EventType::MeteoraDammV2ClosePosition,
536            EventType::MeteoraDammV2InitializePool,
537            EventType::MeteoraDammV2RemoveLiquidity,
538        ])
539    }
540
541    #[inline]
542    pub fn includes_pump_fees(&self) -> bool {
543        self.includes_any(&[
544            EventType::PumpFeesCreateFeeSharingConfig,
545            EventType::PumpFeesInitializeFeeConfig,
546            EventType::PumpFeesResetFeeSharingConfig,
547            EventType::PumpFeesRevokeFeeSharingAuthority,
548            EventType::PumpFeesTransferFeeSharingAuthority,
549            EventType::PumpFeesUpdateAdmin,
550            EventType::PumpFeesUpdateFeeConfig,
551            EventType::PumpFeesUpdateFeeShares,
552            EventType::PumpFeesUpsertFeeTiers,
553        ])
554    }
555
556    /// Check if PumpSwap protocol events are included in the filter
557    #[inline]
558    pub fn includes_pumpswap(&self) -> bool {
559        self.includes_any(&[
560            EventType::PumpSwapTrade,
561            EventType::PumpSwapBuy,
562            EventType::PumpSwapSell,
563            EventType::PumpSwapCreatePool,
564            EventType::PumpSwapLiquidityAdded,
565            EventType::PumpSwapLiquidityRemoved,
566        ])
567    }
568
569    /// Check if Raydium LaunchLab events are included in the filter.
570    #[inline]
571    pub fn includes_raydium_launchlab(&self) -> bool {
572        self.includes_any(&[
573            EventType::RaydiumLaunchlabTrade,
574            EventType::RaydiumLaunchlabPoolCreate,
575            EventType::RaydiumLaunchlabMigrateAmm,
576        ])
577    }
578
579    #[inline]
580    pub fn includes_raydium_cpmm(&self) -> bool {
581        self.includes_any(&[
582            EventType::RaydiumCpmmSwap,
583            EventType::RaydiumCpmmDeposit,
584            EventType::RaydiumCpmmWithdraw,
585            EventType::RaydiumCpmmInitialize,
586        ])
587    }
588
589    #[inline]
590    pub fn includes_raydium_clmm(&self) -> bool {
591        self.includes_any(&[
592            EventType::RaydiumClmmSwap,
593            EventType::RaydiumClmmCreatePool,
594            EventType::RaydiumClmmOpenPosition,
595            EventType::RaydiumClmmClosePosition,
596            EventType::RaydiumClmmIncreaseLiquidity,
597            EventType::RaydiumClmmDecreaseLiquidity,
598            EventType::RaydiumClmmLiquidityChange,
599            EventType::RaydiumClmmConfigChange,
600            EventType::RaydiumClmmCreatePersonalPosition,
601            EventType::RaydiumClmmLiquidityCalculate,
602            EventType::RaydiumClmmOpenLimitOrder,
603            EventType::RaydiumClmmIncreaseLimitOrder,
604            EventType::RaydiumClmmDecreaseLimitOrder,
605            EventType::RaydiumClmmSettleLimitOrder,
606            EventType::RaydiumClmmUpdateRewardInfos,
607            EventType::RaydiumClmmOpenPositionWithTokenExtNft,
608            EventType::RaydiumClmmCollectFee,
609        ])
610    }
611
612    #[inline]
613    pub fn includes_raydium_amm_v4(&self) -> bool {
614        self.includes_any(&[
615            EventType::RaydiumAmmV4Swap,
616            EventType::RaydiumAmmV4Deposit,
617            EventType::RaydiumAmmV4Withdraw,
618            EventType::RaydiumAmmV4Initialize2,
619            EventType::RaydiumAmmV4WithdrawPnl,
620        ])
621    }
622
623    #[inline]
624    pub fn includes_orca_whirlpool(&self) -> bool {
625        self.includes_any(&[
626            EventType::OrcaWhirlpoolSwap,
627            EventType::OrcaWhirlpoolLiquidityIncreased,
628            EventType::OrcaWhirlpoolLiquidityDecreased,
629            EventType::OrcaWhirlpoolPoolInitialized,
630        ])
631    }
632
633    #[inline]
634    pub fn includes_meteora_pools(&self) -> bool {
635        self.includes_any(&[
636            EventType::MeteoraPoolsSwap,
637            EventType::MeteoraPoolsAddLiquidity,
638            EventType::MeteoraPoolsRemoveLiquidity,
639            EventType::MeteoraPoolsBootstrapLiquidity,
640            EventType::MeteoraPoolsPoolCreated,
641            EventType::MeteoraPoolsSetPoolFees,
642        ])
643    }
644
645    #[inline]
646    pub fn includes_meteora_dlmm(&self) -> bool {
647        self.includes_any(&[
648            EventType::MeteoraDlmmSwap,
649            EventType::MeteoraDlmmAddLiquidity,
650            EventType::MeteoraDlmmRemoveLiquidity,
651            EventType::MeteoraDlmmInitializePool,
652            EventType::MeteoraDlmmInitializeBinArray,
653            EventType::MeteoraDlmmCreatePosition,
654            EventType::MeteoraDlmmClosePosition,
655            EventType::MeteoraDlmmClaimFee,
656        ])
657    }
658
659    #[inline]
660    pub fn includes_meteora_dbc(&self) -> bool {
661        self.includes_any(&[
662            EventType::MeteoraDbcSwap,
663            EventType::MeteoraDbcInitializePool,
664            EventType::MeteoraDbcCurveComplete,
665        ])
666    }
667}
668
669#[inline]
670fn is_pumpfun_buy_family(event_type: EventType) -> bool {
671    matches!(event_type, EventType::PumpFunBuy | EventType::PumpFunBuyExactSolIn)
672}
673
674#[inline]
675fn is_pumpfun_create_family(event_type: EventType) -> bool {
676    matches!(event_type, EventType::PumpFunCreate | EventType::PumpFunCreateV2)
677}
678
679#[inline]
680pub fn event_type_from_dex_event(event: &crate::core::events::DexEvent) -> Option<EventType> {
681    use crate::core::events::DexEvent;
682    match event {
683        DexEvent::PumpFunCreate(_) => Some(EventType::PumpFunCreate),
684        DexEvent::PumpFunCreateV2(_) => Some(EventType::PumpFunCreateV2),
685        DexEvent::PumpFunTrade(_) => Some(EventType::PumpFunTrade),
686        DexEvent::PumpFunBuy(_) => Some(EventType::PumpFunBuy),
687        DexEvent::PumpFunSell(_) => Some(EventType::PumpFunSell),
688        DexEvent::PumpFunBuyExactSolIn(_) => Some(EventType::PumpFunBuyExactSolIn),
689        DexEvent::PumpFunMigrate(_) => Some(EventType::PumpFunMigrate),
690        DexEvent::PumpFeesCreateFeeSharingConfig(_) => {
691            Some(EventType::PumpFeesCreateFeeSharingConfig)
692        }
693        DexEvent::PumpFeesInitializeFeeConfig(_) => Some(EventType::PumpFeesInitializeFeeConfig),
694        DexEvent::PumpFeesResetFeeSharingConfig(_) => {
695            Some(EventType::PumpFeesResetFeeSharingConfig)
696        }
697        DexEvent::PumpFeesRevokeFeeSharingAuthority(_) => {
698            Some(EventType::PumpFeesRevokeFeeSharingAuthority)
699        }
700        DexEvent::PumpFeesTransferFeeSharingAuthority(_) => {
701            Some(EventType::PumpFeesTransferFeeSharingAuthority)
702        }
703        DexEvent::PumpFeesUpdateAdmin(_) => Some(EventType::PumpFeesUpdateAdmin),
704        DexEvent::PumpFeesUpdateFeeConfig(_) => Some(EventType::PumpFeesUpdateFeeConfig),
705        DexEvent::PumpFeesUpdateFeeShares(_) => Some(EventType::PumpFeesUpdateFeeShares),
706        DexEvent::PumpFeesUpsertFeeTiers(_) => Some(EventType::PumpFeesUpsertFeeTiers),
707        DexEvent::PumpFunMigrateBondingCurveCreator(_) => {
708            Some(EventType::PumpFunMigrateBondingCurveCreator)
709        }
710        DexEvent::PumpFunGlobalAccount(_) => Some(EventType::AccountPumpFunGlobal),
711        DexEvent::PumpFunBondingCurveAccount(_) => Some(EventType::AccountPumpFunBondingCurve),
712        DexEvent::PumpFunFeeConfigAccount(_) => Some(EventType::AccountPumpFunFeeConfig),
713        DexEvent::PumpFunSharingConfigAccount(_) => Some(EventType::AccountPumpFunSharingConfig),
714        DexEvent::PumpFunGlobalVolumeAccumulatorAccount(_) => {
715            Some(EventType::AccountPumpFunGlobalVolumeAccumulator)
716        }
717        DexEvent::PumpFunUserVolumeAccumulatorAccount(_) => {
718            Some(EventType::AccountPumpFunUserVolumeAccumulator)
719        }
720        DexEvent::PumpSwapTrade(_) => Some(EventType::PumpSwapTrade),
721        DexEvent::PumpSwapBuy(_) => Some(EventType::PumpSwapBuy),
722        DexEvent::PumpSwapSell(_) => Some(EventType::PumpSwapSell),
723        DexEvent::PumpSwapCreatePool(_) => Some(EventType::PumpSwapCreatePool),
724        DexEvent::PumpSwapLiquidityAdded(_) => Some(EventType::PumpSwapLiquidityAdded),
725        DexEvent::PumpSwapLiquidityRemoved(_) => Some(EventType::PumpSwapLiquidityRemoved),
726        DexEvent::MeteoraDammV2Swap(_) => Some(EventType::MeteoraDammV2Swap),
727        DexEvent::MeteoraDammV2CreatePosition(_) => Some(EventType::MeteoraDammV2CreatePosition),
728        DexEvent::MeteoraDammV2ClosePosition(_) => Some(EventType::MeteoraDammV2ClosePosition),
729        DexEvent::MeteoraDammV2AddLiquidity(_) => Some(EventType::MeteoraDammV2AddLiquidity),
730        DexEvent::MeteoraDammV2RemoveLiquidity(_) => Some(EventType::MeteoraDammV2RemoveLiquidity),
731        DexEvent::MeteoraDammV2InitializePool(_) => Some(EventType::MeteoraDammV2InitializePool),
732        DexEvent::MeteoraDbcSwap(_) => Some(EventType::MeteoraDbcSwap),
733        DexEvent::MeteoraDbcInitializePool(_) => Some(EventType::MeteoraDbcInitializePool),
734        DexEvent::MeteoraDbcCurveComplete(_) => Some(EventType::MeteoraDbcCurveComplete),
735        DexEvent::RaydiumLaunchlabTrade(_) => Some(EventType::RaydiumLaunchlabTrade),
736        DexEvent::RaydiumLaunchlabPoolCreate(_) => Some(EventType::RaydiumLaunchlabPoolCreate),
737        DexEvent::RaydiumLaunchlabMigrateAmm(_) => Some(EventType::RaydiumLaunchlabMigrateAmm),
738        DexEvent::RaydiumClmmSwap(_) => Some(EventType::RaydiumClmmSwap),
739        DexEvent::RaydiumClmmCreatePool(_) => Some(EventType::RaydiumClmmCreatePool),
740        DexEvent::RaydiumClmmOpenPosition(_) => Some(EventType::RaydiumClmmOpenPosition),
741        DexEvent::RaydiumClmmOpenPositionWithTokenExtNft(_) => {
742            Some(EventType::RaydiumClmmOpenPositionWithTokenExtNft)
743        }
744        DexEvent::RaydiumClmmClosePosition(_) => Some(EventType::RaydiumClmmClosePosition),
745        DexEvent::RaydiumClmmIncreaseLiquidity(_) => Some(EventType::RaydiumClmmIncreaseLiquidity),
746        DexEvent::RaydiumClmmDecreaseLiquidity(_) => Some(EventType::RaydiumClmmDecreaseLiquidity),
747        DexEvent::RaydiumClmmLiquidityChange(_) => Some(EventType::RaydiumClmmLiquidityChange),
748        DexEvent::RaydiumClmmConfigChange(_) => Some(EventType::RaydiumClmmConfigChange),
749        DexEvent::RaydiumClmmCreatePersonalPosition(_) => {
750            Some(EventType::RaydiumClmmCreatePersonalPosition)
751        }
752        DexEvent::RaydiumClmmLiquidityCalculate(_) => {
753            Some(EventType::RaydiumClmmLiquidityCalculate)
754        }
755        DexEvent::RaydiumClmmOpenLimitOrder(_) => Some(EventType::RaydiumClmmOpenLimitOrder),
756        DexEvent::RaydiumClmmIncreaseLimitOrder(_) => {
757            Some(EventType::RaydiumClmmIncreaseLimitOrder)
758        }
759        DexEvent::RaydiumClmmDecreaseLimitOrder(_) => {
760            Some(EventType::RaydiumClmmDecreaseLimitOrder)
761        }
762        DexEvent::RaydiumClmmSettleLimitOrder(_) => Some(EventType::RaydiumClmmSettleLimitOrder),
763        DexEvent::RaydiumClmmUpdateRewardInfos(_) => Some(EventType::RaydiumClmmUpdateRewardInfos),
764        DexEvent::RaydiumClmmCollectFee(_) => Some(EventType::RaydiumClmmCollectFee),
765        DexEvent::RaydiumClmmAmmConfigAccount(_) => Some(EventType::AccountRaydiumClmmAmmConfig),
766        DexEvent::RaydiumClmmPoolStateAccount(_) => Some(EventType::AccountRaydiumClmmPoolState),
767        DexEvent::RaydiumClmmTickArrayStateAccount(_) => {
768            Some(EventType::AccountRaydiumClmmTickArrayState)
769        }
770        DexEvent::RaydiumCpmmSwap(_) => Some(EventType::RaydiumCpmmSwap),
771        DexEvent::RaydiumCpmmDeposit(_) => Some(EventType::RaydiumCpmmDeposit),
772        DexEvent::RaydiumCpmmWithdraw(_) => Some(EventType::RaydiumCpmmWithdraw),
773        DexEvent::RaydiumCpmmInitialize(_) => Some(EventType::RaydiumCpmmInitialize),
774        DexEvent::RaydiumCpmmAmmConfigAccount(_) => Some(EventType::AccountRaydiumCpmmAmmConfig),
775        DexEvent::RaydiumCpmmPoolStateAccount(_) => Some(EventType::AccountRaydiumCpmmPoolState),
776        DexEvent::RaydiumAmmV4Swap(_) => Some(EventType::RaydiumAmmV4Swap),
777        DexEvent::RaydiumAmmV4Deposit(_) => Some(EventType::RaydiumAmmV4Deposit),
778        DexEvent::RaydiumAmmV4Initialize2(_) => Some(EventType::RaydiumAmmV4Initialize2),
779        DexEvent::RaydiumAmmV4Withdraw(_) => Some(EventType::RaydiumAmmV4Withdraw),
780        DexEvent::RaydiumAmmV4WithdrawPnl(_) => Some(EventType::RaydiumAmmV4WithdrawPnl),
781        DexEvent::OrcaWhirlpoolSwap(_) => Some(EventType::OrcaWhirlpoolSwap),
782        DexEvent::OrcaWhirlpoolLiquidityIncreased(_) => {
783            Some(EventType::OrcaWhirlpoolLiquidityIncreased)
784        }
785        DexEvent::OrcaWhirlpoolLiquidityDecreased(_) => {
786            Some(EventType::OrcaWhirlpoolLiquidityDecreased)
787        }
788        DexEvent::OrcaWhirlpoolPoolInitialized(_) => Some(EventType::OrcaWhirlpoolPoolInitialized),
789        DexEvent::OrcaWhirlpoolAccount(_) => Some(EventType::AccountOrcaWhirlpool),
790        DexEvent::OrcaPositionAccount(_) => Some(EventType::AccountOrcaPosition),
791        DexEvent::OrcaTickArrayAccount(_) => Some(EventType::AccountOrcaTickArray),
792        DexEvent::OrcaFeeTierAccount(_) => Some(EventType::AccountOrcaFeeTier),
793        DexEvent::OrcaWhirlpoolsConfigAccount(_) => Some(EventType::AccountOrcaWhirlpoolsConfig),
794        DexEvent::MeteoraPoolsSwap(_) => Some(EventType::MeteoraPoolsSwap),
795        DexEvent::MeteoraPoolsAddLiquidity(_) => Some(EventType::MeteoraPoolsAddLiquidity),
796        DexEvent::MeteoraPoolsRemoveLiquidity(_) => Some(EventType::MeteoraPoolsRemoveLiquidity),
797        DexEvent::MeteoraPoolsBootstrapLiquidity(_) => {
798            Some(EventType::MeteoraPoolsBootstrapLiquidity)
799        }
800        DexEvent::MeteoraPoolsPoolCreated(_) => Some(EventType::MeteoraPoolsPoolCreated),
801        DexEvent::MeteoraPoolsSetPoolFees(_) => Some(EventType::MeteoraPoolsSetPoolFees),
802        DexEvent::MeteoraDlmmSwap(_) => Some(EventType::MeteoraDlmmSwap),
803        DexEvent::MeteoraDlmmAddLiquidity(_) => Some(EventType::MeteoraDlmmAddLiquidity),
804        DexEvent::MeteoraDlmmRemoveLiquidity(_) => Some(EventType::MeteoraDlmmRemoveLiquidity),
805        DexEvent::MeteoraDlmmInitializePool(_) => Some(EventType::MeteoraDlmmInitializePool),
806        DexEvent::MeteoraDlmmInitializeBinArray(_) => {
807            Some(EventType::MeteoraDlmmInitializeBinArray)
808        }
809        DexEvent::MeteoraDlmmCreatePosition(_) => Some(EventType::MeteoraDlmmCreatePosition),
810        DexEvent::MeteoraDlmmClosePosition(_) => Some(EventType::MeteoraDlmmClosePosition),
811        DexEvent::MeteoraDlmmClaimFee(_) => Some(EventType::MeteoraDlmmClaimFee),
812        DexEvent::TokenAccount(_) => Some(EventType::TokenAccount),
813        DexEvent::TokenInfo(_) => Some(EventType::TokenInfo),
814        DexEvent::NonceAccount(_) => Some(EventType::NonceAccount),
815        DexEvent::PumpSwapGlobalConfigAccount(_) => Some(EventType::AccountPumpSwapGlobalConfig),
816        DexEvent::PumpSwapPoolAccount(_) => Some(EventType::AccountPumpSwapPool),
817        DexEvent::BlockMeta(_) => Some(EventType::BlockMeta),
818        DexEvent::Error(_) => None,
819    }
820}
821
822#[cfg(test)]
823mod event_type_filter_tests {
824    use super::*;
825
826    #[test]
827    fn generic_trade_filters_cover_specific_trade_variants() {
828        let pump = EventTypeFilter::include_only(vec![EventType::PumpFunTrade]);
829        assert!(pump.should_include(EventType::PumpFunTrade));
830        assert!(pump.should_include(EventType::PumpFunBuy));
831        assert!(pump.should_include(EventType::PumpFunSell));
832        assert!(pump.should_include(EventType::PumpFunBuyExactSolIn));
833
834        let pump_specific = EventTypeFilter::include_only(vec![EventType::PumpFunBuy]);
835        assert!(pump_specific.should_include(EventType::PumpFunTrade));
836        assert!(pump_specific.should_include(EventType::PumpFunBuyExactSolIn));
837
838        let pump_exact_buy = EventTypeFilter::include_only(vec![EventType::PumpFunBuyExactSolIn]);
839        assert!(pump_exact_buy.should_include(EventType::PumpFunTrade));
840        assert!(pump_exact_buy.should_include(EventType::PumpFunBuy));
841
842        let pumpswap = EventTypeFilter::include_only(vec![EventType::PumpSwapTrade]);
843        assert!(pumpswap.should_include(EventType::PumpSwapBuy));
844        assert!(pumpswap.should_include(EventType::PumpSwapSell));
845
846        let exclude_pumpswap = EventTypeFilter::exclude_types(vec![EventType::PumpSwapTrade]);
847        assert!(!exclude_pumpswap.should_include(EventType::PumpSwapBuy));
848        assert!(!exclude_pumpswap.should_include(EventType::PumpSwapSell));
849    }
850
851    #[test]
852    fn generic_pumpfun_trade_filter_normalizes_specific_variants() {
853        use crate::core::events::{DexEvent, PumpFunTradeEvent};
854
855        let filter = EventTypeFilter::include_only(vec![EventType::PumpFunTrade]);
856        let event = DexEvent::PumpFunBuy(PumpFunTradeEvent { is_buy: true, ..Default::default() });
857        assert!(matches!(filter.normalize_dex_event(event), DexEvent::PumpFunTrade(_)));
858
859        let specific_filter =
860            EventTypeFilter::include_only(vec![EventType::PumpFunTrade, EventType::PumpFunBuy]);
861        let event = DexEvent::PumpFunBuy(PumpFunTradeEvent { is_buy: true, ..Default::default() });
862        assert!(matches!(specific_filter.normalize_dex_event(event), DexEvent::PumpFunBuy(_)));
863    }
864
865    #[test]
866    fn all_protocol_groups_are_filterable() {
867        assert!(EventTypeFilter::include_only(vec![EventType::PumpFunTrade]).includes_pumpfun());
868        assert!(!EventTypeFilter::include_only(vec![EventType::AccountPumpFunGlobal])
869            .includes_pumpfun());
870        assert!(
871            !EventTypeFilter::include_only(vec![EventType::PumpFeesUpdateAdmin]).includes_pumpfun()
872        );
873        assert!(EventTypeFilter::include_only(vec![EventType::PumpSwapTrade]).includes_pumpswap());
874        assert!(EventTypeFilter::include_only(vec![EventType::PumpFeesUpdateFeeShares])
875            .includes_pump_fees());
876        assert!(EventTypeFilter::include_only(vec![EventType::RaydiumLaunchlabTrade])
877            .includes_raydium_launchlab());
878        assert!(
879            EventTypeFilter::include_only(vec![EventType::RaydiumCpmmSwap]).includes_raydium_cpmm()
880        );
881        assert!(
882            EventTypeFilter::include_only(vec![EventType::RaydiumClmmSwap]).includes_raydium_clmm()
883        );
884        assert!(!EventTypeFilter::include_only(vec![EventType::AccountRaydiumClmmPoolState])
885            .includes_raydium_clmm());
886        assert!(EventTypeFilter::include_only(vec![EventType::RaydiumAmmV4Swap])
887            .includes_raydium_amm_v4());
888        assert!(EventTypeFilter::include_only(vec![EventType::OrcaWhirlpoolSwap])
889            .includes_orca_whirlpool());
890        assert!(EventTypeFilter::include_only(vec![EventType::MeteoraPoolsSwap])
891            .includes_meteora_pools());
892        assert!(EventTypeFilter::include_only(vec![EventType::MeteoraDammV2Swap])
893            .includes_meteora_damm_v2());
894        assert!(EventTypeFilter::include_only(vec![EventType::MeteoraDammV2InitializePool])
895            .includes_meteora_damm_v2());
896        assert!(
897            EventTypeFilter::include_only(vec![EventType::MeteoraDlmmSwap]).includes_meteora_dlmm()
898        );
899        assert!(
900            EventTypeFilter::include_only(vec![EventType::MeteoraDbcSwap]).includes_meteora_dbc()
901        );
902    }
903
904    #[test]
905    fn exclude_filters_do_not_skip_whole_protocol_groups() {
906        let raydium = EventTypeFilter::exclude_types(vec![EventType::RaydiumCpmmSwap]);
907        assert!(raydium.includes_raydium_cpmm());
908        assert!(!raydium.should_include(EventType::RaydiumCpmmSwap));
909        assert!(raydium.should_include(EventType::RaydiumCpmmDeposit));
910
911        let all_cpmm = EventTypeFilter::exclude_types(vec![
912            EventType::RaydiumCpmmSwap,
913            EventType::RaydiumCpmmDeposit,
914            EventType::RaydiumCpmmWithdraw,
915            EventType::RaydiumCpmmInitialize,
916        ]);
917        assert!(!all_cpmm.includes_raydium_cpmm());
918
919        let all_launchlab = EventTypeFilter::exclude_types(vec![
920            EventType::RaydiumLaunchlabTrade,
921            EventType::RaydiumLaunchlabPoolCreate,
922            EventType::RaydiumLaunchlabMigrateAmm,
923        ]);
924        assert!(!all_launchlab.includes_raydium_launchlab());
925
926        let pump = EventTypeFilter::exclude_types(vec![EventType::PumpFunBuy]);
927        assert!(pump.includes_pumpfun());
928        assert!(!pump.should_include(EventType::PumpFunBuy));
929        assert!(!pump.should_include(EventType::PumpFunBuyExactSolIn));
930        assert!(pump.should_include(EventType::PumpFunSell));
931    }
932}
933
934#[derive(Debug, Clone)]
935pub struct SlotFilter {
936    pub min_slot: Option<u64>,
937    pub max_slot: Option<u64>,
938}
939
940impl SlotFilter {
941    pub fn new() -> Self {
942        Self { min_slot: None, max_slot: None }
943    }
944
945    pub fn min_slot(mut self, slot: u64) -> Self {
946        self.min_slot = Some(slot);
947        self
948    }
949
950    pub fn max_slot(mut self, slot: u64) -> Self {
951        self.max_slot = Some(slot);
952        self
953    }
954}
955
956impl Default for SlotFilter {
957    fn default() -> Self {
958        Self::new()
959    }
960}