Skip to main content

sol_parser_sdk/grpc/
types.rs

1use serde::{Deserialize, Serialize};
2use yellowstone_grpc_proto::geyser::SubscribeRequestFilterAccountsFilter;
3
4/// 事件输出顺序模式
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
6pub enum OrderMode {
7    /// 无序模式:收到即输出,超低延迟 (10-20μs)
8    #[default]
9    Unordered,
10    /// 有序模式:按 slot + tx_index 排序后输出
11    /// 同一 slot 内的交易会等待收齐后按 tx_index 排序
12    /// 延迟增加约 1-50ms(取决于 slot 内交易数量)
13    Ordered,
14    /// 流式有序模式:连续序列立即释放,低延迟 + 顺序保证
15    /// 只要收到从 0 开始的连续 tx_index 序列,立即释放
16    /// 延迟约 0.1-5ms,比 Ordered 低 5-50 倍
17    StreamingOrdered,
18    /// 微批次模式:极短时间窗口内收集事件,窗口结束后排序释放
19    /// 窗口大小由 micro_batch_us 配置(默认 100μs)
20    /// 延迟约 50-200μs,接近 Unordered 但保证顺序
21    MicroBatch,
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct ClientConfig {
26    /// 是否启用性能监控
27    pub enable_metrics: bool,
28    /// 连接超时时间(毫秒)
29    pub connection_timeout_ms: u64,
30    /// 请求超时时间(毫秒)
31    pub request_timeout_ms: u64,
32    /// 是否启用TLS
33    pub enable_tls: bool,
34    pub max_retries: u32,
35    pub retry_delay_ms: u64,
36    pub max_concurrent_streams: u32,
37    pub keep_alive_interval_ms: u64,
38    pub keep_alive_timeout_ms: u64,
39    pub buffer_size: usize,
40    /// 事件输出顺序模式
41    pub order_mode: OrderMode,
42    /// 有序模式下,slot 超时时间(毫秒)
43    /// 超过此时间未收到新 slot 信号,强制输出当前缓冲的事件
44    pub order_timeout_ms: u64,
45    /// MicroBatch 模式下的时间窗口大小(微秒)
46    /// 默认 100μs,可根据网络状况调整
47    pub micro_batch_us: u64,
48}
49
50impl Default for ClientConfig {
51    fn default() -> Self {
52        Self {
53            enable_metrics: false,
54            connection_timeout_ms: 8000,
55            request_timeout_ms: 15000,
56            enable_tls: true,
57            max_retries: 3,
58            retry_delay_ms: 1000,
59            max_concurrent_streams: 100,
60            keep_alive_interval_ms: 30000,
61            keep_alive_timeout_ms: 5000,
62            buffer_size: 8192,
63            order_mode: OrderMode::Unordered,
64            order_timeout_ms: 100,
65            micro_batch_us: 100, // 100μs 默认窗口
66        }
67    }
68}
69
70impl ClientConfig {
71    pub fn low_latency() -> Self {
72        Self {
73            enable_metrics: false,
74            connection_timeout_ms: 5000,
75            request_timeout_ms: 10000,
76            enable_tls: true,
77            max_retries: 1,
78            retry_delay_ms: 100,
79            max_concurrent_streams: 200,
80            keep_alive_interval_ms: 10000,
81            keep_alive_timeout_ms: 2000,
82            buffer_size: 16384,
83            order_mode: OrderMode::Unordered,
84            order_timeout_ms: 50,
85            micro_batch_us: 50, // 50μs 更激进的窗口
86        }
87    }
88
89    pub fn high_throughput() -> Self {
90        Self {
91            enable_metrics: true,
92            connection_timeout_ms: 10000,
93            request_timeout_ms: 30000,
94            enable_tls: true,
95            max_retries: 5,
96            retry_delay_ms: 2000,
97            max_concurrent_streams: 500,
98            keep_alive_interval_ms: 60000,
99            keep_alive_timeout_ms: 10000,
100            buffer_size: 32768,
101            order_mode: OrderMode::Unordered,
102            order_timeout_ms: 200,
103            micro_batch_us: 200, // 200μs 高吞吐模式
104        }
105    }
106}
107
108#[derive(Debug, Clone)]
109pub struct TransactionFilter {
110    pub account_include: Vec<String>,
111    pub account_exclude: Vec<String>,
112    pub account_required: Vec<String>,
113}
114
115impl TransactionFilter {
116    pub fn new() -> Self {
117        Self {
118            account_include: Vec::new(),
119            account_exclude: Vec::new(),
120            account_required: Vec::new(),
121        }
122    }
123
124    pub fn include_account(mut self, account: impl Into<String>) -> Self {
125        self.account_include.push(account.into());
126        self
127    }
128
129    pub fn exclude_account(mut self, account: impl Into<String>) -> Self {
130        self.account_exclude.push(account.into());
131        self
132    }
133
134    pub fn require_account(mut self, account: impl Into<String>) -> Self {
135        self.account_required.push(account.into());
136        self
137    }
138
139    /// 从程序ID列表创建过滤器
140    pub fn from_program_ids(program_ids: Vec<String>) -> Self {
141        Self {
142            account_include: program_ids,
143            account_exclude: Vec::new(),
144            account_required: Vec::new(),
145        }
146    }
147}
148
149impl Default for TransactionFilter {
150    fn default() -> Self {
151        Self::new()
152    }
153}
154
155#[derive(Debug, Clone)]
156pub struct AccountFilter {
157    pub account: Vec<String>,
158    pub owner: Vec<String>,
159    pub filters: Vec<SubscribeRequestFilterAccountsFilter>,
160}
161
162impl AccountFilter {
163    pub fn new() -> Self {
164        Self { account: Vec::new(), owner: Vec::new(), filters: Vec::new() }
165    }
166
167    pub fn add_account(mut self, account: impl Into<String>) -> Self {
168        self.account.push(account.into());
169        self
170    }
171
172    pub fn add_owner(mut self, owner: impl Into<String>) -> Self {
173        self.owner.push(owner.into());
174        self
175    }
176
177    pub fn add_filter(mut self, filter: SubscribeRequestFilterAccountsFilter) -> Self {
178        self.filters.push(filter);
179        self
180    }
181
182    /// 从程序ID列表创建所有者过滤器
183    pub fn from_program_owners(program_ids: Vec<String>) -> Self {
184        Self { account: Vec::new(), owner: program_ids, filters: Vec::new() }
185    }
186}
187
188impl Default for AccountFilter {
189    fn default() -> Self {
190        Self::new()
191    }
192}
193
194#[derive(Debug, Clone)]
195pub struct AccountFilterData {
196    pub memcmp: Option<AccountFilterMemcmp>,
197    pub datasize: Option<u64>,
198}
199
200#[derive(Debug, Clone)]
201pub struct AccountFilterMemcmp {
202    pub offset: u64,
203    pub bytes: Vec<u8>,
204}
205
206#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
207pub enum Protocol {
208    PumpFun,
209    PumpSwap,
210    Bonk,
211    RaydiumCpmm,
212    RaydiumClmm,
213    RaydiumAmmV4,
214    MeteoraDammV2,
215}
216
217#[derive(Debug, Clone, Copy, PartialEq, Eq)]
218pub enum EventType {
219    // Block events
220    BlockMeta,
221
222    // Bonk events
223    BonkTrade,
224    BonkPoolCreate,
225    BonkMigrateAmm,
226
227    // PumpFun events
228    PumpFunTrade,    // All trade events (backward compatible)
229    PumpFunBuy,      // Buy events only (filter by ix_name)
230    PumpFunSell,     // Sell events only (filter by ix_name)
231    PumpFunBuyExactSolIn, // BuyExactSolIn events only (filter by ix_name)
232    PumpFunCreate,
233    PumpFunComplete,
234    PumpFunMigrate,
235
236    // PumpSwap events
237    PumpSwapBuy,
238    PumpSwapSell,
239    PumpSwapCreatePool,
240    PumpSwapLiquidityAdded,
241    PumpSwapLiquidityRemoved,
242    // PumpSwapPoolUpdated,
243    // PumpSwapFeesClaimed,
244
245    // Raydium CPMM events
246    // RaydiumCpmmSwap,
247    // RaydiumCpmmDeposit,
248    // RaydiumCpmmWithdraw,
249    // RaydiumCpmmInitialize,
250
251    // Raydium CLMM events
252    // RaydiumClmmSwap,
253    // RaydiumClmmCreatePool,
254    // RaydiumClmmOpenPosition,
255    // RaydiumClmmClosePosition,
256    // RaydiumClmmIncreaseLiquidity,
257    // RaydiumClmmDecreaseLiquidity,
258    // RaydiumClmmOpenPositionWithTokenExtNft,
259    // RaydiumClmmCollectFee,
260
261    // Raydium AMM V4 events
262    // RaydiumAmmV4Swap,
263    // RaydiumAmmV4Deposit,
264    // RaydiumAmmV4Withdraw,
265    // RaydiumAmmV4Initialize2,
266    // RaydiumAmmV4WithdrawPnl,
267
268    // Orca Whirlpool events
269    // OrcaWhirlpoolSwap,
270    // OrcaWhirlpoolLiquidityIncreased,
271    // OrcaWhirlpoolLiquidityDecreased,
272    // OrcaWhirlpoolPoolInitialized,
273
274    // Meteora events
275    // MeteoraPoolsSwap,
276    // MeteoraPoolsAddLiquidity,
277    // MeteoraPoolsRemoveLiquidity,
278    // MeteoraPoolsBootstrapLiquidity,
279    // MeteoraPoolsPoolCreated,
280    // MeteoraPoolsSetPoolFees,
281
282    // Meteora DAMM V2 events
283    MeteoraDammV2Swap,
284    MeteoraDammV2AddLiquidity,
285    MeteoraDammV2RemoveLiquidity,
286    // MeteoraDammV2InitializePool,
287    MeteoraDammV2CreatePosition,
288    MeteoraDammV2ClosePosition,
289    // MeteoraDammV2ClaimPositionFee,
290    // MeteoraDammV2InitializeReward,
291    // MeteoraDammV2FundReward,
292    // MeteoraDammV2ClaimReward,
293
294    // Account events
295    TokenAccount,
296    NonceAccount,
297
298    AccountPumpSwapGlobalConfig,
299    AccountPumpSwapPool,
300}
301
302#[derive(Debug, Clone)]
303pub struct EventTypeFilter {
304    pub include_only: Option<Vec<EventType>>,
305    pub exclude_types: Option<Vec<EventType>>,
306}
307
308impl EventTypeFilter {
309    pub fn include_only(types: Vec<EventType>) -> Self {
310        Self { include_only: Some(types), exclude_types: None }
311    }
312
313    pub fn exclude_types(types: Vec<EventType>) -> Self {
314        Self { include_only: None, exclude_types: Some(types) }
315    }
316
317    pub fn should_include(&self, event_type: EventType) -> bool {
318        if let Some(ref include_only) = self.include_only {
319            // Direct match
320            if include_only.contains(&event_type) {
321                return true;
322            }
323            // Special case: PumpFunTrade discriminator is shared by Buy/Sell/BuyExactSolIn
324            // If filter includes any of these specific types, allow PumpFunTrade through
325            // (secondary filtering will happen after parsing)
326            if event_type == EventType::PumpFunTrade {
327                return include_only.iter().any(|t| matches!(t,
328                    EventType::PumpFunBuy | EventType::PumpFunSell | EventType::PumpFunBuyExactSolIn
329                ));
330            }
331            return false;
332        }
333
334        if let Some(ref exclude_types) = self.exclude_types {
335            return !exclude_types.contains(&event_type);
336        }
337
338        true
339    }
340
341    #[inline]
342    pub fn includes_pumpfun(&self) -> bool {
343        if let Some(ref include_only) = self.include_only {
344            return include_only.iter().any(|t| {
345                matches!(
346                    t,
347                    EventType::PumpFunTrade
348                        | EventType::PumpFunBuy
349                        | EventType::PumpFunSell
350                        | EventType::PumpFunBuyExactSolIn
351                        | EventType::PumpFunCreate
352                        | EventType::PumpFunComplete
353                        | EventType::PumpFunMigrate
354                )
355            });
356        }
357
358        if let Some(ref exclude_types) = self.exclude_types {
359            return !exclude_types.iter().any(|t| {
360                matches!(
361                    t,
362                    EventType::PumpFunTrade
363                        | EventType::PumpFunBuy
364                        | EventType::PumpFunSell
365                        | EventType::PumpFunBuyExactSolIn
366                        | EventType::PumpFunCreate
367                        | EventType::PumpFunComplete
368                        | EventType::PumpFunMigrate
369                )
370            });
371        }
372
373        true
374    }
375
376    #[inline]
377    pub fn includes_meteora_damm_v2(&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::MeteoraDammV2Swap
383                        | EventType::MeteoraDammV2AddLiquidity
384                        | EventType::MeteoraDammV2CreatePosition
385                        | EventType::MeteoraDammV2ClosePosition
386                        | EventType::MeteoraDammV2RemoveLiquidity
387                )
388            });
389        }
390        if let Some(ref exclude_types) = self.exclude_types {
391            return !exclude_types.iter().any(|t| {
392                matches!(
393                    t,
394                    EventType::MeteoraDammV2Swap
395                        | EventType::MeteoraDammV2AddLiquidity
396                        | EventType::MeteoraDammV2CreatePosition
397                        | EventType::MeteoraDammV2ClosePosition
398                        | EventType::MeteoraDammV2RemoveLiquidity
399                )
400            });
401        }
402        true
403    }
404
405    /// Check if PumpSwap protocol events are included in the filter
406    #[inline]
407    pub fn includes_pumpswap(&self) -> bool {
408        if let Some(ref include_only) = self.include_only {
409            return include_only.iter().any(|t| {
410                matches!(
411                    t,
412                    EventType::PumpSwapBuy
413                        | EventType::PumpSwapSell
414                        | EventType::PumpSwapCreatePool
415                        | EventType::PumpSwapLiquidityAdded
416                        | EventType::PumpSwapLiquidityRemoved
417                )
418            });
419        }
420        if let Some(ref exclude_types) = self.exclude_types {
421            return !exclude_types.iter().any(|t| {
422                matches!(
423                    t,
424                    EventType::PumpSwapBuy
425                        | EventType::PumpSwapSell
426                        | EventType::PumpSwapCreatePool
427                        | EventType::PumpSwapLiquidityAdded
428                        | EventType::PumpSwapLiquidityRemoved
429                )
430            });
431        }
432        true
433    }
434
435    /// Check if Raydium Launchpad (Bonk) events are included in the filter
436    #[inline]
437    pub fn includes_raydium_launchpad(&self) -> bool {
438        if let Some(ref include_only) = self.include_only {
439            return include_only.iter().any(|t| {
440                matches!(
441                    t,
442                    EventType::BonkTrade
443                        | EventType::BonkPoolCreate
444                        | EventType::BonkMigrateAmm
445                )
446            });
447        }
448        if let Some(ref exclude_types) = self.exclude_types {
449            return !exclude_types.iter().any(|t| {
450                matches!(
451                    t,
452                    EventType::BonkTrade
453                        | EventType::BonkPoolCreate
454                        | EventType::BonkMigrateAmm
455                )
456            });
457        }
458        true
459    }
460}
461
462#[derive(Debug, Clone)]
463pub struct SlotFilter {
464    pub min_slot: Option<u64>,
465    pub max_slot: Option<u64>,
466}
467
468impl SlotFilter {
469    pub fn new() -> Self {
470        Self { min_slot: None, max_slot: None }
471    }
472
473    pub fn min_slot(mut self, slot: u64) -> Self {
474        self.min_slot = Some(slot);
475        self
476    }
477
478    pub fn max_slot(mut self, slot: u64) -> Self {
479        self.max_slot = Some(slot);
480        self
481    }
482}
483
484impl Default for SlotFilter {
485    fn default() -> Self {
486        Self::new()
487    }
488}