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    RaydiumLaunchlab,
228    RaydiumCpmm,
229    RaydiumClmm,
230    RaydiumAmmV4,
231    OrcaWhirlpool,
232    MeteoraPools,
233    MeteoraDammV2,
234    MeteoraDlmm,
235    MeteoraDbc,
236}
237
238#[derive(Debug, Clone, Copy, PartialEq, Eq)]
239#[non_exhaustive]
240pub enum EventType {
241    // Block events
242    BlockMeta,
243
244    // RaydiumLaunchlab events
245    RaydiumLaunchlabTrade,
246    RaydiumLaunchlabPoolCreate,
247    RaydiumLaunchlabMigrateAmm,
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 DBC events
340    MeteoraDbcSwap,
341    MeteoraDbcInitializePool,
342    MeteoraDbcCurveComplete,
343
344    // Meteora DLMM events
345    MeteoraDlmmSwap,
346    MeteoraDlmmAddLiquidity,
347    MeteoraDlmmRemoveLiquidity,
348    MeteoraDlmmInitializePool,
349    MeteoraDlmmInitializeBinArray,
350    MeteoraDlmmCreatePosition,
351    MeteoraDlmmClosePosition,
352    MeteoraDlmmClaimFee,
353
354    // Account events
355    TokenAccount,
356    TokenInfo,
357    NonceAccount,
358    AccountPumpFunGlobal,
359    AccountPumpFunBondingCurve,
360    AccountPumpFunFeeConfig,
361    AccountPumpFunSharingConfig,
362    AccountPumpFunGlobalVolumeAccumulator,
363    AccountPumpFunUserVolumeAccumulator,
364
365    AccountPumpSwapGlobalConfig,
366    AccountPumpSwapPool,
367    AccountRaydiumClmmAmmConfig,
368    AccountRaydiumClmmPoolState,
369    AccountRaydiumClmmTickArrayState,
370    AccountRaydiumCpmmAmmConfig,
371    AccountRaydiumCpmmPoolState,
372    AccountOrcaWhirlpool,
373    AccountOrcaPosition,
374    AccountOrcaTickArray,
375    AccountOrcaFeeTier,
376    AccountOrcaWhirlpoolsConfig,
377}
378
379#[derive(Debug, Clone)]
380pub struct EventTypeFilter {
381    pub include_only: Option<Vec<EventType>>,
382    pub exclude_types: Option<Vec<EventType>>,
383}
384
385impl EventTypeFilter {
386    pub fn include_only(types: Vec<EventType>) -> Self {
387        Self { include_only: Some(types), exclude_types: None }
388    }
389
390    pub fn exclude_types(types: Vec<EventType>) -> Self {
391        Self { include_only: None, exclude_types: Some(types) }
392    }
393
394    #[inline]
395    fn includes_any(&self, event_types: &[EventType]) -> bool {
396        event_types.iter().any(|event_type| self.should_include(*event_type))
397    }
398
399    pub fn should_include(&self, event_type: EventType) -> bool {
400        if let Some(ref include_only) = self.include_only {
401            // Direct match
402            if include_only.contains(&event_type) {
403                return true;
404            }
405            if matches!(
406                event_type,
407                EventType::PumpFunBuy | EventType::PumpFunSell | EventType::PumpFunBuyExactSolIn
408            ) {
409                if pumpfun_trade_filter_is_generic(include_only) {
410                    return true;
411                }
412                if event_type == EventType::PumpFunBuyExactSolIn
413                    && pumpfun_buy_filter_is_generic(include_only)
414                {
415                    return true;
416                }
417                return false;
418            }
419            if is_pumpfun_create_family(event_type) {
420                return include_only.iter().any(|t| is_pumpfun_create_family(*t));
421            }
422            if matches!(event_type, EventType::PumpSwapBuy | EventType::PumpSwapSell) {
423                return include_only.contains(&EventType::PumpSwapTrade);
424            }
425            return false;
426        }
427
428        if let Some(ref exclude_types) = self.exclude_types {
429            if exclude_types.contains(&event_type) {
430                return false;
431            }
432            if matches!(
433                event_type,
434                EventType::PumpFunBuy | EventType::PumpFunSell | EventType::PumpFunBuyExactSolIn
435            ) && exclude_types.contains(&EventType::PumpFunTrade)
436            {
437                return false;
438            }
439            if event_type == EventType::PumpFunBuyExactSolIn
440                && exclude_types.contains(&EventType::PumpFunBuy)
441            {
442                return false;
443            }
444            if is_pumpfun_create_family(event_type)
445                && exclude_types.iter().any(|t| is_pumpfun_create_family(*t))
446            {
447                return false;
448            }
449            if matches!(event_type, EventType::PumpSwapBuy | EventType::PumpSwapSell)
450                && exclude_types.contains(&EventType::PumpSwapTrade)
451            {
452                return false;
453            }
454            return true;
455        }
456
457        true
458    }
459
460    pub fn should_include_dex_event(&self, event: &crate::core::events::DexEvent) -> bool {
461        let Some(event_type) = event_type_from_dex_event(event) else { return true };
462        self.should_include(event_type)
463    }
464
465    #[inline]
466    pub fn includes_block_meta(&self) -> bool {
467        if let Some(ref include_only) = self.include_only {
468            return include_only.contains(&EventType::BlockMeta);
469        }
470        false
471    }
472
473    #[inline]
474    pub fn normalize_dex_event(
475        &self,
476        event: crate::core::events::DexEvent,
477    ) -> crate::core::events::DexEvent {
478        use crate::core::events::DexEvent;
479
480        let Some(ref include_only) = self.include_only else { return event };
481        if pumpfun_trade_filter_is_generic(include_only) {
482            return match event {
483                DexEvent::PumpFunBuy(t)
484                | DexEvent::PumpFunSell(t)
485                | DexEvent::PumpFunBuyExactSolIn(t) => DexEvent::PumpFunTrade(t),
486                other => other,
487            };
488        }
489        if pumpfun_buy_filter_is_generic(include_only) {
490            return match event {
491                DexEvent::PumpFunBuyExactSolIn(t) => DexEvent::PumpFunBuy(t),
492                other => other,
493            };
494        }
495
496        event
497    }
498
499    #[inline]
500    pub fn includes_pumpfun(&self) -> bool {
501        self.includes_any(&[
502            EventType::PumpFunTrade,
503            EventType::PumpFunBuy,
504            EventType::PumpFunSell,
505            EventType::PumpFunBuyExactSolIn,
506            EventType::PumpFunCreate,
507            EventType::PumpFunCreateV2,
508            EventType::PumpFunComplete,
509            EventType::PumpFunMigrate,
510            EventType::PumpFunMigrateBondingCurveCreator,
511        ])
512    }
513
514    #[inline]
515    pub fn includes_meteora_damm_v2(&self) -> bool {
516        self.includes_any(&[
517            EventType::MeteoraDammV2Swap,
518            EventType::MeteoraDammV2AddLiquidity,
519            EventType::MeteoraDammV2CreatePosition,
520            EventType::MeteoraDammV2ClosePosition,
521            EventType::MeteoraDammV2InitializePool,
522            EventType::MeteoraDammV2RemoveLiquidity,
523        ])
524    }
525
526    #[inline]
527    pub fn includes_pump_fees(&self) -> bool {
528        self.includes_any(&[
529            EventType::PumpFeesCreateFeeSharingConfig,
530            EventType::PumpFeesInitializeFeeConfig,
531            EventType::PumpFeesResetFeeSharingConfig,
532            EventType::PumpFeesRevokeFeeSharingAuthority,
533            EventType::PumpFeesTransferFeeSharingAuthority,
534            EventType::PumpFeesUpdateAdmin,
535            EventType::PumpFeesUpdateFeeConfig,
536            EventType::PumpFeesUpdateFeeShares,
537            EventType::PumpFeesUpsertFeeTiers,
538        ])
539    }
540
541    /// Check if PumpSwap protocol events are included in the filter
542    #[inline]
543    pub fn includes_pumpswap(&self) -> bool {
544        self.includes_any(&[
545            EventType::PumpSwapTrade,
546            EventType::PumpSwapBuy,
547            EventType::PumpSwapSell,
548            EventType::PumpSwapCreatePool,
549            EventType::PumpSwapLiquidityAdded,
550            EventType::PumpSwapLiquidityRemoved,
551        ])
552    }
553
554    /// Check if Raydium LaunchLab events are included in the filter.
555    #[inline]
556    pub fn includes_raydium_launchlab(&self) -> bool {
557        self.includes_any(&[
558            EventType::RaydiumLaunchlabTrade,
559            EventType::RaydiumLaunchlabPoolCreate,
560            EventType::RaydiumLaunchlabMigrateAmm,
561        ])
562    }
563
564    #[inline]
565    pub fn includes_raydium_cpmm(&self) -> bool {
566        self.includes_any(&[
567            EventType::RaydiumCpmmSwap,
568            EventType::RaydiumCpmmDeposit,
569            EventType::RaydiumCpmmWithdraw,
570            EventType::RaydiumCpmmInitialize,
571        ])
572    }
573
574    #[inline]
575    pub fn includes_raydium_clmm(&self) -> bool {
576        self.includes_any(&[
577            EventType::RaydiumClmmSwap,
578            EventType::RaydiumClmmCreatePool,
579            EventType::RaydiumClmmOpenPosition,
580            EventType::RaydiumClmmClosePosition,
581            EventType::RaydiumClmmIncreaseLiquidity,
582            EventType::RaydiumClmmDecreaseLiquidity,
583            EventType::RaydiumClmmLiquidityChange,
584            EventType::RaydiumClmmConfigChange,
585            EventType::RaydiumClmmCreatePersonalPosition,
586            EventType::RaydiumClmmLiquidityCalculate,
587            EventType::RaydiumClmmOpenLimitOrder,
588            EventType::RaydiumClmmIncreaseLimitOrder,
589            EventType::RaydiumClmmDecreaseLimitOrder,
590            EventType::RaydiumClmmSettleLimitOrder,
591            EventType::RaydiumClmmUpdateRewardInfos,
592            EventType::RaydiumClmmOpenPositionWithTokenExtNft,
593            EventType::RaydiumClmmCollectFee,
594        ])
595    }
596
597    #[inline]
598    pub fn includes_raydium_amm_v4(&self) -> bool {
599        self.includes_any(&[
600            EventType::RaydiumAmmV4Swap,
601            EventType::RaydiumAmmV4Deposit,
602            EventType::RaydiumAmmV4Withdraw,
603            EventType::RaydiumAmmV4Initialize2,
604            EventType::RaydiumAmmV4WithdrawPnl,
605        ])
606    }
607
608    #[inline]
609    pub fn includes_orca_whirlpool(&self) -> bool {
610        self.includes_any(&[
611            EventType::OrcaWhirlpoolSwap,
612            EventType::OrcaWhirlpoolLiquidityIncreased,
613            EventType::OrcaWhirlpoolLiquidityDecreased,
614            EventType::OrcaWhirlpoolPoolInitialized,
615        ])
616    }
617
618    #[inline]
619    pub fn includes_meteora_pools(&self) -> bool {
620        self.includes_any(&[
621            EventType::MeteoraPoolsSwap,
622            EventType::MeteoraPoolsAddLiquidity,
623            EventType::MeteoraPoolsRemoveLiquidity,
624            EventType::MeteoraPoolsBootstrapLiquidity,
625            EventType::MeteoraPoolsPoolCreated,
626            EventType::MeteoraPoolsSetPoolFees,
627        ])
628    }
629
630    #[inline]
631    pub fn includes_meteora_dlmm(&self) -> bool {
632        self.includes_any(&[
633            EventType::MeteoraDlmmSwap,
634            EventType::MeteoraDlmmAddLiquidity,
635            EventType::MeteoraDlmmRemoveLiquidity,
636            EventType::MeteoraDlmmInitializePool,
637            EventType::MeteoraDlmmInitializeBinArray,
638            EventType::MeteoraDlmmCreatePosition,
639            EventType::MeteoraDlmmClosePosition,
640            EventType::MeteoraDlmmClaimFee,
641        ])
642    }
643
644    #[inline]
645    pub fn includes_meteora_dbc(&self) -> bool {
646        self.includes_any(&[
647            EventType::MeteoraDbcSwap,
648            EventType::MeteoraDbcInitializePool,
649            EventType::MeteoraDbcCurveComplete,
650        ])
651    }
652}
653
654#[inline]
655fn pumpfun_trade_filter_is_generic(include_only: &[EventType]) -> bool {
656    include_only.contains(&EventType::PumpFunTrade)
657        && !include_only.iter().any(|t| {
658            matches!(
659                t,
660                EventType::PumpFunBuy | EventType::PumpFunSell | EventType::PumpFunBuyExactSolIn
661            )
662        })
663}
664
665#[inline]
666fn pumpfun_buy_filter_is_generic(include_only: &[EventType]) -> bool {
667    include_only.contains(&EventType::PumpFunBuy)
668        && !include_only.contains(&EventType::PumpFunBuyExactSolIn)
669}
670
671#[inline]
672fn is_pumpfun_create_family(event_type: EventType) -> bool {
673    matches!(event_type, EventType::PumpFunCreate | EventType::PumpFunCreateV2)
674}
675
676#[inline]
677pub fn event_type_from_dex_event(event: &crate::core::events::DexEvent) -> Option<EventType> {
678    use crate::core::events::DexEvent;
679    match event {
680        DexEvent::PumpFunCreate(_) => Some(EventType::PumpFunCreate),
681        DexEvent::PumpFunCreateV2(_) => Some(EventType::PumpFunCreateV2),
682        DexEvent::PumpFunTrade(_) => Some(EventType::PumpFunTrade),
683        DexEvent::PumpFunBuy(_) => Some(EventType::PumpFunBuy),
684        DexEvent::PumpFunSell(_) => Some(EventType::PumpFunSell),
685        DexEvent::PumpFunBuyExactSolIn(_) => Some(EventType::PumpFunBuyExactSolIn),
686        DexEvent::PumpFunMigrate(_) => Some(EventType::PumpFunMigrate),
687        DexEvent::PumpFeesCreateFeeSharingConfig(_) => {
688            Some(EventType::PumpFeesCreateFeeSharingConfig)
689        }
690        DexEvent::PumpFeesInitializeFeeConfig(_) => Some(EventType::PumpFeesInitializeFeeConfig),
691        DexEvent::PumpFeesResetFeeSharingConfig(_) => {
692            Some(EventType::PumpFeesResetFeeSharingConfig)
693        }
694        DexEvent::PumpFeesRevokeFeeSharingAuthority(_) => {
695            Some(EventType::PumpFeesRevokeFeeSharingAuthority)
696        }
697        DexEvent::PumpFeesTransferFeeSharingAuthority(_) => {
698            Some(EventType::PumpFeesTransferFeeSharingAuthority)
699        }
700        DexEvent::PumpFeesUpdateAdmin(_) => Some(EventType::PumpFeesUpdateAdmin),
701        DexEvent::PumpFeesUpdateFeeConfig(_) => Some(EventType::PumpFeesUpdateFeeConfig),
702        DexEvent::PumpFeesUpdateFeeShares(_) => Some(EventType::PumpFeesUpdateFeeShares),
703        DexEvent::PumpFeesUpsertFeeTiers(_) => Some(EventType::PumpFeesUpsertFeeTiers),
704        DexEvent::PumpFunMigrateBondingCurveCreator(_) => {
705            Some(EventType::PumpFunMigrateBondingCurveCreator)
706        }
707        DexEvent::PumpFunGlobalAccount(_) => Some(EventType::AccountPumpFunGlobal),
708        DexEvent::PumpFunBondingCurveAccount(_) => Some(EventType::AccountPumpFunBondingCurve),
709        DexEvent::PumpFunFeeConfigAccount(_) => Some(EventType::AccountPumpFunFeeConfig),
710        DexEvent::PumpFunSharingConfigAccount(_) => Some(EventType::AccountPumpFunSharingConfig),
711        DexEvent::PumpFunGlobalVolumeAccumulatorAccount(_) => {
712            Some(EventType::AccountPumpFunGlobalVolumeAccumulator)
713        }
714        DexEvent::PumpFunUserVolumeAccumulatorAccount(_) => {
715            Some(EventType::AccountPumpFunUserVolumeAccumulator)
716        }
717        DexEvent::PumpSwapTrade(_) => Some(EventType::PumpSwapTrade),
718        DexEvent::PumpSwapBuy(_) => Some(EventType::PumpSwapBuy),
719        DexEvent::PumpSwapSell(_) => Some(EventType::PumpSwapSell),
720        DexEvent::PumpSwapCreatePool(_) => Some(EventType::PumpSwapCreatePool),
721        DexEvent::PumpSwapLiquidityAdded(_) => Some(EventType::PumpSwapLiquidityAdded),
722        DexEvent::PumpSwapLiquidityRemoved(_) => Some(EventType::PumpSwapLiquidityRemoved),
723        DexEvent::MeteoraDammV2Swap(_) => Some(EventType::MeteoraDammV2Swap),
724        DexEvent::MeteoraDammV2CreatePosition(_) => Some(EventType::MeteoraDammV2CreatePosition),
725        DexEvent::MeteoraDammV2ClosePosition(_) => Some(EventType::MeteoraDammV2ClosePosition),
726        DexEvent::MeteoraDammV2AddLiquidity(_) => Some(EventType::MeteoraDammV2AddLiquidity),
727        DexEvent::MeteoraDammV2RemoveLiquidity(_) => Some(EventType::MeteoraDammV2RemoveLiquidity),
728        DexEvent::MeteoraDammV2InitializePool(_) => Some(EventType::MeteoraDammV2InitializePool),
729        DexEvent::MeteoraDbcSwap(_) => Some(EventType::MeteoraDbcSwap),
730        DexEvent::MeteoraDbcInitializePool(_) => Some(EventType::MeteoraDbcInitializePool),
731        DexEvent::MeteoraDbcCurveComplete(_) => Some(EventType::MeteoraDbcCurveComplete),
732        DexEvent::RaydiumLaunchlabTrade(_) => Some(EventType::RaydiumLaunchlabTrade),
733        DexEvent::RaydiumLaunchlabPoolCreate(_) => Some(EventType::RaydiumLaunchlabPoolCreate),
734        DexEvent::RaydiumLaunchlabMigrateAmm(_) => Some(EventType::RaydiumLaunchlabMigrateAmm),
735        DexEvent::RaydiumClmmSwap(_) => Some(EventType::RaydiumClmmSwap),
736        DexEvent::RaydiumClmmCreatePool(_) => Some(EventType::RaydiumClmmCreatePool),
737        DexEvent::RaydiumClmmOpenPosition(_) => Some(EventType::RaydiumClmmOpenPosition),
738        DexEvent::RaydiumClmmOpenPositionWithTokenExtNft(_) => {
739            Some(EventType::RaydiumClmmOpenPositionWithTokenExtNft)
740        }
741        DexEvent::RaydiumClmmClosePosition(_) => Some(EventType::RaydiumClmmClosePosition),
742        DexEvent::RaydiumClmmIncreaseLiquidity(_) => Some(EventType::RaydiumClmmIncreaseLiquidity),
743        DexEvent::RaydiumClmmDecreaseLiquidity(_) => Some(EventType::RaydiumClmmDecreaseLiquidity),
744        DexEvent::RaydiumClmmLiquidityChange(_) => Some(EventType::RaydiumClmmLiquidityChange),
745        DexEvent::RaydiumClmmConfigChange(_) => Some(EventType::RaydiumClmmConfigChange),
746        DexEvent::RaydiumClmmCreatePersonalPosition(_) => {
747            Some(EventType::RaydiumClmmCreatePersonalPosition)
748        }
749        DexEvent::RaydiumClmmLiquidityCalculate(_) => {
750            Some(EventType::RaydiumClmmLiquidityCalculate)
751        }
752        DexEvent::RaydiumClmmOpenLimitOrder(_) => Some(EventType::RaydiumClmmOpenLimitOrder),
753        DexEvent::RaydiumClmmIncreaseLimitOrder(_) => {
754            Some(EventType::RaydiumClmmIncreaseLimitOrder)
755        }
756        DexEvent::RaydiumClmmDecreaseLimitOrder(_) => {
757            Some(EventType::RaydiumClmmDecreaseLimitOrder)
758        }
759        DexEvent::RaydiumClmmSettleLimitOrder(_) => Some(EventType::RaydiumClmmSettleLimitOrder),
760        DexEvent::RaydiumClmmUpdateRewardInfos(_) => Some(EventType::RaydiumClmmUpdateRewardInfos),
761        DexEvent::RaydiumClmmCollectFee(_) => Some(EventType::RaydiumClmmCollectFee),
762        DexEvent::RaydiumClmmAmmConfigAccount(_) => Some(EventType::AccountRaydiumClmmAmmConfig),
763        DexEvent::RaydiumClmmPoolStateAccount(_) => Some(EventType::AccountRaydiumClmmPoolState),
764        DexEvent::RaydiumClmmTickArrayStateAccount(_) => {
765            Some(EventType::AccountRaydiumClmmTickArrayState)
766        }
767        DexEvent::RaydiumCpmmSwap(_) => Some(EventType::RaydiumCpmmSwap),
768        DexEvent::RaydiumCpmmDeposit(_) => Some(EventType::RaydiumCpmmDeposit),
769        DexEvent::RaydiumCpmmWithdraw(_) => Some(EventType::RaydiumCpmmWithdraw),
770        DexEvent::RaydiumCpmmInitialize(_) => Some(EventType::RaydiumCpmmInitialize),
771        DexEvent::RaydiumCpmmAmmConfigAccount(_) => Some(EventType::AccountRaydiumCpmmAmmConfig),
772        DexEvent::RaydiumCpmmPoolStateAccount(_) => Some(EventType::AccountRaydiumCpmmPoolState),
773        DexEvent::RaydiumAmmV4Swap(_) => Some(EventType::RaydiumAmmV4Swap),
774        DexEvent::RaydiumAmmV4Deposit(_) => Some(EventType::RaydiumAmmV4Deposit),
775        DexEvent::RaydiumAmmV4Initialize2(_) => Some(EventType::RaydiumAmmV4Initialize2),
776        DexEvent::RaydiumAmmV4Withdraw(_) => Some(EventType::RaydiumAmmV4Withdraw),
777        DexEvent::RaydiumAmmV4WithdrawPnl(_) => Some(EventType::RaydiumAmmV4WithdrawPnl),
778        DexEvent::OrcaWhirlpoolSwap(_) => Some(EventType::OrcaWhirlpoolSwap),
779        DexEvent::OrcaWhirlpoolLiquidityIncreased(_) => {
780            Some(EventType::OrcaWhirlpoolLiquidityIncreased)
781        }
782        DexEvent::OrcaWhirlpoolLiquidityDecreased(_) => {
783            Some(EventType::OrcaWhirlpoolLiquidityDecreased)
784        }
785        DexEvent::OrcaWhirlpoolPoolInitialized(_) => Some(EventType::OrcaWhirlpoolPoolInitialized),
786        DexEvent::OrcaWhirlpoolAccount(_) => Some(EventType::AccountOrcaWhirlpool),
787        DexEvent::OrcaPositionAccount(_) => Some(EventType::AccountOrcaPosition),
788        DexEvent::OrcaTickArrayAccount(_) => Some(EventType::AccountOrcaTickArray),
789        DexEvent::OrcaFeeTierAccount(_) => Some(EventType::AccountOrcaFeeTier),
790        DexEvent::OrcaWhirlpoolsConfigAccount(_) => Some(EventType::AccountOrcaWhirlpoolsConfig),
791        DexEvent::MeteoraPoolsSwap(_) => Some(EventType::MeteoraPoolsSwap),
792        DexEvent::MeteoraPoolsAddLiquidity(_) => Some(EventType::MeteoraPoolsAddLiquidity),
793        DexEvent::MeteoraPoolsRemoveLiquidity(_) => Some(EventType::MeteoraPoolsRemoveLiquidity),
794        DexEvent::MeteoraPoolsBootstrapLiquidity(_) => {
795            Some(EventType::MeteoraPoolsBootstrapLiquidity)
796        }
797        DexEvent::MeteoraPoolsPoolCreated(_) => Some(EventType::MeteoraPoolsPoolCreated),
798        DexEvent::MeteoraPoolsSetPoolFees(_) => Some(EventType::MeteoraPoolsSetPoolFees),
799        DexEvent::MeteoraDlmmSwap(_) => Some(EventType::MeteoraDlmmSwap),
800        DexEvent::MeteoraDlmmAddLiquidity(_) => Some(EventType::MeteoraDlmmAddLiquidity),
801        DexEvent::MeteoraDlmmRemoveLiquidity(_) => Some(EventType::MeteoraDlmmRemoveLiquidity),
802        DexEvent::MeteoraDlmmInitializePool(_) => Some(EventType::MeteoraDlmmInitializePool),
803        DexEvent::MeteoraDlmmInitializeBinArray(_) => {
804            Some(EventType::MeteoraDlmmInitializeBinArray)
805        }
806        DexEvent::MeteoraDlmmCreatePosition(_) => Some(EventType::MeteoraDlmmCreatePosition),
807        DexEvent::MeteoraDlmmClosePosition(_) => Some(EventType::MeteoraDlmmClosePosition),
808        DexEvent::MeteoraDlmmClaimFee(_) => Some(EventType::MeteoraDlmmClaimFee),
809        DexEvent::TokenAccount(_) => Some(EventType::TokenAccount),
810        DexEvent::TokenInfo(_) => Some(EventType::TokenInfo),
811        DexEvent::NonceAccount(_) => Some(EventType::NonceAccount),
812        DexEvent::PumpSwapGlobalConfigAccount(_) => Some(EventType::AccountPumpSwapGlobalConfig),
813        DexEvent::PumpSwapPoolAccount(_) => Some(EventType::AccountPumpSwapPool),
814        DexEvent::BlockMeta(_) => Some(EventType::BlockMeta),
815        DexEvent::Error(_) => None,
816    }
817}
818
819#[cfg(test)]
820mod event_type_filter_tests {
821    use super::*;
822
823    #[test]
824    fn generic_trade_filters_cover_specific_trade_variants() {
825        let pump = EventTypeFilter::include_only(vec![EventType::PumpFunTrade]);
826        assert!(pump.should_include(EventType::PumpFunTrade));
827        assert!(pump.should_include(EventType::PumpFunBuy));
828        assert!(pump.should_include(EventType::PumpFunSell));
829        assert!(pump.should_include(EventType::PumpFunBuyExactSolIn));
830
831        let pump_specific = EventTypeFilter::include_only(vec![EventType::PumpFunBuy]);
832        assert!(!pump_specific.should_include(EventType::PumpFunTrade));
833        assert!(pump_specific.should_include(EventType::PumpFunBuy));
834        assert!(!pump_specific.should_include(EventType::PumpFunSell));
835        assert!(pump_specific.should_include(EventType::PumpFunBuyExactSolIn));
836
837        let pump_exact_buy = EventTypeFilter::include_only(vec![EventType::PumpFunBuyExactSolIn]);
838        assert!(!pump_exact_buy.should_include(EventType::PumpFunTrade));
839        assert!(!pump_exact_buy.should_include(EventType::PumpFunBuy));
840        assert!(!pump_exact_buy.should_include(EventType::PumpFunSell));
841        assert!(pump_exact_buy.should_include(EventType::PumpFunBuyExactSolIn));
842
843        let pumpswap = EventTypeFilter::include_only(vec![EventType::PumpSwapTrade]);
844        assert!(pumpswap.should_include(EventType::PumpSwapBuy));
845        assert!(pumpswap.should_include(EventType::PumpSwapSell));
846
847        let exclude_pumpswap = EventTypeFilter::exclude_types(vec![EventType::PumpSwapTrade]);
848        assert!(!exclude_pumpswap.should_include(EventType::PumpSwapBuy));
849        assert!(!exclude_pumpswap.should_include(EventType::PumpSwapSell));
850    }
851
852    #[test]
853    fn generic_pumpfun_trade_filter_normalizes_specific_variants() {
854        use crate::core::events::{DexEvent, PumpFunTradeEvent};
855
856        let filter = EventTypeFilter::include_only(vec![EventType::PumpFunTrade]);
857        let event = DexEvent::PumpFunBuy(PumpFunTradeEvent { is_buy: true, ..Default::default() });
858        assert!(matches!(filter.normalize_dex_event(event), DexEvent::PumpFunTrade(_)));
859
860        let specific_filter =
861            EventTypeFilter::include_only(vec![EventType::PumpFunTrade, EventType::PumpFunBuy]);
862        let event = DexEvent::PumpFunBuy(PumpFunTradeEvent { is_buy: true, ..Default::default() });
863        assert!(matches!(specific_filter.normalize_dex_event(event), DexEvent::PumpFunBuy(_)));
864
865        let buy_filter = EventTypeFilter::include_only(vec![EventType::PumpFunBuy]);
866        let event = DexEvent::PumpFunBuyExactSolIn(PumpFunTradeEvent {
867            is_buy: true,
868            ..Default::default()
869        });
870        assert!(matches!(buy_filter.normalize_dex_event(event), DexEvent::PumpFunBuy(_)));
871
872        let exact_filter = EventTypeFilter::include_only(vec![EventType::PumpFunBuyExactSolIn]);
873        let event = DexEvent::PumpFunBuyExactSolIn(PumpFunTradeEvent {
874            is_buy: true,
875            ..Default::default()
876        });
877        assert!(matches!(
878            exact_filter.normalize_dex_event(event),
879            DexEvent::PumpFunBuyExactSolIn(_)
880        ));
881
882        let create_and_trade_filter =
883            EventTypeFilter::include_only(vec![EventType::PumpFunCreate, EventType::PumpFunTrade]);
884        let event =
885            DexEvent::PumpFunSell(PumpFunTradeEvent { is_buy: false, ..Default::default() });
886        assert!(matches!(
887            create_and_trade_filter.normalize_dex_event(event),
888            DexEvent::PumpFunTrade(_)
889        ));
890    }
891
892    #[test]
893    fn all_protocol_groups_are_filterable() {
894        assert!(EventTypeFilter::include_only(vec![EventType::PumpFunTrade]).includes_pumpfun());
895        assert!(!EventTypeFilter::include_only(vec![EventType::AccountPumpFunGlobal])
896            .includes_pumpfun());
897        assert!(
898            !EventTypeFilter::include_only(vec![EventType::PumpFeesUpdateAdmin]).includes_pumpfun()
899        );
900        assert!(EventTypeFilter::include_only(vec![EventType::PumpSwapTrade]).includes_pumpswap());
901        assert!(EventTypeFilter::include_only(vec![EventType::PumpFeesUpdateFeeShares])
902            .includes_pump_fees());
903        assert!(EventTypeFilter::include_only(vec![EventType::RaydiumLaunchlabTrade])
904            .includes_raydium_launchlab());
905        assert!(
906            EventTypeFilter::include_only(vec![EventType::RaydiumCpmmSwap]).includes_raydium_cpmm()
907        );
908        assert!(
909            EventTypeFilter::include_only(vec![EventType::RaydiumClmmSwap]).includes_raydium_clmm()
910        );
911        assert!(!EventTypeFilter::include_only(vec![EventType::AccountRaydiumClmmPoolState])
912            .includes_raydium_clmm());
913        assert!(EventTypeFilter::include_only(vec![EventType::RaydiumAmmV4Swap])
914            .includes_raydium_amm_v4());
915        assert!(EventTypeFilter::include_only(vec![EventType::OrcaWhirlpoolSwap])
916            .includes_orca_whirlpool());
917        assert!(EventTypeFilter::include_only(vec![EventType::MeteoraPoolsSwap])
918            .includes_meteora_pools());
919        assert!(EventTypeFilter::include_only(vec![EventType::MeteoraDammV2Swap])
920            .includes_meteora_damm_v2());
921        assert!(EventTypeFilter::include_only(vec![EventType::MeteoraDammV2InitializePool])
922            .includes_meteora_damm_v2());
923        assert!(
924            EventTypeFilter::include_only(vec![EventType::MeteoraDlmmSwap]).includes_meteora_dlmm()
925        );
926        assert!(
927            EventTypeFilter::include_only(vec![EventType::MeteoraDbcSwap]).includes_meteora_dbc()
928        );
929    }
930
931    #[test]
932    fn exclude_filters_do_not_skip_whole_protocol_groups() {
933        let raydium = EventTypeFilter::exclude_types(vec![EventType::RaydiumCpmmSwap]);
934        assert!(raydium.includes_raydium_cpmm());
935        assert!(!raydium.should_include(EventType::RaydiumCpmmSwap));
936        assert!(raydium.should_include(EventType::RaydiumCpmmDeposit));
937
938        let all_cpmm = EventTypeFilter::exclude_types(vec![
939            EventType::RaydiumCpmmSwap,
940            EventType::RaydiumCpmmDeposit,
941            EventType::RaydiumCpmmWithdraw,
942            EventType::RaydiumCpmmInitialize,
943        ]);
944        assert!(!all_cpmm.includes_raydium_cpmm());
945
946        let all_launchlab = EventTypeFilter::exclude_types(vec![
947            EventType::RaydiumLaunchlabTrade,
948            EventType::RaydiumLaunchlabPoolCreate,
949            EventType::RaydiumLaunchlabMigrateAmm,
950        ]);
951        assert!(!all_launchlab.includes_raydium_launchlab());
952
953        let pump = EventTypeFilter::exclude_types(vec![EventType::PumpFunBuy]);
954        assert!(pump.includes_pumpfun());
955        assert!(!pump.should_include(EventType::PumpFunBuy));
956        assert!(!pump.should_include(EventType::PumpFunBuyExactSolIn));
957        assert!(pump.should_include(EventType::PumpFunSell));
958    }
959}
960
961#[derive(Debug, Clone)]
962pub struct SlotFilter {
963    pub min_slot: Option<u64>,
964    pub max_slot: Option<u64>,
965}
966
967impl SlotFilter {
968    pub fn new() -> Self {
969        Self { min_slot: None, max_slot: None }
970    }
971
972    pub fn min_slot(mut self, slot: u64) -> Self {
973        self.min_slot = Some(slot);
974        self
975    }
976
977    pub fn max_slot(mut self, slot: u64) -> Self {
978        self.max_slot = Some(slot);
979        self
980    }
981}
982
983impl Default for SlotFilter {
984    fn default() -> Self {
985        Self::new()
986    }
987}