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: 8192,
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: 16384,
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: 32768,
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    Bonk,
227    RaydiumCpmm,
228    RaydiumClmm,
229    RaydiumAmmV4,
230    MeteoraDammV2,
231}
232
233#[derive(Debug, Clone, Copy, PartialEq, Eq)]
234pub enum EventType {
235    // Block events
236    BlockMeta,
237
238    // Bonk events
239    BonkTrade,
240    BonkPoolCreate,
241    BonkMigrateAmm,
242
243    // PumpFun events
244    PumpFunTrade,         // All trade events (backward compatible)
245    PumpFunBuy,           // Buy events only (filter by ix_name)
246    PumpFunSell,          // Sell events only (filter by ix_name)
247    PumpFunBuyExactSolIn, // BuyExactSolIn events only (filter by ix_name)
248    PumpFunCreate,
249    PumpFunCreateV2, // SPL-22 / Mayhem create
250    PumpFunComplete,
251    PumpFunMigrate,
252    /// Pump fees(`pfeeUx...`,`idls/pump_fees.json` Program data events)
253    PumpFeesCreateFeeSharingConfig,
254    PumpFeesInitializeFeeConfig,
255    PumpFeesResetFeeSharingConfig,
256    PumpFeesRevokeFeeSharingAuthority,
257    PumpFeesTransferFeeSharingAuthority,
258    PumpFeesUpdateAdmin,
259    PumpFeesUpdateFeeConfig,
260    PumpFeesUpdateFeeShares,
261    PumpFeesUpsertFeeTiers,
262    /// Pump.fun:`migrateBondingCurveCreatorEvent`
263    PumpFunMigrateBondingCurveCreator,
264
265    // PumpSwap events
266    PumpSwapBuy,
267    PumpSwapSell,
268    PumpSwapCreatePool,
269    PumpSwapLiquidityAdded,
270    PumpSwapLiquidityRemoved,
271    // PumpSwapPoolUpdated,
272    // PumpSwapFeesClaimed,
273
274    // Raydium CPMM events
275    // RaydiumCpmmSwap,
276    // RaydiumCpmmDeposit,
277    // RaydiumCpmmWithdraw,
278    // RaydiumCpmmInitialize,
279
280    // Raydium CLMM events
281    // RaydiumClmmSwap,
282    // RaydiumClmmCreatePool,
283    // RaydiumClmmOpenPosition,
284    // RaydiumClmmClosePosition,
285    // RaydiumClmmIncreaseLiquidity,
286    // RaydiumClmmDecreaseLiquidity,
287    // RaydiumClmmOpenPositionWithTokenExtNft,
288    // RaydiumClmmCollectFee,
289
290    // Raydium AMM V4 events
291    // RaydiumAmmV4Swap,
292    // RaydiumAmmV4Deposit,
293    // RaydiumAmmV4Withdraw,
294    // RaydiumAmmV4Initialize2,
295    // RaydiumAmmV4WithdrawPnl,
296
297    // Orca Whirlpool events
298    // OrcaWhirlpoolSwap,
299    // OrcaWhirlpoolLiquidityIncreased,
300    // OrcaWhirlpoolLiquidityDecreased,
301    // OrcaWhirlpoolPoolInitialized,
302
303    // Meteora events
304    // MeteoraPoolsSwap,
305    // MeteoraPoolsAddLiquidity,
306    // MeteoraPoolsRemoveLiquidity,
307    // MeteoraPoolsBootstrapLiquidity,
308    // MeteoraPoolsPoolCreated,
309    // MeteoraPoolsSetPoolFees,
310
311    // Meteora DAMM V2 events
312    MeteoraDammV2Swap,
313    MeteoraDammV2AddLiquidity,
314    MeteoraDammV2RemoveLiquidity,
315    // MeteoraDammV2InitializePool,
316    MeteoraDammV2CreatePosition,
317    MeteoraDammV2ClosePosition,
318    // MeteoraDammV2ClaimPositionFee,
319    // MeteoraDammV2InitializeReward,
320    // MeteoraDammV2FundReward,
321    // MeteoraDammV2ClaimReward,
322
323    // Account events
324    TokenAccount,
325    NonceAccount,
326    AccountPumpFunGlobal,
327
328    AccountPumpSwapGlobalConfig,
329    AccountPumpSwapPool,
330}
331
332#[derive(Debug, Clone)]
333pub struct EventTypeFilter {
334    pub include_only: Option<Vec<EventType>>,
335    pub exclude_types: Option<Vec<EventType>>,
336}
337
338impl EventTypeFilter {
339    pub fn include_only(types: Vec<EventType>) -> Self {
340        Self { include_only: Some(types), exclude_types: None }
341    }
342
343    pub fn exclude_types(types: Vec<EventType>) -> Self {
344        Self { include_only: None, exclude_types: Some(types) }
345    }
346
347    pub fn should_include(&self, event_type: EventType) -> bool {
348        if let Some(ref include_only) = self.include_only {
349            // Direct match
350            if include_only.contains(&event_type) {
351                return true;
352            }
353            // Special case: PumpFunTrade discriminator is shared by Buy/Sell/BuyExactSolIn
354            // If filter includes any of these specific types, allow PumpFunTrade through
355            // (secondary filtering will happen after parsing)
356            if event_type == EventType::PumpFunTrade {
357                return include_only.iter().any(|t| {
358                    matches!(
359                        t,
360                        EventType::PumpFunBuy
361                            | EventType::PumpFunSell
362                            | EventType::PumpFunBuyExactSolIn
363                    )
364                });
365            }
366            return false;
367        }
368
369        if let Some(ref exclude_types) = self.exclude_types {
370            return !exclude_types.contains(&event_type);
371        }
372
373        true
374    }
375
376    #[inline]
377    pub fn includes_pumpfun(&self) -> bool {
378        if let Some(ref include_only) = self.include_only {
379            return include_only.iter().any(|t| {
380                matches!(
381                    t,
382                    EventType::PumpFunTrade
383                        | EventType::PumpFunBuy
384                        | EventType::PumpFunSell
385                        | EventType::PumpFunBuyExactSolIn
386                        | EventType::PumpFunCreate
387                        | EventType::PumpFunCreateV2
388                        | EventType::PumpFunComplete
389                        | EventType::PumpFunMigrate
390                        | EventType::PumpFeesCreateFeeSharingConfig
391                        | EventType::PumpFeesInitializeFeeConfig
392                        | EventType::PumpFeesResetFeeSharingConfig
393                        | EventType::PumpFeesRevokeFeeSharingAuthority
394                        | EventType::PumpFeesTransferFeeSharingAuthority
395                        | EventType::PumpFeesUpdateAdmin
396                        | EventType::PumpFeesUpdateFeeConfig
397                        | EventType::PumpFeesUpdateFeeShares
398                        | EventType::PumpFeesUpsertFeeTiers
399                        | EventType::PumpFunMigrateBondingCurveCreator
400                        | EventType::AccountPumpFunGlobal
401                )
402            });
403        }
404
405        if let Some(ref exclude_types) = self.exclude_types {
406            return !exclude_types.iter().any(|t| {
407                matches!(
408                    t,
409                    EventType::PumpFunTrade
410                        | EventType::PumpFunBuy
411                        | EventType::PumpFunSell
412                        | EventType::PumpFunBuyExactSolIn
413                        | EventType::PumpFunCreate
414                        | EventType::PumpFunCreateV2
415                        | EventType::PumpFunComplete
416                        | EventType::PumpFunMigrate
417                        | EventType::PumpFeesCreateFeeSharingConfig
418                        | EventType::PumpFeesInitializeFeeConfig
419                        | EventType::PumpFeesResetFeeSharingConfig
420                        | EventType::PumpFeesRevokeFeeSharingAuthority
421                        | EventType::PumpFeesTransferFeeSharingAuthority
422                        | EventType::PumpFeesUpdateAdmin
423                        | EventType::PumpFeesUpdateFeeConfig
424                        | EventType::PumpFeesUpdateFeeShares
425                        | EventType::PumpFeesUpsertFeeTiers
426                        | EventType::PumpFunMigrateBondingCurveCreator
427                        | EventType::AccountPumpFunGlobal
428                )
429            });
430        }
431
432        true
433    }
434
435    #[inline]
436    pub fn includes_meteora_damm_v2(&self) -> bool {
437        if let Some(ref include_only) = self.include_only {
438            return include_only.iter().any(|t| {
439                matches!(
440                    t,
441                    EventType::MeteoraDammV2Swap
442                        | EventType::MeteoraDammV2AddLiquidity
443                        | EventType::MeteoraDammV2CreatePosition
444                        | EventType::MeteoraDammV2ClosePosition
445                        | EventType::MeteoraDammV2RemoveLiquidity
446                )
447            });
448        }
449        if let Some(ref exclude_types) = self.exclude_types {
450            return !exclude_types.iter().any(|t| {
451                matches!(
452                    t,
453                    EventType::MeteoraDammV2Swap
454                        | EventType::MeteoraDammV2AddLiquidity
455                        | EventType::MeteoraDammV2CreatePosition
456                        | EventType::MeteoraDammV2ClosePosition
457                        | EventType::MeteoraDammV2RemoveLiquidity
458                )
459            });
460        }
461        true
462    }
463
464    #[inline]
465    pub fn includes_pump_fees(&self) -> bool {
466        macro_rules! any_pfees {
467            () => {
468                EventType::PumpFeesCreateFeeSharingConfig
469                    | EventType::PumpFeesInitializeFeeConfig
470                    | EventType::PumpFeesResetFeeSharingConfig
471                    | EventType::PumpFeesRevokeFeeSharingAuthority
472                    | EventType::PumpFeesTransferFeeSharingAuthority
473                    | EventType::PumpFeesUpdateAdmin
474                    | EventType::PumpFeesUpdateFeeConfig
475                    | EventType::PumpFeesUpdateFeeShares
476                    | EventType::PumpFeesUpsertFeeTiers
477            };
478        }
479        if let Some(ref include_only) = self.include_only {
480            return include_only.iter().any(|t| matches!(t, any_pfees!()));
481        }
482        if let Some(ref exclude_types) = self.exclude_types {
483            return !exclude_types.iter().any(|t| matches!(t, any_pfees!()));
484        }
485        true
486    }
487
488    /// Check if PumpSwap protocol events are included in the filter
489    #[inline]
490    pub fn includes_pumpswap(&self) -> bool {
491        if let Some(ref include_only) = self.include_only {
492            return include_only.iter().any(|t| {
493                matches!(
494                    t,
495                    EventType::PumpSwapBuy
496                        | EventType::PumpSwapSell
497                        | EventType::PumpSwapCreatePool
498                        | EventType::PumpSwapLiquidityAdded
499                        | EventType::PumpSwapLiquidityRemoved
500                )
501            });
502        }
503        if let Some(ref exclude_types) = self.exclude_types {
504            return !exclude_types.iter().any(|t| {
505                matches!(
506                    t,
507                    EventType::PumpSwapBuy
508                        | EventType::PumpSwapSell
509                        | EventType::PumpSwapCreatePool
510                        | EventType::PumpSwapLiquidityAdded
511                        | EventType::PumpSwapLiquidityRemoved
512                )
513            });
514        }
515        true
516    }
517
518    /// Check if Raydium Launchpad (Bonk) events are included in the filter
519    #[inline]
520    pub fn includes_raydium_launchpad(&self) -> bool {
521        if let Some(ref include_only) = self.include_only {
522            return include_only.iter().any(|t| {
523                matches!(
524                    t,
525                    EventType::BonkTrade | EventType::BonkPoolCreate | EventType::BonkMigrateAmm
526                )
527            });
528        }
529        if let Some(ref exclude_types) = self.exclude_types {
530            return !exclude_types.iter().any(|t| {
531                matches!(
532                    t,
533                    EventType::BonkTrade | EventType::BonkPoolCreate | EventType::BonkMigrateAmm
534                )
535            });
536        }
537        true
538    }
539}
540
541#[derive(Debug, Clone)]
542pub struct SlotFilter {
543    pub min_slot: Option<u64>,
544    pub max_slot: Option<u64>,
545}
546
547impl SlotFilter {
548    pub fn new() -> Self {
549        Self { min_slot: None, max_slot: None }
550    }
551
552    pub fn min_slot(mut self, slot: u64) -> Self {
553        self.min_slot = Some(slot);
554        self
555    }
556
557    pub fn max_slot(mut self, slot: u64) -> Self {
558        self.max_slot = Some(slot);
559        self
560    }
561}
562
563impl Default for SlotFilter {
564    fn default() -> Self {
565        Self::new()
566    }
567}