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