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    /// Backward-compatible alias for Raydium Launchpad / LaunchLab.
228    Bonk,
229    RaydiumLaunchpad,
230    RaydiumCpmm,
231    RaydiumClmm,
232    RaydiumAmmV4,
233    OrcaWhirlpool,
234    MeteoraPools,
235    MeteoraDammV2,
236    MeteoraDlmm,
237}
238
239#[derive(Debug, Clone, Copy, PartialEq, Eq)]
240pub enum EventType {
241    // Block events
242    BlockMeta,
243
244    // Bonk events
245    BonkTrade,
246    BonkPoolCreate,
247    BonkMigrateAmm,
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 DLMM events
340    MeteoraDlmmSwap,
341    MeteoraDlmmAddLiquidity,
342    MeteoraDlmmRemoveLiquidity,
343    MeteoraDlmmInitializePool,
344    MeteoraDlmmInitializeBinArray,
345    MeteoraDlmmCreatePosition,
346    MeteoraDlmmClosePosition,
347    MeteoraDlmmClaimFee,
348
349    // Account events
350    TokenAccount,
351    NonceAccount,
352    AccountPumpFunGlobal,
353    AccountPumpFunBondingCurve,
354    AccountPumpFunFeeConfig,
355    AccountPumpFunSharingConfig,
356    AccountPumpFunGlobalVolumeAccumulator,
357    AccountPumpFunUserVolumeAccumulator,
358
359    AccountPumpSwapGlobalConfig,
360    AccountPumpSwapPool,
361    AccountRaydiumClmmAmmConfig,
362    AccountRaydiumClmmPoolState,
363    AccountRaydiumClmmTickArrayState,
364}
365
366#[derive(Debug, Clone)]
367pub struct EventTypeFilter {
368    pub include_only: Option<Vec<EventType>>,
369    pub exclude_types: Option<Vec<EventType>>,
370}
371
372impl EventTypeFilter {
373    pub fn include_only(types: Vec<EventType>) -> Self {
374        Self { include_only: Some(types), exclude_types: None }
375    }
376
377    pub fn exclude_types(types: Vec<EventType>) -> Self {
378        Self { include_only: None, exclude_types: Some(types) }
379    }
380
381    #[inline]
382    fn includes_group<F>(&self, mut is_group: F) -> bool
383    where
384        F: FnMut(&EventType) -> bool,
385    {
386        if let Some(ref include_only) = self.include_only {
387            return include_only.iter().any(&mut is_group);
388        }
389        // `exclude_types` cannot be used as a protocol-level skip hint. Excluding
390        // one event in a protocol must not suppress every other event in that
391        // protocol; exact exclusion happens after parsing.
392        true
393    }
394
395    pub fn should_include(&self, event_type: EventType) -> bool {
396        if let Some(ref include_only) = self.include_only {
397            // Direct match
398            if include_only.contains(&event_type) {
399                return true;
400            }
401            // Special case: PumpFunTrade discriminator is shared by Buy/Sell/BuyExactSolIn
402            // If filter includes any of these specific types, allow PumpFunTrade through
403            // (secondary filtering will happen after parsing)
404            if event_type == EventType::PumpFunTrade {
405                return include_only.iter().any(|t| {
406                    matches!(
407                        t,
408                        EventType::PumpFunBuy
409                            | EventType::PumpFunSell
410                            | EventType::PumpFunBuyExactSolIn
411                    )
412                });
413            }
414            if matches!(
415                event_type,
416                EventType::PumpFunBuy | EventType::PumpFunSell | EventType::PumpFunBuyExactSolIn
417            ) {
418                return include_only.contains(&EventType::PumpFunTrade);
419            }
420            if matches!(event_type, EventType::PumpSwapBuy | EventType::PumpSwapSell) {
421                return include_only.contains(&EventType::PumpSwapTrade);
422            }
423            return false;
424        }
425
426        if let Some(ref exclude_types) = self.exclude_types {
427            if exclude_types.contains(&event_type) {
428                return false;
429            }
430            if matches!(
431                event_type,
432                EventType::PumpFunBuy | EventType::PumpFunSell | EventType::PumpFunBuyExactSolIn
433            ) && exclude_types.contains(&EventType::PumpFunTrade)
434            {
435                return false;
436            }
437            if matches!(event_type, EventType::PumpSwapBuy | EventType::PumpSwapSell)
438                && exclude_types.contains(&EventType::PumpSwapTrade)
439            {
440                return false;
441            }
442            return true;
443        }
444
445        true
446    }
447
448    pub fn should_include_dex_event(&self, event: &crate::core::events::DexEvent) -> bool {
449        let Some(event_type) = event_type_from_dex_event(event) else { return true };
450        self.should_include(event_type)
451    }
452
453    #[inline]
454    pub fn includes_pumpfun(&self) -> bool {
455        self.includes_group(|t| {
456            matches!(
457                t,
458                EventType::PumpFunTrade
459                    | EventType::PumpFunBuy
460                    | EventType::PumpFunSell
461                    | EventType::PumpFunBuyExactSolIn
462                    | EventType::PumpFunCreate
463                    | EventType::PumpFunCreateV2
464                    | EventType::PumpFunComplete
465                    | EventType::PumpFunMigrate
466                    | EventType::PumpFunMigrateBondingCurveCreator
467                    | EventType::AccountPumpFunGlobal
468                    | EventType::AccountPumpFunBondingCurve
469                    | EventType::AccountPumpFunFeeConfig
470                    | EventType::AccountPumpFunSharingConfig
471                    | EventType::AccountPumpFunGlobalVolumeAccumulator
472                    | EventType::AccountPumpFunUserVolumeAccumulator
473            )
474        })
475    }
476
477    #[inline]
478    pub fn includes_meteora_damm_v2(&self) -> bool {
479        self.includes_group(|t| {
480            matches!(
481                t,
482                EventType::MeteoraDammV2Swap
483                    | EventType::MeteoraDammV2AddLiquidity
484                    | EventType::MeteoraDammV2CreatePosition
485                    | EventType::MeteoraDammV2ClosePosition
486                    | EventType::MeteoraDammV2RemoveLiquidity
487            )
488        })
489    }
490
491    #[inline]
492    pub fn includes_pump_fees(&self) -> bool {
493        self.includes_group(|t| {
494            matches!(
495                t,
496                EventType::PumpFeesCreateFeeSharingConfig
497                    | EventType::PumpFeesInitializeFeeConfig
498                    | EventType::PumpFeesResetFeeSharingConfig
499                    | EventType::PumpFeesRevokeFeeSharingAuthority
500                    | EventType::PumpFeesTransferFeeSharingAuthority
501                    | EventType::PumpFeesUpdateAdmin
502                    | EventType::PumpFeesUpdateFeeConfig
503                    | EventType::PumpFeesUpdateFeeShares
504                    | EventType::PumpFeesUpsertFeeTiers
505            )
506        })
507    }
508
509    /// Check if PumpSwap protocol events are included in the filter
510    #[inline]
511    pub fn includes_pumpswap(&self) -> bool {
512        self.includes_group(|t| {
513            matches!(
514                t,
515                EventType::PumpSwapTrade
516                    | EventType::PumpSwapBuy
517                    | EventType::PumpSwapSell
518                    | EventType::PumpSwapCreatePool
519                    | EventType::PumpSwapLiquidityAdded
520                    | EventType::PumpSwapLiquidityRemoved
521            )
522        })
523    }
524
525    /// Check if Raydium Launchpad (Bonk) events are included in the filter
526    #[inline]
527    pub fn includes_raydium_launchpad(&self) -> bool {
528        self.includes_group(|t| {
529            matches!(
530                t,
531                EventType::BonkTrade | EventType::BonkPoolCreate | EventType::BonkMigrateAmm
532            )
533        })
534    }
535
536    #[inline]
537    pub fn includes_raydium_cpmm(&self) -> bool {
538        self.includes_group(|t| {
539            matches!(
540                t,
541                EventType::RaydiumCpmmSwap
542                    | EventType::RaydiumCpmmDeposit
543                    | EventType::RaydiumCpmmWithdraw
544                    | EventType::RaydiumCpmmInitialize
545            )
546        })
547    }
548
549    #[inline]
550    pub fn includes_raydium_clmm(&self) -> bool {
551        self.includes_group(|t| {
552            matches!(
553                t,
554                EventType::RaydiumClmmSwap
555                    | EventType::RaydiumClmmCreatePool
556                    | EventType::RaydiumClmmOpenPosition
557                    | EventType::RaydiumClmmClosePosition
558                    | EventType::RaydiumClmmIncreaseLiquidity
559                    | EventType::RaydiumClmmDecreaseLiquidity
560                    | EventType::RaydiumClmmLiquidityChange
561                    | EventType::RaydiumClmmConfigChange
562                    | EventType::RaydiumClmmCreatePersonalPosition
563                    | EventType::RaydiumClmmLiquidityCalculate
564                    | EventType::RaydiumClmmOpenLimitOrder
565                    | EventType::RaydiumClmmIncreaseLimitOrder
566                    | EventType::RaydiumClmmDecreaseLimitOrder
567                    | EventType::RaydiumClmmSettleLimitOrder
568                    | EventType::RaydiumClmmUpdateRewardInfos
569                    | EventType::RaydiumClmmOpenPositionWithTokenExtNft
570                    | EventType::RaydiumClmmCollectFee
571                    | EventType::AccountRaydiumClmmAmmConfig
572                    | EventType::AccountRaydiumClmmPoolState
573                    | EventType::AccountRaydiumClmmTickArrayState
574            )
575        })
576    }
577
578    #[inline]
579    pub fn includes_raydium_amm_v4(&self) -> bool {
580        self.includes_group(|t| {
581            matches!(
582                t,
583                EventType::RaydiumAmmV4Swap
584                    | EventType::RaydiumAmmV4Deposit
585                    | EventType::RaydiumAmmV4Withdraw
586                    | EventType::RaydiumAmmV4Initialize2
587                    | EventType::RaydiumAmmV4WithdrawPnl
588            )
589        })
590    }
591
592    #[inline]
593    pub fn includes_orca_whirlpool(&self) -> bool {
594        self.includes_group(|t| {
595            matches!(
596                t,
597                EventType::OrcaWhirlpoolSwap
598                    | EventType::OrcaWhirlpoolLiquidityIncreased
599                    | EventType::OrcaWhirlpoolLiquidityDecreased
600                    | EventType::OrcaWhirlpoolPoolInitialized
601            )
602        })
603    }
604
605    #[inline]
606    pub fn includes_meteora_pools(&self) -> bool {
607        self.includes_group(|t| {
608            matches!(
609                t,
610                EventType::MeteoraPoolsSwap
611                    | EventType::MeteoraPoolsAddLiquidity
612                    | EventType::MeteoraPoolsRemoveLiquidity
613                    | EventType::MeteoraPoolsBootstrapLiquidity
614                    | EventType::MeteoraPoolsPoolCreated
615                    | EventType::MeteoraPoolsSetPoolFees
616            )
617        })
618    }
619
620    #[inline]
621    pub fn includes_meteora_dlmm(&self) -> bool {
622        self.includes_group(|t| {
623            matches!(
624                t,
625                EventType::MeteoraDlmmSwap
626                    | EventType::MeteoraDlmmAddLiquidity
627                    | EventType::MeteoraDlmmRemoveLiquidity
628                    | EventType::MeteoraDlmmInitializePool
629                    | EventType::MeteoraDlmmInitializeBinArray
630                    | EventType::MeteoraDlmmCreatePosition
631                    | EventType::MeteoraDlmmClosePosition
632                    | EventType::MeteoraDlmmClaimFee
633            )
634        })
635    }
636}
637
638#[inline]
639pub fn event_type_from_dex_event(event: &crate::core::events::DexEvent) -> Option<EventType> {
640    use crate::core::events::DexEvent;
641    match event {
642        DexEvent::PumpFunCreate(_) => Some(EventType::PumpFunCreate),
643        DexEvent::PumpFunCreateV2(_) => Some(EventType::PumpFunCreateV2),
644        DexEvent::PumpFunTrade(_) => Some(EventType::PumpFunTrade),
645        DexEvent::PumpFunBuy(_) => Some(EventType::PumpFunBuy),
646        DexEvent::PumpFunSell(_) => Some(EventType::PumpFunSell),
647        DexEvent::PumpFunBuyExactSolIn(_) => Some(EventType::PumpFunBuyExactSolIn),
648        DexEvent::PumpFunMigrate(_) => Some(EventType::PumpFunMigrate),
649        DexEvent::PumpFeesCreateFeeSharingConfig(_) => {
650            Some(EventType::PumpFeesCreateFeeSharingConfig)
651        }
652        DexEvent::PumpFeesInitializeFeeConfig(_) => Some(EventType::PumpFeesInitializeFeeConfig),
653        DexEvent::PumpFeesResetFeeSharingConfig(_) => {
654            Some(EventType::PumpFeesResetFeeSharingConfig)
655        }
656        DexEvent::PumpFeesRevokeFeeSharingAuthority(_) => {
657            Some(EventType::PumpFeesRevokeFeeSharingAuthority)
658        }
659        DexEvent::PumpFeesTransferFeeSharingAuthority(_) => {
660            Some(EventType::PumpFeesTransferFeeSharingAuthority)
661        }
662        DexEvent::PumpFeesUpdateAdmin(_) => Some(EventType::PumpFeesUpdateAdmin),
663        DexEvent::PumpFeesUpdateFeeConfig(_) => Some(EventType::PumpFeesUpdateFeeConfig),
664        DexEvent::PumpFeesUpdateFeeShares(_) => Some(EventType::PumpFeesUpdateFeeShares),
665        DexEvent::PumpFeesUpsertFeeTiers(_) => Some(EventType::PumpFeesUpsertFeeTiers),
666        DexEvent::PumpFunMigrateBondingCurveCreator(_) => {
667            Some(EventType::PumpFunMigrateBondingCurveCreator)
668        }
669        DexEvent::PumpFunGlobalAccount(_) => Some(EventType::AccountPumpFunGlobal),
670        DexEvent::PumpFunBondingCurveAccount(_) => Some(EventType::AccountPumpFunBondingCurve),
671        DexEvent::PumpFunFeeConfigAccount(_) => Some(EventType::AccountPumpFunFeeConfig),
672        DexEvent::PumpFunSharingConfigAccount(_) => Some(EventType::AccountPumpFunSharingConfig),
673        DexEvent::PumpFunGlobalVolumeAccumulatorAccount(_) => {
674            Some(EventType::AccountPumpFunGlobalVolumeAccumulator)
675        }
676        DexEvent::PumpFunUserVolumeAccumulatorAccount(_) => {
677            Some(EventType::AccountPumpFunUserVolumeAccumulator)
678        }
679        DexEvent::PumpSwapTrade(_) => Some(EventType::PumpSwapTrade),
680        DexEvent::PumpSwapBuy(_) => Some(EventType::PumpSwapBuy),
681        DexEvent::PumpSwapSell(_) => Some(EventType::PumpSwapSell),
682        DexEvent::PumpSwapCreatePool(_) => Some(EventType::PumpSwapCreatePool),
683        DexEvent::PumpSwapLiquidityAdded(_) => Some(EventType::PumpSwapLiquidityAdded),
684        DexEvent::PumpSwapLiquidityRemoved(_) => Some(EventType::PumpSwapLiquidityRemoved),
685        DexEvent::MeteoraDammV2Swap(_) => Some(EventType::MeteoraDammV2Swap),
686        DexEvent::MeteoraDammV2CreatePosition(_) => Some(EventType::MeteoraDammV2CreatePosition),
687        DexEvent::MeteoraDammV2ClosePosition(_) => Some(EventType::MeteoraDammV2ClosePosition),
688        DexEvent::MeteoraDammV2AddLiquidity(_) => Some(EventType::MeteoraDammV2AddLiquidity),
689        DexEvent::MeteoraDammV2RemoveLiquidity(_) => Some(EventType::MeteoraDammV2RemoveLiquidity),
690        DexEvent::BonkTrade(_) => Some(EventType::BonkTrade),
691        DexEvent::BonkPoolCreate(_) => Some(EventType::BonkPoolCreate),
692        DexEvent::BonkMigrateAmm(_) => Some(EventType::BonkMigrateAmm),
693        DexEvent::RaydiumClmmSwap(_) => Some(EventType::RaydiumClmmSwap),
694        DexEvent::RaydiumClmmCreatePool(_) => Some(EventType::RaydiumClmmCreatePool),
695        DexEvent::RaydiumClmmOpenPosition(_) => Some(EventType::RaydiumClmmOpenPosition),
696        DexEvent::RaydiumClmmOpenPositionWithTokenExtNft(_) => {
697            Some(EventType::RaydiumClmmOpenPositionWithTokenExtNft)
698        }
699        DexEvent::RaydiumClmmClosePosition(_) => Some(EventType::RaydiumClmmClosePosition),
700        DexEvent::RaydiumClmmIncreaseLiquidity(_) => Some(EventType::RaydiumClmmIncreaseLiquidity),
701        DexEvent::RaydiumClmmDecreaseLiquidity(_) => Some(EventType::RaydiumClmmDecreaseLiquidity),
702        DexEvent::RaydiumClmmLiquidityChange(_) => Some(EventType::RaydiumClmmLiquidityChange),
703        DexEvent::RaydiumClmmConfigChange(_) => Some(EventType::RaydiumClmmConfigChange),
704        DexEvent::RaydiumClmmCreatePersonalPosition(_) => {
705            Some(EventType::RaydiumClmmCreatePersonalPosition)
706        }
707        DexEvent::RaydiumClmmLiquidityCalculate(_) => {
708            Some(EventType::RaydiumClmmLiquidityCalculate)
709        }
710        DexEvent::RaydiumClmmOpenLimitOrder(_) => Some(EventType::RaydiumClmmOpenLimitOrder),
711        DexEvent::RaydiumClmmIncreaseLimitOrder(_) => {
712            Some(EventType::RaydiumClmmIncreaseLimitOrder)
713        }
714        DexEvent::RaydiumClmmDecreaseLimitOrder(_) => {
715            Some(EventType::RaydiumClmmDecreaseLimitOrder)
716        }
717        DexEvent::RaydiumClmmSettleLimitOrder(_) => Some(EventType::RaydiumClmmSettleLimitOrder),
718        DexEvent::RaydiumClmmUpdateRewardInfos(_) => Some(EventType::RaydiumClmmUpdateRewardInfos),
719        DexEvent::RaydiumClmmCollectFee(_) => Some(EventType::RaydiumClmmCollectFee),
720        DexEvent::RaydiumClmmAmmConfigAccount(_) => Some(EventType::AccountRaydiumClmmAmmConfig),
721        DexEvent::RaydiumClmmPoolStateAccount(_) => Some(EventType::AccountRaydiumClmmPoolState),
722        DexEvent::RaydiumClmmTickArrayStateAccount(_) => {
723            Some(EventType::AccountRaydiumClmmTickArrayState)
724        }
725        DexEvent::RaydiumCpmmSwap(_) => Some(EventType::RaydiumCpmmSwap),
726        DexEvent::RaydiumCpmmDeposit(_) => Some(EventType::RaydiumCpmmDeposit),
727        DexEvent::RaydiumCpmmWithdraw(_) => Some(EventType::RaydiumCpmmWithdraw),
728        DexEvent::RaydiumCpmmInitialize(_) => Some(EventType::RaydiumCpmmInitialize),
729        DexEvent::RaydiumAmmV4Swap(_) => Some(EventType::RaydiumAmmV4Swap),
730        DexEvent::RaydiumAmmV4Deposit(_) => Some(EventType::RaydiumAmmV4Deposit),
731        DexEvent::RaydiumAmmV4Initialize2(_) => Some(EventType::RaydiumAmmV4Initialize2),
732        DexEvent::RaydiumAmmV4Withdraw(_) => Some(EventType::RaydiumAmmV4Withdraw),
733        DexEvent::RaydiumAmmV4WithdrawPnl(_) => Some(EventType::RaydiumAmmV4WithdrawPnl),
734        DexEvent::OrcaWhirlpoolSwap(_) => Some(EventType::OrcaWhirlpoolSwap),
735        DexEvent::OrcaWhirlpoolLiquidityIncreased(_) => {
736            Some(EventType::OrcaWhirlpoolLiquidityIncreased)
737        }
738        DexEvent::OrcaWhirlpoolLiquidityDecreased(_) => {
739            Some(EventType::OrcaWhirlpoolLiquidityDecreased)
740        }
741        DexEvent::OrcaWhirlpoolPoolInitialized(_) => Some(EventType::OrcaWhirlpoolPoolInitialized),
742        DexEvent::MeteoraPoolsSwap(_) => Some(EventType::MeteoraPoolsSwap),
743        DexEvent::MeteoraPoolsAddLiquidity(_) => Some(EventType::MeteoraPoolsAddLiquidity),
744        DexEvent::MeteoraPoolsRemoveLiquidity(_) => Some(EventType::MeteoraPoolsRemoveLiquidity),
745        DexEvent::MeteoraPoolsBootstrapLiquidity(_) => {
746            Some(EventType::MeteoraPoolsBootstrapLiquidity)
747        }
748        DexEvent::MeteoraPoolsPoolCreated(_) => Some(EventType::MeteoraPoolsPoolCreated),
749        DexEvent::MeteoraPoolsSetPoolFees(_) => Some(EventType::MeteoraPoolsSetPoolFees),
750        DexEvent::MeteoraDlmmSwap(_) => Some(EventType::MeteoraDlmmSwap),
751        DexEvent::MeteoraDlmmAddLiquidity(_) => Some(EventType::MeteoraDlmmAddLiquidity),
752        DexEvent::MeteoraDlmmRemoveLiquidity(_) => Some(EventType::MeteoraDlmmRemoveLiquidity),
753        DexEvent::MeteoraDlmmInitializePool(_) => Some(EventType::MeteoraDlmmInitializePool),
754        DexEvent::MeteoraDlmmInitializeBinArray(_) => {
755            Some(EventType::MeteoraDlmmInitializeBinArray)
756        }
757        DexEvent::MeteoraDlmmCreatePosition(_) => Some(EventType::MeteoraDlmmCreatePosition),
758        DexEvent::MeteoraDlmmClosePosition(_) => Some(EventType::MeteoraDlmmClosePosition),
759        DexEvent::MeteoraDlmmClaimFee(_) => Some(EventType::MeteoraDlmmClaimFee),
760        DexEvent::TokenAccount(_) => Some(EventType::TokenAccount),
761        DexEvent::NonceAccount(_) => Some(EventType::NonceAccount),
762        DexEvent::PumpSwapGlobalConfigAccount(_) => Some(EventType::AccountPumpSwapGlobalConfig),
763        DexEvent::PumpSwapPoolAccount(_) => Some(EventType::AccountPumpSwapPool),
764        DexEvent::BlockMeta(_) => Some(EventType::BlockMeta),
765        DexEvent::TokenInfo(_) | DexEvent::Error(_) => None,
766    }
767}
768
769#[cfg(test)]
770mod event_type_filter_tests {
771    use super::*;
772
773    #[test]
774    fn generic_trade_filters_cover_specific_trade_variants() {
775        let pump = EventTypeFilter::include_only(vec![EventType::PumpFunTrade]);
776        assert!(pump.should_include(EventType::PumpFunTrade));
777        assert!(pump.should_include(EventType::PumpFunBuy));
778        assert!(pump.should_include(EventType::PumpFunSell));
779        assert!(pump.should_include(EventType::PumpFunBuyExactSolIn));
780
781        let pump_specific = EventTypeFilter::include_only(vec![EventType::PumpFunBuy]);
782        assert!(pump_specific.should_include(EventType::PumpFunTrade));
783
784        let pumpswap = EventTypeFilter::include_only(vec![EventType::PumpSwapTrade]);
785        assert!(pumpswap.should_include(EventType::PumpSwapBuy));
786        assert!(pumpswap.should_include(EventType::PumpSwapSell));
787
788        let exclude_pumpswap = EventTypeFilter::exclude_types(vec![EventType::PumpSwapTrade]);
789        assert!(!exclude_pumpswap.should_include(EventType::PumpSwapBuy));
790        assert!(!exclude_pumpswap.should_include(EventType::PumpSwapSell));
791    }
792
793    #[test]
794    fn all_protocol_groups_are_filterable() {
795        assert!(EventTypeFilter::include_only(vec![EventType::PumpFunTrade]).includes_pumpfun());
796        assert!(EventTypeFilter::include_only(vec![EventType::PumpSwapTrade]).includes_pumpswap());
797        assert!(EventTypeFilter::include_only(vec![EventType::PumpFeesUpdateFeeShares])
798            .includes_pump_fees());
799        assert!(
800            EventTypeFilter::include_only(vec![EventType::BonkTrade]).includes_raydium_launchpad()
801        );
802        assert!(
803            EventTypeFilter::include_only(vec![EventType::RaydiumCpmmSwap]).includes_raydium_cpmm()
804        );
805        assert!(
806            EventTypeFilter::include_only(vec![EventType::RaydiumClmmSwap]).includes_raydium_clmm()
807        );
808        assert!(EventTypeFilter::include_only(vec![EventType::RaydiumAmmV4Swap])
809            .includes_raydium_amm_v4());
810        assert!(EventTypeFilter::include_only(vec![EventType::OrcaWhirlpoolSwap])
811            .includes_orca_whirlpool());
812        assert!(EventTypeFilter::include_only(vec![EventType::MeteoraPoolsSwap])
813            .includes_meteora_pools());
814        assert!(EventTypeFilter::include_only(vec![EventType::MeteoraDammV2Swap])
815            .includes_meteora_damm_v2());
816        assert!(
817            EventTypeFilter::include_only(vec![EventType::MeteoraDlmmSwap]).includes_meteora_dlmm()
818        );
819    }
820
821    #[test]
822    fn exclude_filters_do_not_skip_whole_protocol_groups() {
823        let raydium = EventTypeFilter::exclude_types(vec![EventType::RaydiumCpmmSwap]);
824        assert!(raydium.includes_raydium_cpmm());
825        assert!(!raydium.should_include(EventType::RaydiumCpmmSwap));
826        assert!(raydium.should_include(EventType::RaydiumCpmmDeposit));
827
828        let pump = EventTypeFilter::exclude_types(vec![EventType::PumpFunBuy]);
829        assert!(pump.includes_pumpfun());
830        assert!(!pump.should_include(EventType::PumpFunBuy));
831        assert!(pump.should_include(EventType::PumpFunSell));
832    }
833}
834
835#[derive(Debug, Clone)]
836pub struct SlotFilter {
837    pub min_slot: Option<u64>,
838    pub max_slot: Option<u64>,
839}
840
841impl SlotFilter {
842    pub fn new() -> Self {
843        Self { min_slot: None, max_slot: None }
844    }
845
846    pub fn min_slot(mut self, slot: u64) -> Self {
847        self.min_slot = Some(slot);
848        self
849    }
850
851    pub fn max_slot(mut self, slot: u64) -> Self {
852        self.max_slot = Some(slot);
853        self
854    }
855}
856
857impl Default for SlotFilter {
858    fn default() -> Self {
859        Self::new()
860    }
861}