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