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#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
10pub enum OrderMode {
11 #[default]
13 Unordered,
14 Ordered,
18 StreamingOrdered,
22 MicroBatch,
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
29pub struct ClientConfig {
30 pub enable_metrics: bool,
32 pub connection_timeout_ms: u64,
34 pub request_timeout_ms: u64,
36 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 pub order_mode: OrderMode,
46 pub order_timeout_ms: u64,
49 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, }
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, }
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, }
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 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 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#[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 BlockMeta,
237
238 BonkTrade,
240 BonkPoolCreate,
241 BonkMigrateAmm,
242
243 PumpFunTrade, PumpFunBuy, PumpFunSell, PumpFunBuyExactSolIn, PumpFunCreate,
249 PumpFunCreateV2, PumpFunComplete,
251 PumpFunMigrate,
252
253 PumpSwapBuy,
255 PumpSwapSell,
256 PumpSwapCreatePool,
257 PumpSwapLiquidityAdded,
258 PumpSwapLiquidityRemoved,
259 MeteoraDammV2Swap,
301 MeteoraDammV2AddLiquidity,
302 MeteoraDammV2RemoveLiquidity,
303 MeteoraDammV2CreatePosition,
305 MeteoraDammV2ClosePosition,
306 TokenAccount,
313 NonceAccount,
314
315 AccountPumpSwapGlobalConfig,
316 AccountPumpSwapPool,
317}
318
319#[derive(Debug, Clone)]
320pub struct EventTypeFilter {
321 pub include_only: Option<Vec<EventType>>,
322 pub exclude_types: Option<Vec<EventType>>,
323}
324
325impl EventTypeFilter {
326 pub fn include_only(types: Vec<EventType>) -> Self {
327 Self { include_only: Some(types), exclude_types: None }
328 }
329
330 pub fn exclude_types(types: Vec<EventType>) -> Self {
331 Self { include_only: None, exclude_types: Some(types) }
332 }
333
334 pub fn should_include(&self, event_type: EventType) -> bool {
335 if let Some(ref include_only) = self.include_only {
336 if include_only.contains(&event_type) {
338 return true;
339 }
340 if event_type == EventType::PumpFunTrade {
344 return include_only.iter().any(|t| {
345 matches!(
346 t,
347 EventType::PumpFunBuy
348 | EventType::PumpFunSell
349 | EventType::PumpFunBuyExactSolIn
350 )
351 });
352 }
353 return false;
354 }
355
356 if let Some(ref exclude_types) = self.exclude_types {
357 return !exclude_types.contains(&event_type);
358 }
359
360 true
361 }
362
363 #[inline]
364 pub fn includes_pumpfun(&self) -> bool {
365 if let Some(ref include_only) = self.include_only {
366 return include_only.iter().any(|t| {
367 matches!(
368 t,
369 EventType::PumpFunTrade
370 | EventType::PumpFunBuy
371 | EventType::PumpFunSell
372 | EventType::PumpFunBuyExactSolIn
373 | EventType::PumpFunCreate
374 | EventType::PumpFunCreateV2
375 | EventType::PumpFunComplete
376 | EventType::PumpFunMigrate
377 )
378 });
379 }
380
381 if let Some(ref exclude_types) = self.exclude_types {
382 return !exclude_types.iter().any(|t| {
383 matches!(
384 t,
385 EventType::PumpFunTrade
386 | EventType::PumpFunBuy
387 | EventType::PumpFunSell
388 | EventType::PumpFunBuyExactSolIn
389 | EventType::PumpFunCreate
390 | EventType::PumpFunCreateV2
391 | EventType::PumpFunComplete
392 | EventType::PumpFunMigrate
393 )
394 });
395 }
396
397 true
398 }
399
400 #[inline]
401 pub fn includes_meteora_damm_v2(&self) -> bool {
402 if let Some(ref include_only) = self.include_only {
403 return include_only.iter().any(|t| {
404 matches!(
405 t,
406 EventType::MeteoraDammV2Swap
407 | EventType::MeteoraDammV2AddLiquidity
408 | EventType::MeteoraDammV2CreatePosition
409 | EventType::MeteoraDammV2ClosePosition
410 | EventType::MeteoraDammV2RemoveLiquidity
411 )
412 });
413 }
414 if let Some(ref exclude_types) = self.exclude_types {
415 return !exclude_types.iter().any(|t| {
416 matches!(
417 t,
418 EventType::MeteoraDammV2Swap
419 | EventType::MeteoraDammV2AddLiquidity
420 | EventType::MeteoraDammV2CreatePosition
421 | EventType::MeteoraDammV2ClosePosition
422 | EventType::MeteoraDammV2RemoveLiquidity
423 )
424 });
425 }
426 true
427 }
428
429 #[inline]
431 pub fn includes_pumpswap(&self) -> bool {
432 if let Some(ref include_only) = self.include_only {
433 return include_only.iter().any(|t| {
434 matches!(
435 t,
436 EventType::PumpSwapBuy
437 | EventType::PumpSwapSell
438 | EventType::PumpSwapCreatePool
439 | EventType::PumpSwapLiquidityAdded
440 | EventType::PumpSwapLiquidityRemoved
441 )
442 });
443 }
444 if let Some(ref exclude_types) = self.exclude_types {
445 return !exclude_types.iter().any(|t| {
446 matches!(
447 t,
448 EventType::PumpSwapBuy
449 | EventType::PumpSwapSell
450 | EventType::PumpSwapCreatePool
451 | EventType::PumpSwapLiquidityAdded
452 | EventType::PumpSwapLiquidityRemoved
453 )
454 });
455 }
456 true
457 }
458
459 #[inline]
461 pub fn includes_raydium_launchpad(&self) -> bool {
462 if let Some(ref include_only) = self.include_only {
463 return include_only.iter().any(|t| {
464 matches!(
465 t,
466 EventType::BonkTrade | EventType::BonkPoolCreate | EventType::BonkMigrateAmm
467 )
468 });
469 }
470 if let Some(ref exclude_types) = self.exclude_types {
471 return !exclude_types.iter().any(|t| {
472 matches!(
473 t,
474 EventType::BonkTrade | EventType::BonkPoolCreate | EventType::BonkMigrateAmm
475 )
476 });
477 }
478 true
479 }
480}
481
482#[derive(Debug, Clone)]
483pub struct SlotFilter {
484 pub min_slot: Option<u64>,
485 pub max_slot: Option<u64>,
486}
487
488impl SlotFilter {
489 pub fn new() -> Self {
490 Self { min_slot: None, max_slot: None }
491 }
492
493 pub fn min_slot(mut self, slot: u64) -> Self {
494 self.min_slot = Some(slot);
495 self
496 }
497
498 pub fn max_slot(mut self, slot: u64) -> Self {
499 self.max_slot = Some(slot);
500 self
501 }
502}
503
504impl Default for SlotFilter {
505 fn default() -> Self {
506 Self::new()
507 }
508}