use serde::{Deserialize, Serialize};
use yellowstone_grpc_proto::geyser::{
subscribe_request_filter_accounts_filter::Filter as AccountsFilterOneof,
subscribe_request_filter_accounts_filter_memcmp::Data as MemcmpDataOneof,
SubscribeRequestFilterAccountsFilter, SubscribeRequestFilterAccountsFilterMemcmp,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
pub enum OrderMode {
#[default]
Unordered,
Ordered,
StreamingOrdered,
MicroBatch,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ClientConfig {
pub enable_metrics: bool,
pub connection_timeout_ms: u64,
pub request_timeout_ms: u64,
pub enable_tls: bool,
pub max_retries: u32,
pub retry_delay_ms: u64,
pub max_concurrent_streams: u32,
pub keep_alive_interval_ms: u64,
pub keep_alive_timeout_ms: u64,
pub buffer_size: usize,
pub order_mode: OrderMode,
pub order_timeout_ms: u64,
pub micro_batch_us: u64,
}
impl Default for ClientConfig {
fn default() -> Self {
Self {
enable_metrics: false,
connection_timeout_ms: 8000,
request_timeout_ms: 15000,
enable_tls: true,
max_retries: 3,
retry_delay_ms: 1000,
max_concurrent_streams: 100,
keep_alive_interval_ms: 30000,
keep_alive_timeout_ms: 5000,
buffer_size: 8192,
order_mode: OrderMode::Unordered,
order_timeout_ms: 100,
micro_batch_us: 100, }
}
}
impl ClientConfig {
pub fn low_latency() -> Self {
Self {
enable_metrics: false,
connection_timeout_ms: 5000,
request_timeout_ms: 10000,
enable_tls: true,
max_retries: 1,
retry_delay_ms: 100,
max_concurrent_streams: 200,
keep_alive_interval_ms: 10000,
keep_alive_timeout_ms: 2000,
buffer_size: 16384,
order_mode: OrderMode::Unordered,
order_timeout_ms: 50,
micro_batch_us: 50, }
}
pub fn high_throughput() -> Self {
Self {
enable_metrics: true,
connection_timeout_ms: 10000,
request_timeout_ms: 30000,
enable_tls: true,
max_retries: 5,
retry_delay_ms: 2000,
max_concurrent_streams: 500,
keep_alive_interval_ms: 60000,
keep_alive_timeout_ms: 10000,
buffer_size: 32768,
order_mode: OrderMode::Unordered,
order_timeout_ms: 200,
micro_batch_us: 200, }
}
}
#[derive(Debug, Clone)]
pub struct TransactionFilter {
pub account_include: Vec<String>,
pub account_exclude: Vec<String>,
pub account_required: Vec<String>,
}
impl TransactionFilter {
pub fn new() -> Self {
Self {
account_include: Vec::new(),
account_exclude: Vec::new(),
account_required: Vec::new(),
}
}
pub fn include_account(mut self, account: impl Into<String>) -> Self {
self.account_include.push(account.into());
self
}
pub fn exclude_account(mut self, account: impl Into<String>) -> Self {
self.account_exclude.push(account.into());
self
}
pub fn require_account(mut self, account: impl Into<String>) -> Self {
self.account_required.push(account.into());
self
}
pub fn from_program_ids(program_ids: Vec<String>) -> Self {
Self {
account_include: program_ids,
account_exclude: Vec::new(),
account_required: Vec::new(),
}
}
}
impl Default for TransactionFilter {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct AccountFilter {
pub account: Vec<String>,
pub owner: Vec<String>,
pub filters: Vec<SubscribeRequestFilterAccountsFilter>,
}
impl AccountFilter {
pub fn new() -> Self {
Self { account: Vec::new(), owner: Vec::new(), filters: Vec::new() }
}
pub fn add_account(mut self, account: impl Into<String>) -> Self {
self.account.push(account.into());
self
}
pub fn add_owner(mut self, owner: impl Into<String>) -> Self {
self.owner.push(owner.into());
self
}
pub fn add_filter(mut self, filter: SubscribeRequestFilterAccountsFilter) -> Self {
self.filters.push(filter);
self
}
pub fn from_program_owners(program_ids: Vec<String>) -> Self {
Self { account: Vec::new(), owner: program_ids, filters: Vec::new() }
}
}
impl Default for AccountFilter {
fn default() -> Self {
Self::new()
}
}
#[inline]
pub fn account_filter_memcmp(offset: u64, bytes: Vec<u8>) -> SubscribeRequestFilterAccountsFilter {
SubscribeRequestFilterAccountsFilter {
filter: Some(AccountsFilterOneof::Memcmp(SubscribeRequestFilterAccountsFilterMemcmp {
offset,
data: Some(MemcmpDataOneof::Bytes(bytes)),
})),
}
}
#[derive(Debug, Clone)]
pub struct AccountFilterData {
pub memcmp: Option<AccountFilterMemcmp>,
pub datasize: Option<u64>,
}
#[derive(Debug, Clone)]
pub struct AccountFilterMemcmp {
pub offset: u64,
pub bytes: Vec<u8>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Protocol {
PumpFun,
PumpSwap,
Bonk,
RaydiumCpmm,
RaydiumClmm,
RaydiumAmmV4,
MeteoraDammV2,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EventType {
BlockMeta,
BonkTrade,
BonkPoolCreate,
BonkMigrateAmm,
PumpFunTrade, PumpFunBuy, PumpFunSell, PumpFunBuyExactSolIn, PumpFunCreate,
PumpFunCreateV2, PumpFunComplete,
PumpFunMigrate,
PumpSwapBuy,
PumpSwapSell,
PumpSwapCreatePool,
PumpSwapLiquidityAdded,
PumpSwapLiquidityRemoved,
MeteoraDammV2Swap,
MeteoraDammV2AddLiquidity,
MeteoraDammV2RemoveLiquidity,
MeteoraDammV2CreatePosition,
MeteoraDammV2ClosePosition,
TokenAccount,
NonceAccount,
AccountPumpSwapGlobalConfig,
AccountPumpSwapPool,
}
#[derive(Debug, Clone)]
pub struct EventTypeFilter {
pub include_only: Option<Vec<EventType>>,
pub exclude_types: Option<Vec<EventType>>,
}
impl EventTypeFilter {
pub fn include_only(types: Vec<EventType>) -> Self {
Self { include_only: Some(types), exclude_types: None }
}
pub fn exclude_types(types: Vec<EventType>) -> Self {
Self { include_only: None, exclude_types: Some(types) }
}
pub fn should_include(&self, event_type: EventType) -> bool {
if let Some(ref include_only) = self.include_only {
if include_only.contains(&event_type) {
return true;
}
if event_type == EventType::PumpFunTrade {
return include_only.iter().any(|t| {
matches!(
t,
EventType::PumpFunBuy
| EventType::PumpFunSell
| EventType::PumpFunBuyExactSolIn
)
});
}
return false;
}
if let Some(ref exclude_types) = self.exclude_types {
return !exclude_types.contains(&event_type);
}
true
}
#[inline]
pub fn includes_pumpfun(&self) -> bool {
if let Some(ref include_only) = self.include_only {
return include_only.iter().any(|t| {
matches!(
t,
EventType::PumpFunTrade
| EventType::PumpFunBuy
| EventType::PumpFunSell
| EventType::PumpFunBuyExactSolIn
| EventType::PumpFunCreate
| EventType::PumpFunCreateV2
| EventType::PumpFunComplete
| EventType::PumpFunMigrate
)
});
}
if let Some(ref exclude_types) = self.exclude_types {
return !exclude_types.iter().any(|t| {
matches!(
t,
EventType::PumpFunTrade
| EventType::PumpFunBuy
| EventType::PumpFunSell
| EventType::PumpFunBuyExactSolIn
| EventType::PumpFunCreate
| EventType::PumpFunCreateV2
| EventType::PumpFunComplete
| EventType::PumpFunMigrate
)
});
}
true
}
#[inline]
pub fn includes_meteora_damm_v2(&self) -> bool {
if let Some(ref include_only) = self.include_only {
return include_only.iter().any(|t| {
matches!(
t,
EventType::MeteoraDammV2Swap
| EventType::MeteoraDammV2AddLiquidity
| EventType::MeteoraDammV2CreatePosition
| EventType::MeteoraDammV2ClosePosition
| EventType::MeteoraDammV2RemoveLiquidity
)
});
}
if let Some(ref exclude_types) = self.exclude_types {
return !exclude_types.iter().any(|t| {
matches!(
t,
EventType::MeteoraDammV2Swap
| EventType::MeteoraDammV2AddLiquidity
| EventType::MeteoraDammV2CreatePosition
| EventType::MeteoraDammV2ClosePosition
| EventType::MeteoraDammV2RemoveLiquidity
)
});
}
true
}
#[inline]
pub fn includes_pumpswap(&self) -> bool {
if let Some(ref include_only) = self.include_only {
return include_only.iter().any(|t| {
matches!(
t,
EventType::PumpSwapBuy
| EventType::PumpSwapSell
| EventType::PumpSwapCreatePool
| EventType::PumpSwapLiquidityAdded
| EventType::PumpSwapLiquidityRemoved
)
});
}
if let Some(ref exclude_types) = self.exclude_types {
return !exclude_types.iter().any(|t| {
matches!(
t,
EventType::PumpSwapBuy
| EventType::PumpSwapSell
| EventType::PumpSwapCreatePool
| EventType::PumpSwapLiquidityAdded
| EventType::PumpSwapLiquidityRemoved
)
});
}
true
}
#[inline]
pub fn includes_raydium_launchpad(&self) -> bool {
if let Some(ref include_only) = self.include_only {
return include_only.iter().any(|t| {
matches!(
t,
EventType::BonkTrade | EventType::BonkPoolCreate | EventType::BonkMigrateAmm
)
});
}
if let Some(ref exclude_types) = self.exclude_types {
return !exclude_types.iter().any(|t| {
matches!(
t,
EventType::BonkTrade | EventType::BonkPoolCreate | EventType::BonkMigrateAmm
)
});
}
true
}
}
#[derive(Debug, Clone)]
pub struct SlotFilter {
pub min_slot: Option<u64>,
pub max_slot: Option<u64>,
}
impl SlotFilter {
pub fn new() -> Self {
Self { min_slot: None, max_slot: None }
}
pub fn min_slot(mut self, slot: u64) -> Self {
self.min_slot = Some(slot);
self
}
pub fn max_slot(mut self, slot: u64) -> Self {
self.max_slot = Some(slot);
self
}
}
impl Default for SlotFilter {
fn default() -> Self {
Self::new()
}
}