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