mirror of
https://github.com/0xfnzero/solana-streamer.git
synced 2026-08-20 12:28:08 +00:00
Release solana-streamer-sdk v1.4.3
Route gRPC, ShredStream, and account parsing through sol-parser-sdk. Remove local protocol parsing implementations. Depend on sol-parser-sdk v0.4.8 from crates.io.
This commit is contained in:
@@ -2,7 +2,6 @@ use borsh::BorshDeserialize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
|
||||
use crate::streaming::event_parser::common::utils::{read_i64_le, read_u32_le, read_u64_le};
|
||||
use crate::streaming::event_parser::common::EventMetadata;
|
||||
use crate::streaming::event_parser::protocols::pumpswap::types::{GlobalConfig, Pool};
|
||||
|
||||
@@ -70,102 +69,6 @@ pub const PUMP_SWAP_BUY_EVENT_LOG_MIN: usize = 385;
|
||||
/// Backwards-compatible name for the minimum BuyEvent payload size (legacy `borsh` slice length).
|
||||
pub const PUMP_SWAP_BUY_EVENT_LOG_SIZE: usize = PUMP_SWAP_BUY_EVENT_LOG_MIN;
|
||||
|
||||
pub fn pump_swap_buy_event_log_decode(data: &[u8]) -> Option<PumpSwapBuyEvent> {
|
||||
if data.len() < PUMP_SWAP_BUY_EVENT_LOG_MIN {
|
||||
return None;
|
||||
}
|
||||
let timestamp = read_i64_le(data, 0)?;
|
||||
let base_amount_out = read_u64_le(data, 8)?;
|
||||
let max_quote_amount_in = read_u64_le(data, 16)?;
|
||||
let user_base_token_reserves = read_u64_le(data, 24)?;
|
||||
let user_quote_token_reserves = read_u64_le(data, 32)?;
|
||||
let pool_base_token_reserves = read_u64_le(data, 40)?;
|
||||
let pool_quote_token_reserves = read_u64_le(data, 48)?;
|
||||
let quote_amount_in = read_u64_le(data, 56)?;
|
||||
let lp_fee_basis_points = read_u64_le(data, 64)?;
|
||||
let lp_fee = read_u64_le(data, 72)?;
|
||||
let protocol_fee_basis_points = read_u64_le(data, 80)?;
|
||||
let protocol_fee = read_u64_le(data, 88)?;
|
||||
let quote_amount_in_with_lp_fee = read_u64_le(data, 96)?;
|
||||
let user_quote_amount_in = read_u64_le(data, 104)?;
|
||||
let pool = Pubkey::new_from_array(data.get(112..144)?.try_into().ok()?);
|
||||
let user = Pubkey::new_from_array(data.get(144..176)?.try_into().ok()?);
|
||||
let user_base_token_account = Pubkey::new_from_array(data.get(176..208)?.try_into().ok()?);
|
||||
let user_quote_token_account = Pubkey::new_from_array(data.get(208..240)?.try_into().ok()?);
|
||||
let protocol_fee_recipient = Pubkey::new_from_array(data.get(240..272)?.try_into().ok()?);
|
||||
let protocol_fee_recipient_token_account =
|
||||
Pubkey::new_from_array(data.get(272..304)?.try_into().ok()?);
|
||||
let coin_creator = Pubkey::new_from_array(data.get(304..336)?.try_into().ok()?);
|
||||
let coin_creator_fee_basis_points = read_u64_le(data, 336)?;
|
||||
let coin_creator_fee = read_u64_le(data, 344)?;
|
||||
let track_volume = *data.get(352)? != 0;
|
||||
let total_unclaimed_tokens = read_u64_le(data, 353)?;
|
||||
let total_claimed_tokens = read_u64_le(data, 361)?;
|
||||
let current_sol_volume = read_u64_le(data, 369)?;
|
||||
let last_update_timestamp = read_i64_le(data, 377)?;
|
||||
|
||||
let mut offset = 385usize;
|
||||
let min_base_amount_out = if data.len() >= offset + 8 {
|
||||
let v = read_u64_le(data, offset)?;
|
||||
offset += 8;
|
||||
v
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
let ix_name = if data.len() >= offset + 4 {
|
||||
let slen = read_u32_le(data, offset)? as usize;
|
||||
let str_start = offset + 4;
|
||||
if data.len() < str_start + slen {
|
||||
return None;
|
||||
}
|
||||
offset = str_start + slen;
|
||||
String::from_utf8_lossy(&data[str_start..offset]).into_owned()
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
let cashback_fee_basis_points = read_u64_le(data, offset).unwrap_or(0);
|
||||
let cashback = read_u64_le(data, offset + 8).unwrap_or(0);
|
||||
|
||||
Some(PumpSwapBuyEvent {
|
||||
metadata: EventMetadata::default(),
|
||||
timestamp,
|
||||
base_amount_out,
|
||||
max_quote_amount_in,
|
||||
user_base_token_reserves,
|
||||
user_quote_token_reserves,
|
||||
pool_base_token_reserves,
|
||||
pool_quote_token_reserves,
|
||||
quote_amount_in,
|
||||
lp_fee_basis_points,
|
||||
lp_fee,
|
||||
protocol_fee_basis_points,
|
||||
protocol_fee,
|
||||
quote_amount_in_with_lp_fee,
|
||||
user_quote_amount_in,
|
||||
pool,
|
||||
user,
|
||||
user_base_token_account,
|
||||
user_quote_token_account,
|
||||
protocol_fee_recipient,
|
||||
protocol_fee_recipient_token_account,
|
||||
coin_creator,
|
||||
coin_creator_fee_basis_points,
|
||||
coin_creator_fee,
|
||||
track_volume,
|
||||
total_unclaimed_tokens,
|
||||
total_claimed_tokens,
|
||||
current_sol_volume,
|
||||
last_update_timestamp,
|
||||
min_base_amount_out,
|
||||
ix_name,
|
||||
cashback_fee_basis_points,
|
||||
cashback,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
/// 卖出事件
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
|
||||
pub struct PumpSwapSellEvent {
|
||||
@@ -221,72 +124,6 @@ pub const PUMP_SWAP_SELL_EVENT_WITH_CASHBACK: usize = 368;
|
||||
/// Backwards-compatible name for the pre-cashback SellEvent payload size.
|
||||
pub const PUMP_SWAP_SELL_EVENT_LOG_SIZE: usize = PUMP_SWAP_SELL_EVENT_LOG_MIN;
|
||||
|
||||
pub fn pump_swap_sell_event_log_decode(data: &[u8]) -> Option<PumpSwapSellEvent> {
|
||||
if data.len() < PUMP_SWAP_SELL_EVENT_LOG_MIN {
|
||||
return None;
|
||||
}
|
||||
let timestamp = read_i64_le(data, 0)?;
|
||||
let base_amount_in = read_u64_le(data, 8)?;
|
||||
let min_quote_amount_out = read_u64_le(data, 16)?;
|
||||
let user_base_token_reserves = read_u64_le(data, 24)?;
|
||||
let user_quote_token_reserves = read_u64_le(data, 32)?;
|
||||
let pool_base_token_reserves = read_u64_le(data, 40)?;
|
||||
let pool_quote_token_reserves = read_u64_le(data, 48)?;
|
||||
let quote_amount_out = read_u64_le(data, 56)?;
|
||||
let lp_fee_basis_points = read_u64_le(data, 64)?;
|
||||
let lp_fee = read_u64_le(data, 72)?;
|
||||
let protocol_fee_basis_points = read_u64_le(data, 80)?;
|
||||
let protocol_fee = read_u64_le(data, 88)?;
|
||||
let quote_amount_out_without_lp_fee = read_u64_le(data, 96)?;
|
||||
let user_quote_amount_out = read_u64_le(data, 104)?;
|
||||
let pool = Pubkey::new_from_array(data.get(112..144)?.try_into().ok()?);
|
||||
let user = Pubkey::new_from_array(data.get(144..176)?.try_into().ok()?);
|
||||
let user_base_token_account = Pubkey::new_from_array(data.get(176..208)?.try_into().ok()?);
|
||||
let user_quote_token_account = Pubkey::new_from_array(data.get(208..240)?.try_into().ok()?);
|
||||
let protocol_fee_recipient = Pubkey::new_from_array(data.get(240..272)?.try_into().ok()?);
|
||||
let protocol_fee_recipient_token_account =
|
||||
Pubkey::new_from_array(data.get(272..304)?.try_into().ok()?);
|
||||
let coin_creator = Pubkey::new_from_array(data.get(304..336)?.try_into().ok()?);
|
||||
let coin_creator_fee_basis_points = read_u64_le(data, 336)?;
|
||||
let coin_creator_fee = read_u64_le(data, 344)?;
|
||||
let (cashback_fee_basis_points, cashback) = if data.len() >= PUMP_SWAP_SELL_EVENT_WITH_CASHBACK
|
||||
{
|
||||
(read_u64_le(data, 352)?, read_u64_le(data, 360)?)
|
||||
} else {
|
||||
(0, 0)
|
||||
};
|
||||
|
||||
Some(PumpSwapSellEvent {
|
||||
metadata: EventMetadata::default(),
|
||||
timestamp,
|
||||
base_amount_in,
|
||||
min_quote_amount_out,
|
||||
user_base_token_reserves,
|
||||
user_quote_token_reserves,
|
||||
pool_base_token_reserves,
|
||||
pool_quote_token_reserves,
|
||||
quote_amount_out,
|
||||
lp_fee_basis_points,
|
||||
lp_fee,
|
||||
protocol_fee_basis_points,
|
||||
protocol_fee,
|
||||
quote_amount_out_without_lp_fee,
|
||||
user_quote_amount_out,
|
||||
pool,
|
||||
user,
|
||||
user_base_token_account,
|
||||
user_quote_token_account,
|
||||
protocol_fee_recipient,
|
||||
protocol_fee_recipient_token_account,
|
||||
coin_creator,
|
||||
coin_creator_fee_basis_points,
|
||||
coin_creator_fee,
|
||||
cashback_fee_basis_points,
|
||||
cashback,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
/// 创建池子事件
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
|
||||
pub struct PumpSwapCreatePoolEvent {
|
||||
@@ -322,13 +159,6 @@ pub struct PumpSwapCreatePoolEvent {
|
||||
|
||||
pub const PUMP_SWAP_CREATE_POOL_EVENT_LOG_SIZE: usize = 325;
|
||||
|
||||
pub fn pump_swap_create_pool_event_log_decode(data: &[u8]) -> Option<PumpSwapCreatePoolEvent> {
|
||||
if data.len() < PUMP_SWAP_CREATE_POOL_EVENT_LOG_SIZE {
|
||||
return None;
|
||||
}
|
||||
borsh::from_slice::<PumpSwapCreatePoolEvent>(&data[..PUMP_SWAP_CREATE_POOL_EVENT_LOG_SIZE]).ok()
|
||||
}
|
||||
|
||||
/// 存款事件
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
|
||||
pub struct PumpSwapDepositEvent {
|
||||
@@ -362,13 +192,6 @@ pub struct PumpSwapDepositEvent {
|
||||
|
||||
pub const PUMP_SWAP_DEPOSIT_EVENT_LOG_SIZE: usize = 248;
|
||||
|
||||
pub fn pump_swap_deposit_event_log_decode(data: &[u8]) -> Option<PumpSwapDepositEvent> {
|
||||
if data.len() < PUMP_SWAP_DEPOSIT_EVENT_LOG_SIZE {
|
||||
return None;
|
||||
}
|
||||
borsh::from_slice::<PumpSwapDepositEvent>(&data[..PUMP_SWAP_DEPOSIT_EVENT_LOG_SIZE]).ok()
|
||||
}
|
||||
|
||||
/// 提款事件
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
|
||||
pub struct PumpSwapWithdrawEvent {
|
||||
@@ -402,13 +225,6 @@ pub struct PumpSwapWithdrawEvent {
|
||||
|
||||
pub const PUMP_SWAP_WITHDRAW_EVENT_LOG_SIZE: usize = 248;
|
||||
|
||||
pub fn pump_swap_withdraw_event_log_decode(data: &[u8]) -> Option<PumpSwapWithdrawEvent> {
|
||||
if data.len() < PUMP_SWAP_WITHDRAW_EVENT_LOG_SIZE {
|
||||
return None;
|
||||
}
|
||||
borsh::from_slice::<PumpSwapWithdrawEvent>(&data[..PUMP_SWAP_WITHDRAW_EVENT_LOG_SIZE]).ok()
|
||||
}
|
||||
|
||||
/// 全局配置
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
|
||||
pub struct PumpSwapGlobalConfigAccountEvent {
|
||||
|
||||
Executable → Regular
+12
-342
@@ -1,367 +1,37 @@
|
||||
use crate::streaming::event_parser::{
|
||||
common::{read_u64_le, EventMetadata, EventType},
|
||||
protocols::pumpswap::{
|
||||
discriminators, pump_swap_buy_event_log_decode, pump_swap_create_pool_event_log_decode,
|
||||
pump_swap_deposit_event_log_decode, pump_swap_sell_event_log_decode,
|
||||
pump_swap_withdraw_event_log_decode, PumpSwapBuyEvent, PumpSwapCreatePoolEvent,
|
||||
PumpSwapDepositEvent, PumpSwapSellEvent, PumpSwapWithdrawEvent,
|
||||
},
|
||||
DexEvent,
|
||||
common::EventMetadata, core::EventDispatcher, DexEvent, Protocol,
|
||||
};
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
|
||||
/// PumpSwap程序ID
|
||||
pub const PUMPSWAP_PROGRAM_ID: Pubkey =
|
||||
solana_sdk::pubkey!("pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA");
|
||||
pub use sol_parser_sdk::instr::program_ids::PUMPSWAP_PROGRAM_ID;
|
||||
|
||||
/// 解析 PumpSwap instruction data
|
||||
///
|
||||
/// 根据判别器路由到具体的 instruction 解析函数
|
||||
pub fn parse_pumpswap_instruction_data(
|
||||
discriminator: &[u8],
|
||||
data: &[u8],
|
||||
accounts: &[Pubkey],
|
||||
metadata: EventMetadata,
|
||||
) -> Option<DexEvent> {
|
||||
match discriminator {
|
||||
discriminators::BUY_IX => parse_buy_instruction(data, accounts, metadata),
|
||||
discriminators::BUY_EXACT_QUOTE_IN_IX => {
|
||||
parse_buy_exact_quote_in_instruction(data, accounts, metadata)
|
||||
}
|
||||
discriminators::SELL_IX => parse_sell_instruction(data, accounts, metadata),
|
||||
discriminators::CREATE_POOL_IX => parse_create_pool_instruction(data, accounts, metadata),
|
||||
discriminators::DEPOSIT_IX => parse_deposit_instruction(data, accounts, metadata),
|
||||
discriminators::WITHDRAW_IX => parse_withdraw_instruction(data, accounts, metadata),
|
||||
_ => None,
|
||||
}
|
||||
EventDispatcher::dispatch_instruction(
|
||||
Protocol::PumpSwap,
|
||||
discriminator,
|
||||
data,
|
||||
accounts,
|
||||
metadata,
|
||||
)
|
||||
}
|
||||
|
||||
/// 解析 PumpSwap inner instruction data
|
||||
///
|
||||
/// 根据判别器路由到具体的 inner instruction 解析函数
|
||||
pub fn parse_pumpswap_inner_instruction_data(
|
||||
discriminator: &[u8],
|
||||
data: &[u8],
|
||||
metadata: EventMetadata,
|
||||
) -> Option<DexEvent> {
|
||||
match discriminator {
|
||||
discriminators::BUY_EVENT => parse_buy_inner_instruction(data, metadata),
|
||||
discriminators::SELL_EVENT => parse_sell_inner_instruction(data, metadata),
|
||||
discriminators::CREATE_POOL_EVENT => parse_create_pool_inner_instruction(data, metadata),
|
||||
discriminators::DEPOSIT_EVENT => parse_deposit_inner_instruction(data, metadata),
|
||||
discriminators::WITHDRAW_EVENT => parse_withdraw_inner_instruction(data, metadata),
|
||||
_ => None,
|
||||
}
|
||||
EventDispatcher::dispatch_inner_instruction(Protocol::PumpSwap, discriminator, data, metadata)
|
||||
}
|
||||
|
||||
/// 解析 PumpSwap 账户数据
|
||||
///
|
||||
/// 根据判别器路由到具体的账户解析函数
|
||||
pub fn parse_pumpswap_account_data(
|
||||
discriminator: &[u8],
|
||||
account: &crate::streaming::grpc::AccountPretty,
|
||||
metadata: crate::streaming::event_parser::common::EventMetadata,
|
||||
) -> Option<crate::streaming::event_parser::DexEvent> {
|
||||
match discriminator {
|
||||
discriminators::GLOBAL_CONFIG_ACCOUNT => {
|
||||
crate::streaming::event_parser::protocols::pumpswap::types::global_config_parser(
|
||||
account, metadata,
|
||||
)
|
||||
}
|
||||
discriminators::POOL_ACCOUNT => {
|
||||
crate::streaming::event_parser::protocols::pumpswap::types::pool_parser(
|
||||
account, metadata,
|
||||
)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// 解析买入日志事件
|
||||
fn parse_buy_inner_instruction(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
|
||||
// Note: event_type will be set by instruction parser
|
||||
if let Some(event) = pump_swap_buy_event_log_decode(data) {
|
||||
Some(DexEvent::PumpSwapBuyEvent(PumpSwapBuyEvent { metadata, ..event }))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// 解析卖出日志事件
|
||||
fn parse_sell_inner_instruction(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
|
||||
// Note: event_type will be set by instruction parser
|
||||
if let Some(event) = pump_swap_sell_event_log_decode(data) {
|
||||
Some(DexEvent::PumpSwapSellEvent(PumpSwapSellEvent { metadata, ..event }))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// 解析创建池子日志事件
|
||||
fn parse_create_pool_inner_instruction(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
|
||||
// Note: event_type will be set by instruction parser
|
||||
if let Some(event) = pump_swap_create_pool_event_log_decode(data) {
|
||||
Some(DexEvent::PumpSwapCreatePoolEvent(PumpSwapCreatePoolEvent { metadata, ..event }))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// 解析存款日志事件
|
||||
fn parse_deposit_inner_instruction(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
|
||||
// Note: event_type will be set by instruction parser
|
||||
if let Some(event) = pump_swap_deposit_event_log_decode(data) {
|
||||
Some(DexEvent::PumpSwapDepositEvent(PumpSwapDepositEvent { metadata, ..event }))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// 解析提款日志事件
|
||||
fn parse_withdraw_inner_instruction(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
|
||||
// Note: event_type will be set by instruction parser
|
||||
if let Some(event) = pump_swap_withdraw_event_log_decode(data) {
|
||||
Some(DexEvent::PumpSwapWithdrawEvent(PumpSwapWithdrawEvent { metadata, ..event }))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// 解析买入指令事件
|
||||
/// Buy 指令共 23 个固定账户(与 idl/pump_amm.json 一致):
|
||||
/// 0: pool, 1: user, 2: global_config, 3: base_mint, 4: quote_mint, 5: user_base_token_account,
|
||||
/// 6: user_quote_token_account, 7: pool_base_token_account, 8: pool_quote_token_account,
|
||||
/// 9: protocol_fee_recipient, 10: protocol_fee_recipient_token_account, 11: base_token_program,
|
||||
/// 12: quote_token_program, 13: system_program, 14: associated_token_program, 15: event_authority,
|
||||
/// 16: program, 17: coin_creator_vault_ata, 18: coin_creator_vault_authority,
|
||||
/// 19: global_volume_accumulator, 20: user_volume_accumulator, 21: fee_config, 22: fee_program.
|
||||
fn parse_buy_instruction(
|
||||
data: &[u8],
|
||||
accounts: &[Pubkey],
|
||||
mut metadata: EventMetadata,
|
||||
metadata: EventMetadata,
|
||||
) -> Option<DexEvent> {
|
||||
metadata.event_type = EventType::PumpSwapBuy;
|
||||
|
||||
if data.len() < 16 || accounts.len() < 13 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let base_amount_out = read_u64_le(data, 0)?;
|
||||
let max_quote_amount_in = read_u64_le(data, 8)?;
|
||||
|
||||
Some(DexEvent::PumpSwapBuyEvent(PumpSwapBuyEvent {
|
||||
metadata,
|
||||
base_amount_out,
|
||||
max_quote_amount_in,
|
||||
ix_name: "buy".to_string(),
|
||||
pool: accounts[0],
|
||||
user: accounts[1],
|
||||
base_mint: accounts[3],
|
||||
quote_mint: accounts[4],
|
||||
user_base_token_account: accounts[5],
|
||||
user_quote_token_account: accounts[6],
|
||||
pool_base_token_account: accounts[7],
|
||||
pool_quote_token_account: accounts[8],
|
||||
protocol_fee_recipient: accounts[9],
|
||||
protocol_fee_recipient_token_account: accounts[10],
|
||||
base_token_program: accounts[11],
|
||||
quote_token_program: accounts[12],
|
||||
coin_creator_vault_ata: accounts.get(17).copied().unwrap_or_default(),
|
||||
coin_creator_vault_authority: accounts.get(18).copied().unwrap_or_default(),
|
||||
..Default::default()
|
||||
}))
|
||||
}
|
||||
|
||||
/// 解析 buy_exact_quote_in 指令事件
|
||||
/// 账户布局与 buy 相同,共 23 个固定账户(0–22,17/18 为 coin_creator_vault_ata / coin_creator_vault_authority)。
|
||||
/// 参数顺序与 buy 不同: spendable_quote_in (SOL), min_base_amount_out (token).
|
||||
fn parse_buy_exact_quote_in_instruction(
|
||||
data: &[u8],
|
||||
accounts: &[Pubkey],
|
||||
mut metadata: EventMetadata,
|
||||
) -> Option<DexEvent> {
|
||||
metadata.event_type = EventType::PumpSwapBuy;
|
||||
|
||||
if data.len() < 16 || accounts.len() < 13 {
|
||||
return None;
|
||||
}
|
||||
|
||||
// 注意:buy_exact_quote_in 的参数顺序是先 quote (SOL) 再 base (token)
|
||||
let spendable_quote_in = read_u64_le(data, 0)?;
|
||||
let min_base_amount_out = read_u64_le(data, 8)?;
|
||||
|
||||
Some(DexEvent::PumpSwapBuyEvent(PumpSwapBuyEvent {
|
||||
metadata,
|
||||
base_amount_out: min_base_amount_out,
|
||||
max_quote_amount_in: spendable_quote_in,
|
||||
min_base_amount_out,
|
||||
ix_name: "buy_exact_quote_in".to_string(),
|
||||
pool: accounts[0],
|
||||
user: accounts[1],
|
||||
base_mint: accounts[3],
|
||||
quote_mint: accounts[4],
|
||||
user_base_token_account: accounts[5],
|
||||
user_quote_token_account: accounts[6],
|
||||
pool_base_token_account: accounts[7],
|
||||
pool_quote_token_account: accounts[8],
|
||||
protocol_fee_recipient: accounts[9],
|
||||
protocol_fee_recipient_token_account: accounts[10],
|
||||
base_token_program: accounts[11],
|
||||
quote_token_program: accounts[12],
|
||||
coin_creator_vault_ata: accounts.get(17).copied().unwrap_or_default(),
|
||||
coin_creator_vault_authority: accounts.get(18).copied().unwrap_or_default(),
|
||||
..Default::default()
|
||||
}))
|
||||
}
|
||||
|
||||
/// 解析卖出指令事件
|
||||
/// Sell 指令共 21 个固定账户(与 idl/pump_amm.json 一致):
|
||||
/// 0: pool, 1: user, 2: global_config, 3: base_mint, 4: quote_mint, 5: user_base_token_account,
|
||||
/// 6: user_quote_token_account, 7: pool_base_token_account, 8: pool_quote_token_account,
|
||||
/// 9: protocol_fee_recipient, 10: protocol_fee_recipient_token_account, 11: base_token_program,
|
||||
/// 12: quote_token_program, 13: system_program, 14: associated_token_program, 15: event_authority,
|
||||
/// 16: program, 17: coin_creator_vault_ata, 18: coin_creator_vault_authority, 19: fee_config, 20: fee_program.
|
||||
fn parse_sell_instruction(
|
||||
data: &[u8],
|
||||
accounts: &[Pubkey],
|
||||
mut metadata: EventMetadata,
|
||||
) -> Option<DexEvent> {
|
||||
metadata.event_type = EventType::PumpSwapSell;
|
||||
|
||||
if data.len() < 16 || accounts.len() < 13 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let base_amount_in = read_u64_le(data, 0)?;
|
||||
let min_quote_amount_out = read_u64_le(data, 8)?;
|
||||
|
||||
Some(DexEvent::PumpSwapSellEvent(PumpSwapSellEvent {
|
||||
metadata,
|
||||
base_amount_in,
|
||||
min_quote_amount_out,
|
||||
pool: accounts[0],
|
||||
user: accounts[1],
|
||||
base_mint: accounts[3],
|
||||
quote_mint: accounts[4],
|
||||
user_base_token_account: accounts[5],
|
||||
user_quote_token_account: accounts[6],
|
||||
pool_base_token_account: accounts[7],
|
||||
pool_quote_token_account: accounts[8],
|
||||
protocol_fee_recipient: accounts[9],
|
||||
protocol_fee_recipient_token_account: accounts[10],
|
||||
base_token_program: accounts[11],
|
||||
quote_token_program: accounts[12],
|
||||
coin_creator_vault_ata: accounts.get(17).copied().unwrap_or_default(),
|
||||
coin_creator_vault_authority: accounts.get(18).copied().unwrap_or_default(),
|
||||
..Default::default()
|
||||
}))
|
||||
}
|
||||
|
||||
/// 解析创建池子指令事件
|
||||
fn parse_create_pool_instruction(
|
||||
data: &[u8],
|
||||
accounts: &[Pubkey],
|
||||
mut metadata: EventMetadata,
|
||||
) -> Option<DexEvent> {
|
||||
metadata.event_type = EventType::PumpSwapCreatePool;
|
||||
|
||||
if data.len() < 18 || accounts.len() < 11 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let index = u16::from_le_bytes(data[0..2].try_into().ok()?);
|
||||
let base_amount_in = u64::from_le_bytes(data[2..10].try_into().ok()?);
|
||||
let quote_amount_in = u64::from_le_bytes(data[10..18].try_into().ok()?);
|
||||
let coin_creator = if data.len() >= 50 {
|
||||
Pubkey::new_from_array(data[18..50].try_into().ok()?)
|
||||
} else {
|
||||
Pubkey::default()
|
||||
};
|
||||
|
||||
Some(DexEvent::PumpSwapCreatePoolEvent(PumpSwapCreatePoolEvent {
|
||||
metadata,
|
||||
index,
|
||||
base_amount_in,
|
||||
quote_amount_in,
|
||||
pool: accounts[0],
|
||||
creator: accounts[2],
|
||||
base_mint: accounts[3],
|
||||
quote_mint: accounts[4],
|
||||
lp_mint: accounts[5],
|
||||
user_base_token_account: accounts[6],
|
||||
user_quote_token_account: accounts[7],
|
||||
user_pool_token_account: accounts[8],
|
||||
pool_base_token_account: accounts[9],
|
||||
pool_quote_token_account: accounts[10],
|
||||
coin_creator,
|
||||
..Default::default()
|
||||
}))
|
||||
}
|
||||
|
||||
/// 解析存款指令事件
|
||||
fn parse_deposit_instruction(
|
||||
data: &[u8],
|
||||
accounts: &[Pubkey],
|
||||
mut metadata: EventMetadata,
|
||||
) -> Option<DexEvent> {
|
||||
metadata.event_type = EventType::PumpSwapDeposit;
|
||||
|
||||
if data.len() < 24 || accounts.len() < 11 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let lp_token_amount_out = u64::from_le_bytes(data[0..8].try_into().ok()?);
|
||||
let max_base_amount_in = u64::from_le_bytes(data[8..16].try_into().ok()?);
|
||||
let max_quote_amount_in = u64::from_le_bytes(data[16..24].try_into().ok()?);
|
||||
|
||||
Some(DexEvent::PumpSwapDepositEvent(PumpSwapDepositEvent {
|
||||
metadata,
|
||||
lp_token_amount_out,
|
||||
max_base_amount_in,
|
||||
max_quote_amount_in,
|
||||
pool: accounts[0],
|
||||
user: accounts[2],
|
||||
base_mint: accounts[3],
|
||||
quote_mint: accounts[4],
|
||||
user_base_token_account: accounts[6],
|
||||
user_quote_token_account: accounts[7],
|
||||
user_pool_token_account: accounts[8],
|
||||
pool_base_token_account: accounts[9],
|
||||
pool_quote_token_account: accounts[10],
|
||||
..Default::default()
|
||||
}))
|
||||
}
|
||||
|
||||
/// 解析提款指令事件
|
||||
fn parse_withdraw_instruction(
|
||||
data: &[u8],
|
||||
accounts: &[Pubkey],
|
||||
mut metadata: EventMetadata,
|
||||
) -> Option<DexEvent> {
|
||||
metadata.event_type = EventType::PumpSwapWithdraw;
|
||||
|
||||
if data.len() < 24 || accounts.len() < 11 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let lp_token_amount_in = u64::from_le_bytes(data[0..8].try_into().ok()?);
|
||||
let min_base_amount_out = u64::from_le_bytes(data[8..16].try_into().ok()?);
|
||||
let min_quote_amount_out = u64::from_le_bytes(data[16..24].try_into().ok()?);
|
||||
|
||||
Some(DexEvent::PumpSwapWithdrawEvent(PumpSwapWithdrawEvent {
|
||||
metadata,
|
||||
lp_token_amount_in,
|
||||
min_base_amount_out,
|
||||
min_quote_amount_out,
|
||||
pool: accounts[0],
|
||||
user: accounts[2],
|
||||
base_mint: accounts[3],
|
||||
quote_mint: accounts[4],
|
||||
user_base_token_account: accounts[6],
|
||||
user_quote_token_account: accounts[7],
|
||||
user_pool_token_account: accounts[8],
|
||||
pool_base_token_account: accounts[9],
|
||||
pool_quote_token_account: accounts[10],
|
||||
..Default::default()
|
||||
}))
|
||||
EventDispatcher::dispatch_account(Protocol::PumpSwap, discriminator, account, metadata)
|
||||
}
|
||||
|
||||
@@ -2,15 +2,6 @@ use borsh::BorshDeserialize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
|
||||
use crate::streaming::{
|
||||
event_parser::{
|
||||
common::{EventMetadata, EventType},
|
||||
protocols::pumpswap::{PumpSwapGlobalConfigAccountEvent, PumpSwapPoolAccountEvent},
|
||||
DexEvent,
|
||||
},
|
||||
grpc::AccountPretty,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
|
||||
pub struct GlobalConfig {
|
||||
pub admin: Pubkey,
|
||||
@@ -28,37 +19,6 @@ pub struct GlobalConfig {
|
||||
|
||||
pub const GLOBAL_CONFIG_SIZE: usize = 32 + 8 + 8 + 1 + 32 * 8 + 8 + 32 + 32 + 32 + 1 + 32 * 7;
|
||||
|
||||
pub fn global_config_decode(data: &[u8]) -> Option<GlobalConfig> {
|
||||
if data.len() < GLOBAL_CONFIG_SIZE {
|
||||
return None;
|
||||
}
|
||||
borsh::from_slice::<GlobalConfig>(&data[..GLOBAL_CONFIG_SIZE]).ok()
|
||||
}
|
||||
|
||||
pub fn global_config_parser(
|
||||
account: &AccountPretty,
|
||||
mut metadata: EventMetadata,
|
||||
) -> Option<DexEvent> {
|
||||
metadata.event_type = EventType::AccountPumpSwapGlobalConfig;
|
||||
|
||||
if account.data.len() < GLOBAL_CONFIG_SIZE + 8 {
|
||||
return None;
|
||||
}
|
||||
if let Some(config) = global_config_decode(&account.data[8..GLOBAL_CONFIG_SIZE + 8]) {
|
||||
Some(DexEvent::PumpSwapGlobalConfigAccountEvent(PumpSwapGlobalConfigAccountEvent {
|
||||
metadata,
|
||||
pubkey: account.pubkey,
|
||||
executable: account.executable,
|
||||
lamports: account.lamports,
|
||||
owner: account.owner,
|
||||
rent_epoch: account.rent_epoch,
|
||||
global_config: config,
|
||||
}))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
|
||||
pub struct Pool {
|
||||
pub pool_bump: u8,
|
||||
@@ -83,108 +43,3 @@ pub const POOL_BODY_LEGACY: usize = 1 + 2 + 32 * 6 + 8 + 32 + 1;
|
||||
pub const POOL_BODY: usize = POOL_BODY_LEGACY + 1 + 7;
|
||||
|
||||
pub const POOL_SIZE: usize = POOL_BODY;
|
||||
|
||||
pub fn pool_decode(data: &[u8]) -> Option<Pool> {
|
||||
if data.len() >= POOL_BODY {
|
||||
return borsh::from_slice::<Pool>(&data[..POOL_BODY]).ok();
|
||||
}
|
||||
if data.len() < POOL_BODY_LEGACY {
|
||||
return None;
|
||||
}
|
||||
let legacy = borsh::from_slice::<PoolLegacy>(&data[..POOL_BODY_LEGACY]).ok()?;
|
||||
Some(legacy.into())
|
||||
}
|
||||
|
||||
/// Pre-cashback on-chain layout (Borsh-compatible prefix of `Pool`).
|
||||
#[derive(Clone, Debug, BorshDeserialize)]
|
||||
struct PoolLegacy {
|
||||
pub pool_bump: u8,
|
||||
pub index: u16,
|
||||
pub creator: Pubkey,
|
||||
pub base_mint: Pubkey,
|
||||
pub quote_mint: Pubkey,
|
||||
pub lp_mint: Pubkey,
|
||||
pub pool_base_token_account: Pubkey,
|
||||
pub pool_quote_token_account: Pubkey,
|
||||
pub lp_supply: u64,
|
||||
pub coin_creator: Pubkey,
|
||||
pub is_mayhem_mode: bool,
|
||||
}
|
||||
|
||||
impl From<PoolLegacy> for Pool {
|
||||
fn from(p: PoolLegacy) -> Self {
|
||||
Pool {
|
||||
pool_bump: p.pool_bump,
|
||||
index: p.index,
|
||||
creator: p.creator,
|
||||
base_mint: p.base_mint,
|
||||
quote_mint: p.quote_mint,
|
||||
lp_mint: p.lp_mint,
|
||||
pool_base_token_account: p.pool_base_token_account,
|
||||
pool_quote_token_account: p.pool_quote_token_account,
|
||||
lp_supply: p.lp_supply,
|
||||
coin_creator: p.coin_creator,
|
||||
is_mayhem_mode: p.is_mayhem_mode,
|
||||
is_cashback_coin: false,
|
||||
reserved: [0u8; 7],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pool_parser(account: &AccountPretty, mut metadata: EventMetadata) -> Option<DexEvent> {
|
||||
metadata.event_type = EventType::AccountPumpSwapPool;
|
||||
|
||||
let body = account.data.get(8..)?;
|
||||
if body.len() < POOL_BODY_LEGACY {
|
||||
return None;
|
||||
}
|
||||
if let Some(pool) = pool_decode(body) {
|
||||
Some(DexEvent::PumpSwapPoolAccountEvent(PumpSwapPoolAccountEvent {
|
||||
metadata,
|
||||
pubkey: account.pubkey,
|
||||
executable: account.executable,
|
||||
lamports: account.lamports,
|
||||
owner: account.owner,
|
||||
rent_epoch: account.rent_epoch,
|
||||
pool,
|
||||
}))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn pool_decode_legacy_and_extended() {
|
||||
let keys: Vec<Pubkey> = (0..6).map(|_| Pubkey::new_unique()).collect();
|
||||
let coin = Pubkey::new_unique();
|
||||
let mut legacy = Vec::new();
|
||||
legacy.push(9u8);
|
||||
legacy.extend_from_slice(&7u16.to_le_bytes());
|
||||
for k in &keys {
|
||||
legacy.extend_from_slice(k.as_ref());
|
||||
}
|
||||
legacy.extend_from_slice(&99u64.to_le_bytes());
|
||||
legacy.extend_from_slice(coin.as_ref());
|
||||
legacy.push(1u8);
|
||||
|
||||
let p = pool_decode(&legacy).expect("legacy");
|
||||
assert_eq!(p.pool_bump, 9);
|
||||
assert_eq!(p.index, 7);
|
||||
assert_eq!(p.lp_supply, 99);
|
||||
assert!(p.is_mayhem_mode);
|
||||
assert!(!p.is_cashback_coin);
|
||||
assert_eq!(p.reserved, [0u8; 7]);
|
||||
|
||||
let mut ext = legacy.clone();
|
||||
ext.push(1u8);
|
||||
ext.extend_from_slice(&[2u8, 3, 4, 5, 6, 7, 8]);
|
||||
|
||||
let p2 = pool_decode(&ext).expect("extended");
|
||||
assert!(p2.is_cashback_coin);
|
||||
assert_eq!(p2.reserved, [2, 3, 4, 5, 6, 7, 8]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user