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