diff --git a/examples/meteora_dlmm_account_subscription.rs b/examples/meteora_dlmm_account_subscription.rs deleted file mode 100644 index 1638275..0000000 --- a/examples/meteora_dlmm_account_subscription.rs +++ /dev/null @@ -1,117 +0,0 @@ -use solana_streamer_sdk::streaming::{ - event_parser::{ - protocols::meteora_dlmm::parser::METEORA_DLMM_PROGRAM_ID, DexEvent, Protocol, - }, - grpc::ClientConfig, - yellowstone_grpc::{AccountFilter, TransactionFilter}, - YellowstoneGrpc, -}; - -#[tokio::main] -async fn main() -> Result<(), Box> { - println!("开始 Meteora DLMM 账户数据订阅示例..."); - subscribe_meteora_dlmm_accounts().await?; - Ok(()) -} - -async fn subscribe_meteora_dlmm_accounts() -> Result<(), Box> { - println!("订阅 Meteora DLMM 账户数据..."); - - // 创建客户端配置 - let mut config: ClientConfig = ClientConfig::default(); - // 启用性能监控(可选,有性能开销) - config.enable_metrics = true; - let grpc = YellowstoneGrpc::new_with_config( - "https://solana-yellowstone-grpc.publicnode.com:443".to_string(), - None, - config, - )?; - - println!("gRPC 客户端创建成功"); - - let callback = create_event_callback(); - - // 只订阅 Meteora DLMM 协议 - let protocols = vec![Protocol::MeteoraDlmm]; - - println!("监控协议: {:?}", protocols); - - // 账户过滤器 - 订阅 Meteora DLMM 程序拥有的账户 - let account_filter = AccountFilter { - account: vec![], - owner: vec![METEORA_DLMM_PROGRAM_ID.to_string()], - filters: vec![], - }; - - // 交易过滤器(可选,如果只想订阅账户数据,可以留空) - let transaction_filter = TransactionFilter { - account_include: vec![METEORA_DLMM_PROGRAM_ID.to_string()], - account_exclude: vec![], - account_required: vec![], - }; - - // 事件类型过滤器 - 只订阅账户事件 - use solana_streamer_sdk::streaming::event_parser::common::EventType; - use solana_streamer_sdk::streaming::event_parser::common::filter::EventTypeFilter; - let event_type_filter = Some(EventTypeFilter { - include: vec![EventType::AccountMeteoraDlmmLbPair], - }); - - println!("开始监听事件,按 Ctrl+C 停止..."); - println!("监控程序: {}", METEORA_DLMM_PROGRAM_ID); - - println!("开始订阅..."); - - grpc.subscribe_events_immediate( - protocols, - None, - vec![transaction_filter], - vec![account_filter], - event_type_filter, - None, - callback, - ) - .await?; - - // 支持 stop 方法,测试代码 - 异步1000秒之后停止 - let grpc_clone = grpc.clone(); - tokio::spawn(async move { - tokio::time::sleep(std::time::Duration::from_secs(1000)).await; - grpc_clone.stop().await; - }); - - println!("等待 Ctrl+C 停止..."); - tokio::signal::ctrl_c().await?; - - Ok(()) -} - -fn create_event_callback() -> impl Fn(DexEvent) { - |event: DexEvent| { - println!( - "🎉 事件接收! 类型: {:?}, slot: {:?}", - event.metadata().event_type, - event.metadata().slot - ); - match event { - DexEvent::MeteoraDlmmLbPairAccountEvent(e) => { - println!("=== Meteora DLMM LbPair 账户更新 ==="); - println!("账户地址: {}", e.pubkey); - println!("Token X Mint: {}", e.lb_pair.token_x_mint); - println!("Token Y Mint: {}", e.lb_pair.token_y_mint); - println!("Active ID: {}", e.lb_pair.active_id); - println!("Bin Step: {}", e.lb_pair.bin_step); - println!("Status: {}", e.lb_pair.status); - println!("Reserve X: {}", e.lb_pair.reserve_x); - println!("Reserve Y: {}", e.lb_pair.reserve_y); - println!("Protocol Fee X: {}", e.lb_pair.protocol_fee.amount_x); - println!("Protocol Fee Y: {}", e.lb_pair.protocol_fee.amount_y); - println!("Last Updated At: {}", e.lb_pair.last_updated_at); - println!("====================================="); - } - _ => { - println!("其他事件: {:?}", event); - } - } - } -} diff --git a/examples/raydium_clmm_bitmap_extension_subscription.rs b/examples/raydium_clmm_bitmap_extension_subscription.rs deleted file mode 100644 index 8ac0453..0000000 --- a/examples/raydium_clmm_bitmap_extension_subscription.rs +++ /dev/null @@ -1,158 +0,0 @@ -use solana_streamer_sdk::streaming::{ - event_parser::{ - protocols::raydium_clmm::parser::RAYDIUM_CLMM_PROGRAM_ID, DexEvent, Protocol, - }, - grpc::ClientConfig, - yellowstone_grpc::{AccountFilter, TransactionFilter}, - YellowstoneGrpc, -}; - -#[tokio::main] -async fn main() -> Result<(), Box> { - // 初始化日志系统(可选) - // env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init(); - - println!("开始 Raydium CLMM TickArrayBitmapExtension 账户数据订阅示例..."); - subscribe_raydium_clmm_bitmap_extension().await?; - Ok(()) -} - -async fn subscribe_raydium_clmm_bitmap_extension() -> Result<(), Box> { - println!("订阅 Raydium CLMM TickArrayBitmapExtension 账户数据..."); - - // 创建客户端配置 - let mut config: ClientConfig = ClientConfig::default(); - // 启用性能监控(可选,有性能开销) - config.enable_metrics = true; - let grpc = YellowstoneGrpc::new_with_config( - "https://solana-yellowstone-grpc.publicnode.com:443".to_string(), - None, - config, - )?; - - println!("gRPC 客户端创建成功"); - - let callback = create_event_callback(); - - // 只订阅 Raydium CLMM 协议 - let protocols = vec![Protocol::RaydiumClmm]; - - println!("监控协议: {:?}", protocols); - - // 账户过滤器 - 订阅 Raydium CLMM 程序拥有的账户 - let account_filter = AccountFilter { - account: vec![], - owner: vec![RAYDIUM_CLMM_PROGRAM_ID.to_string()], - filters: vec![], - }; - - // 交易过滤器(可选,如果只想订阅账户数据,可以留空) - let transaction_filter = TransactionFilter { - account_include: vec![], - account_exclude: vec![], - account_required: vec![], - }; - - // 事件类型过滤器 - 只订阅 TickArrayBitmapExtension 账户事件 - use solana_streamer_sdk::streaming::event_parser::common::EventType; - use solana_streamer_sdk::streaming::event_parser::common::filter::EventTypeFilter; - let event_type_filter = Some(EventTypeFilter { - include: vec![EventType::AccountRaydiumClmmTickArrayBitmapExtension], - }); - - println!("开始监听事件,按 Ctrl+C 停止..."); - println!("监控程序: {}", RAYDIUM_CLMM_PROGRAM_ID); - - println!("开始订阅..."); - - grpc.subscribe_events_immediate( - protocols, - None, - vec![transaction_filter], - vec![account_filter], - event_type_filter, - None, - callback, - ) - .await?; - - // 支持 stop 方法,测试代码 - 异步1000秒之后停止 - let grpc_clone = grpc.clone(); - tokio::spawn(async move { - tokio::time::sleep(std::time::Duration::from_secs(1000)).await; - grpc_clone.stop().await; - }); - - println!("等待 Ctrl+C 停止..."); - tokio::signal::ctrl_c().await?; - - Ok(()) -} - -fn create_event_callback() -> impl Fn(DexEvent) { - |event: DexEvent| { - match event { - DexEvent::RaydiumClmmTickArrayBitmapExtensionAccountEvent(e) => { - println!("=== Raydium CLMM TickArrayBitmapExtension 账户更新 ==="); - println!("账户地址: {}", e.pubkey); - println!("Pool ID: {}", e.tick_array_bitmap_extension.pool_id); - println!("Executable: {}", e.executable); - println!("Lamports: {}", e.lamports); - println!("Owner: {}", e.owner); - println!("Rent Epoch: {}", e.rent_epoch); - println!("Slot: {}", e.metadata.slot); - println!("Signature: {}", e.metadata.signature); - - // 由于使用了 #[repr(C, packed)],需要先复制数据到本地变量 - let positive_bitmap = e.tick_array_bitmap_extension.positive_tick_array_bitmap; - let negative_bitmap = e.tick_array_bitmap_extension.negative_tick_array_bitmap; - - // 打印 positive_tick_array_bitmap 的统计信息 - let positive_non_zero_count = positive_bitmap - .iter() - .flatten() - .filter(|&&x| x != 0) - .count(); - println!("Positive Tick Array Bitmap: {} 个非零值", positive_non_zero_count); - - // 打印 negative_tick_array_bitmap 的统计信息 - let negative_non_zero_count = negative_bitmap - .iter() - .flatten() - .filter(|&&x| x != 0) - .count(); - println!("Negative Tick Array Bitmap: {} 个非零值", negative_non_zero_count); - - // 可选:打印前几个非零值作为示例 - println!("\nPositive Bitmap 前 5 个非零值:"); - let mut count = 0; - for (i, row) in positive_bitmap.iter().enumerate() { - for (j, &value) in row.iter().enumerate() { - if value != 0 && count < 5 { - println!(" [{}][{}] = {}", i, j, value); - count += 1; - } - } - } - - println!("\nNegative Bitmap 前 5 个非零值:"); - let mut count = 0; - for (i, row) in negative_bitmap.iter().enumerate() { - for (j, &value) in row.iter().enumerate() { - if value != 0 && count < 5 { - println!(" [{}][{}] = {}", i, j, value); - count += 1; - } - } - } - - println!("=====================================\n"); - } - _ => { - // 其他事件类型,可以忽略或记录 - // println!("其他事件类型: {:?}", event.metadata().event_type); - } - } - } -} - diff --git a/examples/whirlpool_account_subscription.rs b/examples/whirlpool_account_subscription.rs deleted file mode 100644 index b565fb1..0000000 --- a/examples/whirlpool_account_subscription.rs +++ /dev/null @@ -1,134 +0,0 @@ -use solana_streamer_sdk::streaming::{ - event_parser::{ - protocols::whirlpool::parser::WHIRLPOOL_PROGRAM_ID, DexEvent, Protocol, - }, - grpc::ClientConfig, - yellowstone_grpc::{AccountFilter, TransactionFilter}, - YellowstoneGrpc, -}; - -#[tokio::main] -async fn main() -> Result<(), Box> { - // 初始化日志系统,设置日志级别为 debug 以便查看详细信息 - //env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("debug")).init(); - - println!("开始 Whirlpool 账户数据订阅示例..."); - subscribe_whirlpool_accounts().await?; - Ok(()) -} - -async fn subscribe_whirlpool_accounts() -> Result<(), Box> { - println!("订阅 Whirlpool 账户数据..."); - - // 创建客户端配置 - let mut config: ClientConfig = ClientConfig::default(); - // 启用性能监控(可选,有性能开销) - config.enable_metrics = true; - let grpc = YellowstoneGrpc::new_with_config( - "https://solana-yellowstone-grpc.publicnode.com:443".to_string(), - None, - config, - )?; - - println!("gRPC 客户端创建成功"); - - let callback = create_event_callback(); - - // 只订阅 Whirlpool 协议 - let protocols = vec![Protocol::Whirlpool]; - - println!("监控协议: {:?}", protocols); - - // 账户过滤器 - 订阅 Whirlpool 程序拥有的账户 - let account_filter = AccountFilter { - account: vec![], - owner: vec![WHIRLPOOL_PROGRAM_ID.to_string()], - filters: vec![], - }; - - // 交易过滤器(可选,如果只想订阅账户数据,可以留空) - let transaction_filter = TransactionFilter { - account_include: vec![WHIRLPOOL_PROGRAM_ID.to_string()], - account_exclude: vec![], - account_required: vec![], - }; - - // 事件类型过滤器 - 只订阅账户事件 - use solana_streamer_sdk::streaming::event_parser::common::EventType; - use solana_streamer_sdk::streaming::event_parser::common::filter::EventTypeFilter; - let event_type_filter = Some(EventTypeFilter { - include: vec![EventType::AccountWhirlpool], - }); - - println!("开始监听事件,按 Ctrl+C 停止..."); - println!("监控程序: {}", WHIRLPOOL_PROGRAM_ID); - - println!("开始订阅..."); - - grpc.subscribe_events_immediate( - protocols, - None, - vec![transaction_filter], - vec![account_filter], - event_type_filter, - None, - callback, - ) - .await?; - - // 支持 stop 方法,测试代码 - 异步1000秒之后停止 - let grpc_clone = grpc.clone(); - tokio::spawn(async move { - tokio::time::sleep(std::time::Duration::from_secs(1000)).await; - grpc_clone.stop().await; - }); - - println!("等待 Ctrl+C 停止..."); - tokio::signal::ctrl_c().await?; - - Ok(()) -} - -fn create_event_callback() -> impl Fn(DexEvent) { - |event: DexEvent| { - // println!( - // "🎉 事件接收! 类型: {:?}, slot: {:?}", - // event.metadata().event_type, - // event.metadata().slot - // ); - match event { - DexEvent::WhirlpoolAccountEvent(e) => { - println!("=== Whirlpool 账户更新 ==="); - println!("账户地址: {}", e.pubkey); - println!("Whirlpools Config: {}", e.whirlpool.whirlpools_config); - println!("Token Mint A: {}", e.whirlpool.token_mint_a); - println!("Token Mint B: {}", e.whirlpool.token_mint_b); - println!("Token Vault A: {}", e.whirlpool.token_vault_a); - println!("Token Vault B: {}", e.whirlpool.token_vault_b); - println!("Tick Spacing: {}", e.whirlpool.tick_spacing); - println!("Fee Rate: {}", e.whirlpool.fee_rate); - println!("Protocol Fee Rate: {}", e.whirlpool.protocol_fee_rate); - println!("Liquidity: {}", e.whirlpool.liquidity); - println!("Sqrt Price: {}", e.whirlpool.sqrt_price); - println!("Tick Current Index: {}", e.whirlpool.tick_current_index); - println!("Protocol Fee Owed A: {}", e.whirlpool.protocol_fee_owed_a); - println!("Protocol Fee Owed B: {}", e.whirlpool.protocol_fee_owed_b); - println!("Fee Growth Global A: {}", e.whirlpool.fee_growth_global_a); - println!("Fee Growth Global B: {}", e.whirlpool.fee_growth_global_b); - println!("Reward Last Updated Timestamp: {}", e.whirlpool.reward_last_updated_timestamp); - println!("奖励信息数量: {}", e.whirlpool.reward_infos.len()); - for (i, reward_info) in e.whirlpool.reward_infos.iter().enumerate() { - if reward_info.mint != solana_sdk::pubkey::Pubkey::default() { - println!(" 奖励 {}: Mint={}, Vault={}, Authority={}, Emissions={}, Growth={}", - i, reward_info.mint, reward_info.vault, reward_info.authority, - reward_info.emissions_per_second_x64, reward_info.growth_global_x64); - } - } - println!("====================================="); - } - _ => { - //println!("其他事件: {:?}", event); - } - } - } -} diff --git a/src/streaming/event_parser/common/types.rs b/src/streaming/event_parser/common/types.rs index b646d04..7bcaa96 100755 --- a/src/streaming/event_parser/common/types.rs +++ b/src/streaming/event_parser/common/types.rs @@ -52,8 +52,6 @@ pub enum ProtocolType { RaydiumClmm, RaydiumAmmV4, MeteoraDammV2, - MeteoraDlmm, - Whirlpool, Common, } @@ -133,11 +131,8 @@ pub enum EventType { AccountRaydiumClmmAmmConfig, AccountRaydiumClmmPoolState, AccountRaydiumClmmTickArrayState, - AccountRaydiumClmmTickArrayBitmapExtension, AccountRaydiumCpmmAmmConfig, AccountRaydiumCpmmPoolState, - AccountMeteoraDlmmLbPair, - AccountWhirlpool, NonceAccount, TokenAccount, @@ -162,11 +157,8 @@ pub const ACCOUNT_EVENT_TYPES: &[EventType] = &[ EventType::AccountRaydiumClmmAmmConfig, EventType::AccountRaydiumClmmPoolState, EventType::AccountRaydiumClmmTickArrayState, - EventType::AccountRaydiumClmmTickArrayBitmapExtension, EventType::AccountRaydiumCpmmAmmConfig, EventType::AccountRaydiumCpmmPoolState, - EventType::AccountMeteoraDlmmLbPair, - EventType::AccountWhirlpool, EventType::TokenAccount, EventType::NonceAccount, ]; @@ -238,13 +230,8 @@ impl fmt::Display for EventType { EventType::AccountRaydiumClmmTickArrayState => { write!(f, "AccountRaydiumClmmTickArrayState") } - EventType::AccountRaydiumClmmTickArrayBitmapExtension => { - write!(f, "AccountRaydiumClmmTickArrayBitmapExtension") - } EventType::AccountRaydiumCpmmAmmConfig => write!(f, "AccountRaydiumCpmmAmmConfig"), EventType::AccountRaydiumCpmmPoolState => write!(f, "AccountRaydiumCpmmPoolState"), - EventType::AccountMeteoraDlmmLbPair => write!(f, "AccountMeteoraDlmmLbPair"), - EventType::AccountWhirlpool => write!(f, "AccountWhirlpool"), EventType::TokenAccount => write!(f, "TokenAccount"), EventType::NonceAccount => write!(f, "NonceAccount"), EventType::BlockMeta => write!(f, "BlockMeta"), diff --git a/src/streaming/event_parser/core/dispatcher.rs b/src/streaming/event_parser/core/dispatcher.rs index fed511e..77691ce 100644 --- a/src/streaming/event_parser/core/dispatcher.rs +++ b/src/streaming/event_parser/core/dispatcher.rs @@ -11,11 +11,9 @@ use crate::streaming::event_parser::{ common::EventMetadata, core::common_event_parser::{CommonEventParser, COMPUTE_BUDGET_PROGRAM_ID}, protocols::{ - bonk::parser as bonk, meteora_damm_v2::parser as meteora_damm_v2, - meteora_dlmm::parser as meteora_dlmm, pumpfun::parser as pumpfun, + bonk::parser as bonk, meteora_damm_v2::parser as meteora_damm_v2, pumpfun::parser as pumpfun, pumpswap::parser as pumpswap, raydium_amm_v4::parser as raydium_amm_v4, raydium_clmm::parser as raydium_clmm, raydium_cpmm::parser as raydium_cpmm, - whirlpool::parser as whirlpool, }, DexEvent, Protocol, }; @@ -56,8 +54,6 @@ impl EventDispatcher { Protocol::RaydiumClmm => ProtocolType::RaydiumClmm, Protocol::RaydiumAmmV4 => ProtocolType::RaydiumAmmV4, Protocol::MeteoraDammV2 => ProtocolType::MeteoraDammV2, - Protocol::MeteoraDlmm => ProtocolType::MeteoraDlmm, - Protocol::Whirlpool => ProtocolType::Whirlpool, }; match protocol { @@ -103,14 +99,6 @@ impl EventDispatcher { accounts, metadata, ), - Protocol::MeteoraDlmm => { - // Meteora DLMM 目前不需要解析指令数据,返回 None - None - } - Protocol::Whirlpool => { - // Whirlpool 目前不需要解析指令数据,返回 None - None - } } } @@ -141,8 +129,6 @@ impl EventDispatcher { Protocol::RaydiumClmm => ProtocolType::RaydiumClmm, Protocol::RaydiumAmmV4 => ProtocolType::RaydiumAmmV4, Protocol::MeteoraDammV2 => ProtocolType::MeteoraDammV2, - Protocol::MeteoraDlmm => ProtocolType::MeteoraDlmm, - Protocol::Whirlpool => ProtocolType::Whirlpool, }; match protocol { @@ -181,14 +167,6 @@ impl EventDispatcher { inner_instruction_data, metadata, ), - Protocol::MeteoraDlmm => { - // Meteora DLMM 目前不需要解析 inner instruction 数据,返回 None - None - } - Protocol::Whirlpool => { - // Whirlpool 目前不需要解析 inner instruction 数据,返回 None - None - } } } @@ -209,10 +187,6 @@ impl EventDispatcher { Some(Protocol::RaydiumAmmV4) } else if program_id == &meteora_damm_v2::METEORA_DAMM_V2_PROGRAM_ID { Some(Protocol::MeteoraDammV2) - } else if program_id == &meteora_dlmm::METEORA_DLMM_PROGRAM_ID { - Some(Protocol::MeteoraDlmm) - } else if program_id == &whirlpool::WHIRLPOOL_PROGRAM_ID { - Some(Protocol::Whirlpool) } else { None } @@ -251,8 +225,6 @@ impl EventDispatcher { Protocol::RaydiumClmm => raydium_clmm::RAYDIUM_CLMM_PROGRAM_ID, Protocol::RaydiumAmmV4 => raydium_amm_v4::RAYDIUM_AMM_V4_PROGRAM_ID, Protocol::MeteoraDammV2 => meteora_damm_v2::METEORA_DAMM_V2_PROGRAM_ID, - Protocol::MeteoraDlmm => meteora_dlmm::METEORA_DLMM_PROGRAM_ID, - Protocol::Whirlpool => whirlpool::WHIRLPOOL_PROGRAM_ID, } } @@ -289,8 +261,6 @@ impl EventDispatcher { Protocol::RaydiumClmm => ProtocolType::RaydiumClmm, Protocol::RaydiumAmmV4 => ProtocolType::RaydiumAmmV4, Protocol::MeteoraDammV2 => ProtocolType::MeteoraDammV2, - Protocol::MeteoraDlmm => ProtocolType::MeteoraDlmm, - Protocol::Whirlpool => ProtocolType::Whirlpool, }; match protocol { @@ -314,12 +284,6 @@ impl EventDispatcher { // Meteora DAMM 目前不需要解析账户数据,返回 None None } - Protocol::MeteoraDlmm => { - meteora_dlmm::parse_meteora_dlmm_account_data(discriminator, account, metadata) - } - Protocol::Whirlpool => { - whirlpool::parse_whirlpool_account_data(discriminator, account, metadata) - } } } } diff --git a/src/streaming/event_parser/core/event_parser.rs b/src/streaming/event_parser/core/event_parser.rs index b7edb91..848c20a 100644 --- a/src/streaming/event_parser/core/event_parser.rs +++ b/src/streaming/event_parser/core/event_parser.rs @@ -388,13 +388,21 @@ impl EventParser { None => return Ok(()), }; - // 处理 inner instructions + // 处理 inner instructions - 查找对应的 CPI log 进行 merge + // 当 inner_index 有值时,只查找索引大于当前 inner_index 的 CPI log let mut inner_instruction_event: Option = None; if let Some(inner_instructions_ref) = inner_instructions { + let current_inner_idx = inner_index.unwrap_or(-1) as i32; + // 并行执行两个任务: 解析 inner event 和提取 swap_data let (inner_event_result, swap_data_result) = std::thread::scope(|s| { let inner_event_handle = s.spawn(|| { - for inner_instruction in inner_instructions_ref.instructions.iter() { + for (idx, inner_instruction) in inner_instructions_ref.instructions.iter().enumerate() { + // 只查找索引大于当前 inner_index 的 CPI log + if (idx as i32) <= current_inner_idx { + continue; + } + let inner_data = &inner_instruction.data; // 检查长度(需要 16 字节的 discriminator) if inner_data.len() < 16 { @@ -420,7 +428,7 @@ impl EventParser { parse_swap_data_from_next_grpc_instructions( &event, inner_instructions_ref, - inner_index.unwrap_or(-1_i64) as i8, + current_inner_idx as i8, accounts, ) } else { @@ -563,13 +571,21 @@ impl EventParser { None => return Ok(()), }; - // 处理 inner instructions + // 处理 inner instructions - 查找对应的 CPI log 进行 merge + // 当 inner_index 有值时,只查找索引大于当前 inner_index 的 CPI log let mut inner_instruction_event: Option = None; if let Some(inner_instructions_ref) = inner_instructions { + let current_inner_idx = inner_index.unwrap_or(-1) as i32; + // 并行执行两个任务: 解析 inner event 和提取 swap_data let (inner_event_result, swap_data_result) = std::thread::scope(|s| { let inner_event_handle = s.spawn(|| { - for inner_instruction in inner_instructions_ref.instructions.iter() { + for (idx, inner_instruction) in inner_instructions_ref.instructions.iter().enumerate() { + // 只查找索引大于当前 inner_index 的 CPI log + if (idx as i32) <= current_inner_idx { + continue; + } + let inner_data = &inner_instruction.instruction.data; // 检查长度(需要 16 字节的 discriminator) if inner_data.len() < 16 { @@ -595,7 +611,7 @@ impl EventParser { parse_swap_data_from_next_instructions( &event, inner_instructions_ref, - inner_index.unwrap_or(-1_i64) as i8, + current_inner_idx as i8, accounts, ) } else { @@ -735,3 +751,4 @@ impl EventParser { } } } + diff --git a/src/streaming/event_parser/core/traits.rs b/src/streaming/event_parser/core/traits.rs index 64251b1..0dc95f8 100755 --- a/src/streaming/event_parser/core/traits.rs +++ b/src/streaming/event_parser/core/traits.rs @@ -8,13 +8,11 @@ use crate::streaming::event_parser::core::common_event_parser::{ use crate::streaming::event_parser::protocols::block::block_meta_event::BlockMetaEvent; use crate::streaming::event_parser::protocols::bonk::events::*; use crate::streaming::event_parser::protocols::meteora_damm_v2::events::*; -use crate::streaming::event_parser::protocols::meteora_dlmm::events::*; use crate::streaming::event_parser::protocols::pumpfun::events::*; use crate::streaming::event_parser::protocols::pumpswap::events::*; use crate::streaming::event_parser::protocols::raydium_amm_v4::events::*; use crate::streaming::event_parser::protocols::raydium_clmm::events::*; use crate::streaming::event_parser::protocols::raydium_cpmm::events::*; -use crate::streaming::event_parser::protocols::whirlpool::events::*; use serde::{Deserialize, Serialize}; use std::fmt::Debug; @@ -67,7 +65,6 @@ pub enum DexEvent { RaydiumClmmAmmConfigAccountEvent(RaydiumClmmAmmConfigAccountEvent), RaydiumClmmPoolStateAccountEvent(RaydiumClmmPoolStateAccountEvent), RaydiumClmmTickArrayStateAccountEvent(RaydiumClmmTickArrayStateAccountEvent), - RaydiumClmmTickArrayBitmapExtensionAccountEvent(RaydiumClmmTickArrayBitmapExtensionAccountEvent), // Raydium CPMM events RaydiumCpmmSwapEvent(RaydiumCpmmSwapEvent), @@ -84,12 +81,6 @@ pub enum DexEvent { MeteoraDammV2InitializeCustomizablePoolEvent(MeteoraDammV2InitializeCustomizablePoolEvent), MeteoraDammV2InitializePoolWithDynamicConfigEvent(MeteoraDammV2InitializePoolWithDynamicConfigEvent), - // Meteora DLMM events - MeteoraDlmmLbPairAccountEvent(MeteoraDlmmLbPairAccountEvent), - - // Whirlpool events - WhirlpoolAccountEvent(WhirlpoolAccountEvent), - // Common events TokenAccountEvent(TokenAccountEvent), NonceAccountEvent(NonceAccountEvent), @@ -139,7 +130,6 @@ impl DexEvent { DexEvent::RaydiumClmmAmmConfigAccountEvent(e) => &e.metadata, DexEvent::RaydiumClmmPoolStateAccountEvent(e) => &e.metadata, DexEvent::RaydiumClmmTickArrayStateAccountEvent(e) => &e.metadata, - DexEvent::RaydiumClmmTickArrayBitmapExtensionAccountEvent(e) => &e.metadata, DexEvent::RaydiumCpmmSwapEvent(e) => &e.metadata, DexEvent::RaydiumCpmmDepositEvent(e) => &e.metadata, DexEvent::RaydiumCpmmWithdrawEvent(e) => &e.metadata, @@ -151,8 +141,6 @@ impl DexEvent { DexEvent::MeteoraDammV2InitializePoolEvent(e) => &e.metadata, DexEvent::MeteoraDammV2InitializeCustomizablePoolEvent(e) => &e.metadata, DexEvent::MeteoraDammV2InitializePoolWithDynamicConfigEvent(e) => &e.metadata, - DexEvent::MeteoraDlmmLbPairAccountEvent(e) => &e.metadata, - DexEvent::WhirlpoolAccountEvent(e) => &e.metadata, DexEvent::TokenAccountEvent(e) => &e.metadata, DexEvent::NonceAccountEvent(e) => &e.metadata, DexEvent::TokenInfoEvent(e) => &e.metadata, @@ -201,7 +189,6 @@ impl DexEvent { DexEvent::RaydiumClmmAmmConfigAccountEvent(e) => &mut e.metadata, DexEvent::RaydiumClmmPoolStateAccountEvent(e) => &mut e.metadata, DexEvent::RaydiumClmmTickArrayStateAccountEvent(e) => &mut e.metadata, - DexEvent::RaydiumClmmTickArrayBitmapExtensionAccountEvent(e) => &mut e.metadata, DexEvent::RaydiumCpmmSwapEvent(e) => &mut e.metadata, DexEvent::RaydiumCpmmDepositEvent(e) => &mut e.metadata, DexEvent::RaydiumCpmmWithdrawEvent(e) => &mut e.metadata, @@ -213,8 +200,6 @@ impl DexEvent { DexEvent::MeteoraDammV2InitializePoolEvent(e) => &mut e.metadata, DexEvent::MeteoraDammV2InitializeCustomizablePoolEvent(e) => &mut e.metadata, DexEvent::MeteoraDammV2InitializePoolWithDynamicConfigEvent(e) => &mut e.metadata, - DexEvent::MeteoraDlmmLbPairAccountEvent(e) => &mut e.metadata, - DexEvent::WhirlpoolAccountEvent(e) => &mut e.metadata, DexEvent::TokenAccountEvent(e) => &mut e.metadata, DexEvent::NonceAccountEvent(e) => &mut e.metadata, DexEvent::TokenInfoEvent(e) => &mut e.metadata, diff --git a/src/streaming/event_parser/protocols/meteora_dlmm/events.rs b/src/streaming/event_parser/protocols/meteora_dlmm/events.rs deleted file mode 100644 index 31f670a..0000000 --- a/src/streaming/event_parser/protocols/meteora_dlmm/events.rs +++ /dev/null @@ -1,22 +0,0 @@ -use crate::streaming::event_parser::common::EventMetadata; -use crate::streaming::event_parser::protocols::meteora_dlmm::types::LbPair; -use serde::{Deserialize, Serialize}; -use solana_sdk::pubkey::Pubkey; - -/// LbPair 账户事件 -#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] -pub struct MeteoraDlmmLbPairAccountEvent { - pub metadata: EventMetadata, - pub pubkey: Pubkey, - pub executable: bool, - pub lamports: u64, - pub owner: Pubkey, - pub rent_epoch: u64, - pub lb_pair: LbPair, -} - -/// 事件鉴别器常量 -pub mod discriminators { - // 账户鉴别器 - pub const LB_PAIR: &[u8] = &[33, 11, 49, 98, 181, 101, 177, 13]; -} diff --git a/src/streaming/event_parser/protocols/meteora_dlmm/mod.rs b/src/streaming/event_parser/protocols/meteora_dlmm/mod.rs deleted file mode 100644 index 86156fa..0000000 --- a/src/streaming/event_parser/protocols/meteora_dlmm/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod events; -pub mod parser; -pub mod types; - -pub use events::*; diff --git a/src/streaming/event_parser/protocols/meteora_dlmm/parser.rs b/src/streaming/event_parser/protocols/meteora_dlmm/parser.rs deleted file mode 100644 index 77dc4d3..0000000 --- a/src/streaming/event_parser/protocols/meteora_dlmm/parser.rs +++ /dev/null @@ -1,25 +0,0 @@ -use crate::streaming::event_parser::protocols::meteora_dlmm::discriminators; -use solana_sdk::pubkey::Pubkey; - -/// Meteora DLMM 程序ID -pub const METEORA_DLMM_PROGRAM_ID: Pubkey = - solana_sdk::pubkey!("LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo"); - -/// 解析 Meteora DLMM 账户数据 -/// -/// 根据判别器路由到具体的账户解析函数 -pub fn parse_meteora_dlmm_account_data( - discriminator: &[u8], - account: &crate::streaming::grpc::AccountPretty, - metadata: crate::streaming::event_parser::common::EventMetadata, -) -> Option { - match discriminator { - discriminators::LB_PAIR => { - crate::streaming::event_parser::protocols::meteora_dlmm::types::lb_pair_parser( - account, - metadata, - ) - } - _ => None, - } -} diff --git a/src/streaming/event_parser/protocols/meteora_dlmm/types.rs b/src/streaming/event_parser/protocols/meteora_dlmm/types.rs deleted file mode 100644 index 1e7432c..0000000 --- a/src/streaming/event_parser/protocols/meteora_dlmm/types.rs +++ /dev/null @@ -1,129 +0,0 @@ -use borsh::BorshDeserialize; -use serde::{Deserialize, Serialize}; -use solana_sdk::pubkey::Pubkey; - -use crate::streaming::{ - event_parser::{ - common::{EventMetadata, EventType}, - protocols::meteora_dlmm::MeteoraDlmmLbPairAccountEvent, - DexEvent, - }, - grpc::AccountPretty, -}; - -#[repr(C)] -#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)] -pub struct StaticParameters { - pub base_factor: u16, - pub filter_period: u16, - pub decay_period: u16, - pub reduction_factor: u16, - pub variable_fee_control: u32, - pub max_volatility_accumulator: u32, - pub min_bin_id: i32, - pub max_bin_id: i32, - pub protocol_share: u16, - pub base_fee_power_factor: u8, - pub padding: [u8; 5], -} - -#[repr(C)] -#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)] -pub struct VariableParameters { - pub volatility_accumulator: u32, - pub volatility_reference: u32, - pub index_reference: i32, - pub padding: [u8; 4], - pub last_update_timestamp: i64, - pub padding1: [u8; 8], -} - -#[repr(C)] -#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)] -pub struct ProtocolFee { - pub amount_x: u64, - pub amount_y: u64, -} - -#[repr(C)] -#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)] -pub struct RewardInfo { - pub mint: Pubkey, - pub vault: Pubkey, - pub funder: Pubkey, - pub reward_duration: u64, - pub reward_duration_end: u64, - pub reward_rate: u128, - pub last_update_time: u64, - pub cumulative_seconds_with_empty_liquidity_reward: u64, -} - -#[repr(C)] -#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)] -pub struct LbPair { - pub parameters: StaticParameters, - pub v_parameters: VariableParameters, - pub bump_seed: [u8; 1], - pub bin_step_seed: [u8; 2], - pub pair_type: u8, - pub active_id: i32, - pub bin_step: u16, - pub status: u8, - pub require_base_factor_seed: u8, - pub base_factor_seed: [u8; 2], - pub activation_type: u8, - pub creator_pool_on_off_control: u8, - pub token_x_mint: Pubkey, - pub token_y_mint: Pubkey, - pub reserve_x: Pubkey, - pub reserve_y: Pubkey, - pub protocol_fee: ProtocolFee, - pub padding1: [u8; 32], - pub reward_infos: [RewardInfo; 2], - pub oracle: Pubkey, - pub bin_array_bitmap: [u64; 16], - pub last_updated_at: i64, - pub padding2: [u8; 32], - pub pre_activation_swap_address: Pubkey, - pub base_key: Pubkey, - pub activation_point: u64, - pub pre_activation_duration: u64, - pub padding3: [u8; 8], - pub padding4: u64, - pub creator: Pubkey, - pub token_mint_x_program_flag: u8, - pub token_mint_y_program_flag: u8, - pub reserved: [u8; 22], -} - -pub const LB_PAIR_SIZE: usize = std::mem::size_of::(); - -pub fn lb_pair_decode(data: &[u8]) -> Option { - if data.len() < LB_PAIR_SIZE { - return None; - } - borsh::from_slice::(&data[..LB_PAIR_SIZE]).ok() -} - -pub fn lb_pair_parser(account: &AccountPretty, mut metadata: EventMetadata) -> Option { - metadata.event_type = EventType::AccountMeteoraDlmmLbPair; - - if account.data.len() < LB_PAIR_SIZE + 8 { - return None; - } - if let Some(lb_pair) = lb_pair_decode(&account.data[8..LB_PAIR_SIZE + 8]) { - Some(DexEvent::MeteoraDlmmLbPairAccountEvent( - MeteoraDlmmLbPairAccountEvent { - metadata, - pubkey: account.pubkey, - executable: account.executable, - lamports: account.lamports, - owner: account.owner, - rent_epoch: account.rent_epoch, - lb_pair, - }, - )) - } else { - None - } -} diff --git a/src/streaming/event_parser/protocols/mod.rs b/src/streaming/event_parser/protocols/mod.rs index 7f13df0..0d44048 100755 --- a/src/streaming/event_parser/protocols/mod.rs +++ b/src/streaming/event_parser/protocols/mod.rs @@ -1,13 +1,11 @@ pub mod block; pub mod bonk; pub mod meteora_damm_v2; -pub mod meteora_dlmm; pub mod pumpfun; pub mod pumpswap; pub mod raydium_amm_v4; pub mod raydium_clmm; pub mod raydium_cpmm; pub mod types; -pub mod whirlpool; pub use block::block_meta_event::BlockMetaEvent; pub use types::Protocol; diff --git a/src/streaming/event_parser/protocols/raydium_clmm/events.rs b/src/streaming/event_parser/protocols/raydium_clmm/events.rs index d70a295..492fa57 100755 --- a/src/streaming/event_parser/protocols/raydium_clmm/events.rs +++ b/src/streaming/event_parser/protocols/raydium_clmm/events.rs @@ -1,5 +1,5 @@ use crate::streaming::event_parser::common::EventMetadata; -use crate::streaming::event_parser::protocols::raydium_clmm::types::{PoolState, TickArrayBitmapExtension, TickArrayState}; +use crate::streaming::event_parser::protocols::raydium_clmm::types::{PoolState, TickArrayState}; use crate::{ streaming::event_parser::protocols::raydium_clmm::types::AmmConfig, }; @@ -247,18 +247,6 @@ pub struct RaydiumClmmTickArrayStateAccountEvent { pub tick_array_state: TickArrayState, } -/// TickArrayBitmapExtension 账户事件 -#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] -pub struct RaydiumClmmTickArrayBitmapExtensionAccountEvent { - pub metadata: EventMetadata, - pub pubkey: Pubkey, - pub executable: bool, - pub lamports: u64, - pub owner: Pubkey, - pub rent_epoch: u64, - pub tick_array_bitmap_extension: TickArrayBitmapExtension, -} - /// 事件鉴别器常量 pub mod discriminators { // 指令鉴别器 @@ -275,5 +263,4 @@ pub mod discriminators { pub const AMM_CONFIG: &[u8] = &[218, 244, 33, 104, 203, 203, 43, 111]; pub const POOL_STATE: &[u8] = &[247, 237, 227, 245, 215, 195, 222, 70]; pub const TICK_ARRAY_STATE: &[u8] = &[192, 155, 85, 205, 49, 249, 129, 42]; - pub const TICK_ARRAY_BITMAP_EXTENSION: &[u8] = &[60, 150, 36, 219, 97, 128, 139, 153]; } diff --git a/src/streaming/event_parser/protocols/raydium_clmm/parser.rs b/src/streaming/event_parser/protocols/raydium_clmm/parser.rs index 7901104..b127644 100755 --- a/src/streaming/event_parser/protocols/raydium_clmm/parser.rs +++ b/src/streaming/event_parser/protocols/raydium_clmm/parser.rs @@ -79,9 +79,6 @@ pub fn parse_raydium_clmm_account_data( discriminators::TICK_ARRAY_STATE => { crate::streaming::event_parser::protocols::raydium_clmm::types::tick_array_state_parser(account, metadata) } - discriminators::TICK_ARRAY_BITMAP_EXTENSION => { - crate::streaming::event_parser::protocols::raydium_clmm::types::tick_array_bitmap_extension_parser(account, metadata) - } _ => None, } } diff --git a/src/streaming/event_parser/protocols/raydium_clmm/types.rs b/src/streaming/event_parser/protocols/raydium_clmm/types.rs index bb0aa2e..5f3695c 100644 --- a/src/streaming/event_parser/protocols/raydium_clmm/types.rs +++ b/src/streaming/event_parser/protocols/raydium_clmm/types.rs @@ -7,7 +7,7 @@ use crate::streaming::{ common::{EventMetadata, EventType}, protocols::raydium_clmm::{ RaydiumClmmAmmConfigAccountEvent, RaydiumClmmPoolStateAccountEvent, - RaydiumClmmTickArrayBitmapExtensionAccountEvent, RaydiumClmmTickArrayStateAccountEvent, + RaydiumClmmTickArrayStateAccountEvent, }, DexEvent, }, @@ -231,106 +231,3 @@ pub fn tick_array_state_parser( None } } - -// EXTENSION_TICKARRAY_BITMAP_SIZE 常量,根据 Raydium CLMM 实现,通常为 14 -pub const EXTENSION_TICKARRAY_BITMAP_SIZE: usize = 14; - -#[repr(C, packed)] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)] -pub struct TickArrayBitmapExtension { - pub pool_id: Pubkey, - /// Packed initialized tick array state for start_tick_index is positive - pub positive_tick_array_bitmap: [[u64; 8]; EXTENSION_TICKARRAY_BITMAP_SIZE], - /// Packed initialized tick array state for start_tick_index is negative - pub negative_tick_array_bitmap: [[u64; 8]; EXTENSION_TICKARRAY_BITMAP_SIZE], -} - -impl Default for TickArrayBitmapExtension { - fn default() -> Self { - Self { - pool_id: Pubkey::default(), - positive_tick_array_bitmap: [[0u64; 8]; EXTENSION_TICKARRAY_BITMAP_SIZE], - negative_tick_array_bitmap: [[0u64; 8]; EXTENSION_TICKARRAY_BITMAP_SIZE], - } - } -} - -pub const TICK_ARRAY_BITMAP_EXTENSION_SIZE: usize = 32 + (EXTENSION_TICKARRAY_BITMAP_SIZE * 8 * 8) + (EXTENSION_TICKARRAY_BITMAP_SIZE * 8 * 8); - -pub fn tick_array_bitmap_extension_decode(data: &[u8]) -> Option { - if data.len() < TICK_ARRAY_BITMAP_EXTENSION_SIZE { - return None; - } - - // 由于使用了 #[repr(C, packed)],我们需要手动解析 - let mut offset = 0; - - // 读取 pool_id (32 bytes) - if data.len() < offset + 32 { - return None; - } - let pool_id = Pubkey::try_from(&data[offset..offset + 32]).ok()?; - offset += 32; - - // 读取 positive_tick_array_bitmap - let mut positive_tick_array_bitmap = [[0u64; 8]; EXTENSION_TICKARRAY_BITMAP_SIZE]; - for i in 0..EXTENSION_TICKARRAY_BITMAP_SIZE { - for j in 0..8 { - if data.len() < offset + 8 { - return None; - } - positive_tick_array_bitmap[i][j] = u64::from_le_bytes( - data[offset..offset + 8].try_into().ok()? - ); - offset += 8; - } - } - - // 读取 negative_tick_array_bitmap - let mut negative_tick_array_bitmap = [[0u64; 8]; EXTENSION_TICKARRAY_BITMAP_SIZE]; - for i in 0..EXTENSION_TICKARRAY_BITMAP_SIZE { - for j in 0..8 { - if data.len() < offset + 8 { - return None; - } - negative_tick_array_bitmap[i][j] = u64::from_le_bytes( - data[offset..offset + 8].try_into().ok()? - ); - offset += 8; - } - } - - Some(TickArrayBitmapExtension { - pool_id, - positive_tick_array_bitmap, - negative_tick_array_bitmap, - }) -} - -pub fn tick_array_bitmap_extension_parser( - account: &AccountPretty, - mut metadata: EventMetadata, -) -> Option { - metadata.event_type = EventType::AccountRaydiumClmmTickArrayBitmapExtension; - - if account.data.len() < TICK_ARRAY_BITMAP_EXTENSION_SIZE + 8 { - return None; - } - if let Some(tick_array_bitmap_extension) = - tick_array_bitmap_extension_decode(&account.data[8..TICK_ARRAY_BITMAP_EXTENSION_SIZE + 8]) - { - Some(DexEvent::RaydiumClmmTickArrayBitmapExtensionAccountEvent( - RaydiumClmmTickArrayBitmapExtensionAccountEvent { - metadata, - pubkey: account.pubkey, - executable: account.executable, - lamports: account.lamports, - owner: account.owner, - rent_epoch: account.rent_epoch, - tick_array_bitmap_extension, - }, - )) - } else { - None - } -} diff --git a/src/streaming/event_parser/protocols/types.rs b/src/streaming/event_parser/protocols/types.rs index 86f41ec..3d864dc 100755 --- a/src/streaming/event_parser/protocols/types.rs +++ b/src/streaming/event_parser/protocols/types.rs @@ -1,9 +1,8 @@ use crate::streaming::event_parser::protocols::{ bonk::parser::BONK_PROGRAM_ID, meteora_damm_v2::parser::METEORA_DAMM_V2_PROGRAM_ID, - meteora_dlmm::parser::METEORA_DLMM_PROGRAM_ID, pumpfun::parser::PUMPFUN_PROGRAM_ID, - pumpswap::parser::PUMPSWAP_PROGRAM_ID, raydium_amm_v4::parser::RAYDIUM_AMM_V4_PROGRAM_ID, - raydium_clmm::parser::RAYDIUM_CLMM_PROGRAM_ID, raydium_cpmm::parser::RAYDIUM_CPMM_PROGRAM_ID, - whirlpool::parser::WHIRLPOOL_PROGRAM_ID, + pumpfun::parser::PUMPFUN_PROGRAM_ID, pumpswap::parser::PUMPSWAP_PROGRAM_ID, + raydium_amm_v4::parser::RAYDIUM_AMM_V4_PROGRAM_ID, raydium_clmm::parser::RAYDIUM_CLMM_PROGRAM_ID, + raydium_cpmm::parser::RAYDIUM_CPMM_PROGRAM_ID, }; use anyhow::{anyhow, Result}; use solana_sdk::pubkey::Pubkey; @@ -18,8 +17,6 @@ pub enum Protocol { RaydiumClmm, RaydiumAmmV4, MeteoraDammV2, - MeteoraDlmm, - Whirlpool, } impl Protocol { @@ -32,8 +29,6 @@ impl Protocol { Protocol::RaydiumClmm => vec![RAYDIUM_CLMM_PROGRAM_ID], Protocol::RaydiumAmmV4 => vec![RAYDIUM_AMM_V4_PROGRAM_ID], Protocol::MeteoraDammV2 => vec![METEORA_DAMM_V2_PROGRAM_ID], - Protocol::MeteoraDlmm => vec![METEORA_DLMM_PROGRAM_ID], - Protocol::Whirlpool => vec![WHIRLPOOL_PROGRAM_ID], } } } @@ -48,8 +43,6 @@ impl std::fmt::Display for Protocol { Protocol::RaydiumClmm => write!(f, "RaydiumClmm"), Protocol::RaydiumAmmV4 => write!(f, "RaydiumAmmV4"), Protocol::MeteoraDammV2 => write!(f, "MeteoraDammV2"), - Protocol::MeteoraDlmm => write!(f, "MeteoraDlmm"), - Protocol::Whirlpool => write!(f, "Whirlpool"), } } } @@ -66,8 +59,6 @@ impl std::str::FromStr for Protocol { "raydiumclmm" => Ok(Protocol::RaydiumClmm), "raydiumammv4" => Ok(Protocol::RaydiumAmmV4), "meteoradamm_v2" => Ok(Protocol::MeteoraDammV2), - "meteoradlmm" => Ok(Protocol::MeteoraDlmm), - "whirlpool" => Ok(Protocol::Whirlpool), _ => Err(anyhow!("Unsupported protocol: {}", s)), } } diff --git a/src/streaming/event_parser/protocols/whirlpool/events.rs b/src/streaming/event_parser/protocols/whirlpool/events.rs deleted file mode 100644 index 9d5c561..0000000 --- a/src/streaming/event_parser/protocols/whirlpool/events.rs +++ /dev/null @@ -1,23 +0,0 @@ -use crate::streaming::event_parser::common::EventMetadata; -use crate::streaming::event_parser::protocols::whirlpool::types::Whirlpool; -use serde::{Deserialize, Serialize}; -use solana_sdk::pubkey::Pubkey; - -/// Whirlpool 账户事件 -#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] -pub struct WhirlpoolAccountEvent { - pub metadata: EventMetadata, - pub pubkey: Pubkey, - pub executable: bool, - pub lamports: u64, - pub owner: Pubkey, - pub rent_epoch: u64, - pub whirlpool: Whirlpool, -} - -/// 事件鉴别器常量 -pub mod discriminators { - // 账户鉴别器 - Anchor discriminator for "Whirlpool" account - // 这是通过 Anchor 的账户名称 "account:Whirlpool" 计算得出的 8 字节哈希 - pub const WHIRLPOOL: &[u8] = &[63, 149, 209, 12, 225, 128, 99, 9]; -} diff --git a/src/streaming/event_parser/protocols/whirlpool/mod.rs b/src/streaming/event_parser/protocols/whirlpool/mod.rs deleted file mode 100644 index 86156fa..0000000 --- a/src/streaming/event_parser/protocols/whirlpool/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod events; -pub mod parser; -pub mod types; - -pub use events::*; diff --git a/src/streaming/event_parser/protocols/whirlpool/parser.rs b/src/streaming/event_parser/protocols/whirlpool/parser.rs deleted file mode 100644 index c022e72..0000000 --- a/src/streaming/event_parser/protocols/whirlpool/parser.rs +++ /dev/null @@ -1,25 +0,0 @@ -use crate::streaming::event_parser::protocols::whirlpool::discriminators; -use solana_sdk::pubkey::Pubkey; - -/// Whirlpool 程序ID -pub const WHIRLPOOL_PROGRAM_ID: Pubkey = - solana_sdk::pubkey!("whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc"); - -/// 解析 Whirlpool 账户数据 -/// -/// 根据判别器路由到具体的账户解析函数 -pub fn parse_whirlpool_account_data( - discriminator: &[u8], - account: &crate::streaming::grpc::AccountPretty, - metadata: crate::streaming::event_parser::common::EventMetadata, -) -> Option { - match discriminator { - discriminators::WHIRLPOOL => { - crate::streaming::event_parser::protocols::whirlpool::types::whirlpool_parser( - account, - metadata, - ) - } - _ => None, - } -} diff --git a/src/streaming/event_parser/protocols/whirlpool/types.rs b/src/streaming/event_parser/protocols/whirlpool/types.rs deleted file mode 100644 index 579b468..0000000 --- a/src/streaming/event_parser/protocols/whirlpool/types.rs +++ /dev/null @@ -1,291 +0,0 @@ -use borsh::BorshDeserialize; -use serde::{Deserialize, Serialize}; -use solana_sdk::pubkey::Pubkey; - -use crate::streaming::{ - event_parser::{ - common::{EventMetadata, EventType}, - protocols::whirlpool::WhirlpoolAccountEvent, - DexEvent, - }, - grpc::AccountPretty, -}; - -// Number of rewards supported by Whirlpools -pub const NUM_REWARDS: usize = 3; - -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)] -pub struct WhirlpoolRewardInfo { - /// Reward token mint. - pub mint: Pubkey, - /// Reward vault token account. - pub vault: Pubkey, - /// Authority account that has permission to initialize the reward and set emissions. - pub authority: Pubkey, - /// Q64.64 number that indicates how many tokens per second are earned per unit of liquidity. - pub emissions_per_second_x64: u128, - /// Q64.64 number that tracks the total tokens earned per unit of liquidity since the reward - /// emissions were turned on. - pub growth_global_x64: u128, -} - -#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] -pub struct Whirlpool { - pub whirlpools_config: Pubkey, - pub whirlpool_bump: [u8; 1], - pub tick_spacing: u16, - pub fee_tier_index_seed: [u8; 2], - pub fee_rate: u16, - pub protocol_fee_rate: u16, - pub liquidity: u128, - pub sqrt_price: u128, - pub tick_current_index: i32, - pub protocol_fee_owed_a: u64, - pub protocol_fee_owed_b: u64, - pub token_mint_a: Pubkey, - pub token_vault_a: Pubkey, - pub fee_growth_global_a: u128, - pub token_mint_b: Pubkey, - pub token_vault_b: Pubkey, - pub fee_growth_global_b: u128, - pub reward_last_updated_timestamp: u64, - pub reward_infos: [WhirlpoolRewardInfo; NUM_REWARDS], -} - -// Whirlpool::LEN = 8 (discriminator) + 261 + 384 = 653 -// 261 = 32 + 1 + 2 + 2 + 2 + 2 + 16 + 16 + 4 + 8 + 8 + 32 + 32 + 16 + 32 + 32 + 16 + 8 -// 384 = 3 * 128 (each WhirlpoolRewardInfo is 128 bytes) -// 总数据大小(不包括 discriminator)= 261 + 384 = 645 -pub const WHIRLPOOL_SIZE: usize = 261 + 384; // 645 bytes (不包括 discriminator) -pub const WHIRLPOOL_REWARD_INFO_SIZE: usize = 128; - -pub fn whirlpool_decode(data: &[u8]) -> Option { - if data.len() < WHIRLPOOL_SIZE { - return None; - } - - let mut offset = 0; - - // whirlpools_config: Pubkey (32 bytes) - let whirlpools_config = Pubkey::try_from(&data[offset..offset + 32]).ok()?; - offset += 32; - - // whirlpool_bump: [u8; 1] (1 byte) - let whirlpool_bump = [data[offset]]; - offset += 1; - - // tick_spacing: u16 (2 bytes) - let tick_spacing = u16::from_le_bytes([data[offset], data[offset + 1]]); - offset += 2; - - // fee_tier_index_seed: [u8; 2] (2 bytes) - let fee_tier_index_seed = [data[offset], data[offset + 1]]; - offset += 2; - - // fee_rate: u16 (2 bytes) - let fee_rate = u16::from_le_bytes([data[offset], data[offset + 1]]); - offset += 2; - - // protocol_fee_rate: u16 (2 bytes) - let protocol_fee_rate = u16::from_le_bytes([data[offset], data[offset + 1]]); - offset += 2; - - // liquidity: u128 (16 bytes) - let mut liquidity_bytes = [0u8; 16]; - liquidity_bytes.copy_from_slice(&data[offset..offset + 16]); - let liquidity = u128::from_le_bytes(liquidity_bytes); - offset += 16; - - // sqrt_price: u128 (16 bytes) - let mut sqrt_price_bytes = [0u8; 16]; - sqrt_price_bytes.copy_from_slice(&data[offset..offset + 16]); - let sqrt_price = u128::from_le_bytes(sqrt_price_bytes); - offset += 16; - - // tick_current_index: i32 (4 bytes) - let tick_current_index = i32::from_le_bytes([ - data[offset], - data[offset + 1], - data[offset + 2], - data[offset + 3], - ]); - offset += 4; - - // protocol_fee_owed_a: u64 (8 bytes) - let protocol_fee_owed_a = u64::from_le_bytes([ - data[offset], - data[offset + 1], - data[offset + 2], - data[offset + 3], - data[offset + 4], - data[offset + 5], - data[offset + 6], - data[offset + 7], - ]); - offset += 8; - - // protocol_fee_owed_b: u64 (8 bytes) - let protocol_fee_owed_b = u64::from_le_bytes([ - data[offset], - data[offset + 1], - data[offset + 2], - data[offset + 3], - data[offset + 4], - data[offset + 5], - data[offset + 6], - data[offset + 7], - ]); - offset += 8; - - // token_mint_a: Pubkey (32 bytes) - let token_mint_a = Pubkey::try_from(&data[offset..offset + 32]).ok()?; - offset += 32; - - // token_vault_a: Pubkey (32 bytes) - let token_vault_a = Pubkey::try_from(&data[offset..offset + 32]).ok()?; - offset += 32; - - // fee_growth_global_a: u128 (16 bytes) - let mut fee_growth_global_a_bytes = [0u8; 16]; - fee_growth_global_a_bytes.copy_from_slice(&data[offset..offset + 16]); - let fee_growth_global_a = u128::from_le_bytes(fee_growth_global_a_bytes); - offset += 16; - - // token_mint_b: Pubkey (32 bytes) - let token_mint_b = Pubkey::try_from(&data[offset..offset + 32]).ok()?; - offset += 32; - - // token_vault_b: Pubkey (32 bytes) - let token_vault_b = Pubkey::try_from(&data[offset..offset + 32]).ok()?; - offset += 32; - - // fee_growth_global_b: u128 (16 bytes) - let mut fee_growth_global_b_bytes = [0u8; 16]; - fee_growth_global_b_bytes.copy_from_slice(&data[offset..offset + 16]); - let fee_growth_global_b = u128::from_le_bytes(fee_growth_global_b_bytes); - offset += 16; - - // reward_last_updated_timestamp: u64 (8 bytes) - let reward_last_updated_timestamp = u64::from_le_bytes([ - data[offset], - data[offset + 1], - data[offset + 2], - data[offset + 3], - data[offset + 4], - data[offset + 5], - data[offset + 6], - data[offset + 7], - ]); - offset += 8; - - // reward_infos: [WhirlpoolRewardInfo; NUM_REWARDS] (384 bytes) - // 检查是否有足够的数据来解析所有奖励信息 - if data.len() < offset + (NUM_REWARDS * WHIRLPOOL_REWARD_INFO_SIZE) { - log::warn!( - "Whirlpool 奖励信息数据不足: 需要 {} 字节,实际 {} 字节", - offset + (NUM_REWARDS * WHIRLPOOL_REWARD_INFO_SIZE), - data.len() - ); - return None; - } - - let mut reward_infos = [WhirlpoolRewardInfo::default(); NUM_REWARDS]; - for i in 0..NUM_REWARDS { - - // mint: Pubkey (32 bytes) - let mint = Pubkey::try_from(&data[offset..offset + 32]).ok()?; - offset += 32; - - // vault: Pubkey (32 bytes) - let vault = Pubkey::try_from(&data[offset..offset + 32]).ok()?; - offset += 32; - - // authority: Pubkey (32 bytes) - let authority = Pubkey::try_from(&data[offset..offset + 32]).ok()?; - offset += 32; - - // emissions_per_second_x64: u128 (16 bytes) - let mut emissions_bytes = [0u8; 16]; - emissions_bytes.copy_from_slice(&data[offset..offset + 16]); - let emissions_per_second_x64 = u128::from_le_bytes(emissions_bytes); - offset += 16; - - // growth_global_x64: u128 (16 bytes) - let mut growth_bytes = [0u8; 16]; - growth_bytes.copy_from_slice(&data[offset..offset + 16]); - let growth_global_x64 = u128::from_le_bytes(growth_bytes); - offset += 16; - - reward_infos[i] = WhirlpoolRewardInfo { - mint, - vault, - authority, - emissions_per_second_x64, - growth_global_x64, - }; - } - - Some(Whirlpool { - whirlpools_config, - whirlpool_bump, - tick_spacing, - fee_tier_index_seed, - fee_rate, - protocol_fee_rate, - liquidity, - sqrt_price, - tick_current_index, - protocol_fee_owed_a, - protocol_fee_owed_b, - token_mint_a, - token_vault_a, - fee_growth_global_a, - token_mint_b, - token_vault_b, - fee_growth_global_b, - reward_last_updated_timestamp, - reward_infos, - }) -} - -pub fn whirlpool_parser(account: &AccountPretty, mut metadata: EventMetadata) -> Option { - metadata.event_type = EventType::AccountWhirlpool; - - // 账户总大小应该是 8 (discriminator) + 645 (数据) = 653 字节 - let expected_size = 8 + WHIRLPOOL_SIZE; - if account.data.len() < expected_size { - log::warn!( - "Whirlpool 账户数据长度不足: 需要至少 {} 字节,实际 {} 字节", - expected_size, - account.data.len() - ); - return None; - } - - log::debug!( - "开始解析 Whirlpool 账户: pubkey={}, 数据长度={}, 期望长度={}", - account.pubkey, - account.data.len(), - expected_size - ); - - // 跳过前 8 字节的 discriminator,解析接下来的 645 字节 - if let Some(whirlpool) = whirlpool_decode(&account.data[8..8 + WHIRLPOOL_SIZE]) { - Some(DexEvent::WhirlpoolAccountEvent(WhirlpoolAccountEvent { - metadata, - pubkey: account.pubkey, - executable: account.executable, - lamports: account.lamports, - owner: account.owner, - rent_epoch: account.rent_epoch, - whirlpool, - })) - } else { - log::warn!( - "Whirlpool 账户数据解析失败: pubkey={}, 数据长度={}", - account.pubkey, - account.data.len() - ); - None - } -}