Release solana-streamer-sdk v1.4.6

This commit is contained in:
0xfnzero
2026-05-18 01:53:25 +08:00
parent b60838a840
commit ea0f9b9145
9 changed files with 315 additions and 26 deletions
@@ -37,9 +37,10 @@ pub(crate) fn process_event(event: DexEvent, bot_wallet: Option<Pubkey>) -> DexE
DexEvent::PumpFunCreateV2TokenEvent(token_info)
}
DexEvent::PumpFunTradeEvent(mut trade_info) => {
trade_info.is_dev_create_token_trade =
is_dev_address_in_signature(&signature, &trade_info.user)
|| is_dev_address_in_signature(&signature, &trade_info.creator);
trade_info.is_dev_create_token_trade = trade_info.is_dev_create_token_trade
|| trade_info.is_created_buy
|| is_dev_address_in_signature(&signature, &trade_info.user)
|| is_dev_address_in_signature(&signature, &trade_info.creator);
trade_info.is_bot = Some(trade_info.user) == bot_wallet;
if let Some(swap_data) = trade_info.metadata.swap_data.as_mut() {
+162 -5
View File
@@ -1,6 +1,20 @@
use crate::streaming::event_parser::DexEvent;
use solana_sdk::pubkey::Pubkey;
#[inline]
fn fill_pubkey(to: &mut Pubkey, from: Pubkey) {
if *to == Pubkey::default() && from != Pubkey::default() {
*to = from;
}
}
#[inline]
fn fill_u64(to: &mut u64, from: u64) {
if *to == 0 && from != 0 {
*to = from;
}
}
pub fn merge(instruction_event: &mut DexEvent, cpi_log_event: DexEvent) {
match instruction_event {
// PumpFun events
@@ -27,11 +41,65 @@ pub fn merge(instruction_event: &mut DexEvent, cpi_log_event: DexEvent) {
e.total_claimed_tokens = cpie.total_claimed_tokens;
e.current_sol_volume = cpie.current_sol_volume;
e.last_update_timestamp = cpie.last_update_timestamp;
e.ix_name = cpie.ix_name.clone();
e.mayhem_mode = cpie.mayhem_mode;
e.cashback_fee_basis_points = cpie.cashback_fee_basis_points;
e.cashback = cpie.cashback;
e.is_cashback_coin = cpie.is_cashback_coin;
if !cpie.ix_name.is_empty() {
e.ix_name = cpie.ix_name.clone();
}
e.mayhem_mode |= cpie.mayhem_mode;
fill_u64(&mut e.cashback_fee_basis_points, cpie.cashback_fee_basis_points);
fill_u64(&mut e.cashback, cpie.cashback);
fill_u64(&mut e.buyback_fee_basis_points, cpie.buyback_fee_basis_points);
fill_u64(&mut e.buyback_fee, cpie.buyback_fee);
if e.shareholders.is_empty() && !cpie.shareholders.is_empty() {
e.shareholders = cpie.shareholders;
}
fill_pubkey(&mut e.quote_mint, cpie.quote_mint);
fill_u64(&mut e.quote_amount, cpie.quote_amount);
fill_u64(&mut e.virtual_quote_reserves, cpie.virtual_quote_reserves);
fill_u64(&mut e.real_quote_reserves, cpie.real_quote_reserves);
e.is_cashback_coin |= cpie.is_cashback_coin;
e.is_created_buy |= cpie.is_created_buy;
fill_u64(&mut e.amount, cpie.amount);
fill_u64(&mut e.max_sol_cost, cpie.max_sol_cost);
fill_u64(&mut e.min_sol_output, cpie.min_sol_output);
fill_u64(&mut e.spendable_sol_in, cpie.spendable_sol_in);
fill_u64(&mut e.spendable_quote_in, cpie.spendable_quote_in);
fill_u64(&mut e.min_tokens_out, cpie.min_tokens_out);
fill_pubkey(&mut e.global, cpie.global);
fill_pubkey(&mut e.bonding_curve, cpie.bonding_curve);
fill_pubkey(&mut e.bonding_curve_v2, cpie.bonding_curve_v2);
fill_pubkey(&mut e.associated_bonding_curve, cpie.associated_bonding_curve);
fill_pubkey(&mut e.associated_user, cpie.associated_user);
fill_pubkey(&mut e.system_program, cpie.system_program);
fill_pubkey(&mut e.token_program, cpie.token_program);
fill_pubkey(&mut e.quote_token_program, cpie.quote_token_program);
fill_pubkey(&mut e.associated_token_program, cpie.associated_token_program);
fill_pubkey(&mut e.creator_vault, cpie.creator_vault);
fill_pubkey(
&mut e.associated_quote_fee_recipient,
cpie.associated_quote_fee_recipient,
);
fill_pubkey(&mut e.buyback_fee_recipient, cpie.buyback_fee_recipient);
fill_pubkey(
&mut e.associated_quote_buyback_fee_recipient,
cpie.associated_quote_buyback_fee_recipient,
);
fill_pubkey(
&mut e.associated_quote_bonding_curve,
cpie.associated_quote_bonding_curve,
);
fill_pubkey(&mut e.associated_quote_user, cpie.associated_quote_user);
fill_pubkey(&mut e.associated_creator_vault, cpie.associated_creator_vault);
fill_pubkey(&mut e.sharing_config, cpie.sharing_config);
fill_pubkey(&mut e.event_authority, cpie.event_authority);
fill_pubkey(&mut e.program, cpie.program);
fill_pubkey(&mut e.global_volume_accumulator, cpie.global_volume_accumulator);
fill_pubkey(&mut e.user_volume_accumulator, cpie.user_volume_accumulator);
fill_pubkey(
&mut e.associated_user_volume_accumulator,
cpie.associated_user_volume_accumulator,
);
fill_pubkey(&mut e.fee_config, cpie.fee_config);
fill_pubkey(&mut e.fee_program, cpie.fee_program);
if cpie.account.is_some() {
e.account = cpie.account;
}
@@ -206,6 +274,20 @@ pub fn merge(instruction_event: &mut DexEvent, cpi_log_event: DexEvent) {
e.cashback_fee_basis_points = cpie.cashback_fee_basis_points;
e.cashback = cpie.cashback;
e.is_pump_pool = cpie.is_pump_pool;
fill_pubkey(&mut e.base_mint, cpie.base_mint);
fill_pubkey(&mut e.quote_mint, cpie.quote_mint);
fill_pubkey(&mut e.pool_base_token_account, cpie.pool_base_token_account);
fill_pubkey(&mut e.pool_quote_token_account, cpie.pool_quote_token_account);
fill_pubkey(&mut e.coin_creator_vault_ata, cpie.coin_creator_vault_ata);
fill_pubkey(&mut e.coin_creator_vault_authority, cpie.coin_creator_vault_authority);
fill_pubkey(&mut e.base_token_program, cpie.base_token_program);
fill_pubkey(&mut e.quote_token_program, cpie.quote_token_program);
fill_pubkey(&mut e.pool_v2, cpie.pool_v2);
fill_pubkey(&mut e.fee_recipient, cpie.fee_recipient);
fill_pubkey(
&mut e.fee_recipient_quote_token_account,
cpie.fee_recipient_quote_token_account,
);
}
_ => {}
},
@@ -237,6 +319,20 @@ pub fn merge(instruction_event: &mut DexEvent, cpi_log_event: DexEvent) {
e.cashback_fee_basis_points = cpie.cashback_fee_basis_points;
e.cashback = cpie.cashback;
e.is_pump_pool = cpie.is_pump_pool;
fill_pubkey(&mut e.base_mint, cpie.base_mint);
fill_pubkey(&mut e.quote_mint, cpie.quote_mint);
fill_pubkey(&mut e.pool_base_token_account, cpie.pool_base_token_account);
fill_pubkey(&mut e.pool_quote_token_account, cpie.pool_quote_token_account);
fill_pubkey(&mut e.coin_creator_vault_ata, cpie.coin_creator_vault_ata);
fill_pubkey(&mut e.coin_creator_vault_authority, cpie.coin_creator_vault_authority);
fill_pubkey(&mut e.base_token_program, cpie.base_token_program);
fill_pubkey(&mut e.quote_token_program, cpie.quote_token_program);
fill_pubkey(&mut e.pool_v2, cpie.pool_v2);
fill_pubkey(&mut e.fee_recipient, cpie.fee_recipient);
fill_pubkey(
&mut e.fee_recipient_quote_token_account,
cpie.fee_recipient_quote_token_account,
);
}
_ => {}
},
@@ -684,3 +780,64 @@ pub fn merge(instruction_event: &mut DexEvent, cpi_log_event: DexEvent) {
_ => {}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::streaming::event_parser::protocols::pumpfun::events::{
PumpFeesShareholder, PumpFunTradeEvent,
};
#[test]
fn pumpfun_merge_keeps_instruction_context_and_copies_latest_trade_tail() {
let quote_mint = Pubkey::new_unique();
let associated_quote_user = Pubkey::new_unique();
let shareholder = Pubkey::new_unique();
let mut instruction_event = DexEvent::PumpFunTradeEvent(PumpFunTradeEvent {
ix_name: "buy_exact_quote_in_v2".to_string(),
quote_mint,
spendable_quote_in: 500,
min_tokens_out: 600,
associated_quote_user,
..Default::default()
});
let cpi_log_event = DexEvent::PumpFunTradeEvent(PumpFunTradeEvent {
sol_amount: 500,
token_amount: 600,
is_buy: true,
buyback_fee_basis_points: 30,
buyback_fee: 40,
shareholders: vec![PumpFeesShareholder { address: shareholder, share_bps: 250 }],
quote_amount: 500,
virtual_quote_reserves: 700,
real_quote_reserves: 800,
is_created_buy: true,
..Default::default()
});
merge(&mut instruction_event, cpi_log_event);
match instruction_event {
DexEvent::PumpFunTradeEvent(t) => {
assert_eq!(t.sol_amount, 500);
assert_eq!(t.token_amount, 600);
assert_eq!(t.ix_name, "buy_exact_quote_in_v2");
assert_eq!(t.quote_mint, quote_mint);
assert_eq!(t.spendable_quote_in, 500);
assert_eq!(t.min_tokens_out, 600);
assert_eq!(t.associated_quote_user, associated_quote_user);
assert_eq!(t.buyback_fee_basis_points, 30);
assert_eq!(t.buyback_fee, 40);
assert_eq!(t.shareholders.len(), 1);
assert_eq!(t.shareholders[0].address, shareholder);
assert_eq!(t.quote_amount, 500);
assert_eq!(t.virtual_quote_reserves, 700);
assert_eq!(t.real_quote_reserves, 800);
assert!(t.is_created_buy);
}
_ => panic!("expected PumpFunTradeEvent"),
}
}
}
@@ -143,6 +143,8 @@ pub struct PumpFunTradeEvent {
#[borsh(skip)]
pub bonding_curve: Pubkey,
#[borsh(skip)]
pub bonding_curve_v2: Pubkey,
#[borsh(skip)]
pub associated_bonding_curve: Pubkey,
#[borsh(skip)]
pub associated_user: Pubkey,
@@ -179,9 +181,51 @@ pub struct PumpFunTradeEvent {
pub cashback_fee_basis_points: u64,
#[borsh(skip)]
pub cashback: u64,
#[borsh(skip)]
pub buyback_fee_basis_points: u64,
#[borsh(skip)]
pub buyback_fee: u64,
#[borsh(skip)]
pub shareholders: Vec<PumpFeesShareholder>,
#[borsh(skip)]
pub quote_mint: Pubkey,
#[borsh(skip)]
pub quote_amount: u64,
#[borsh(skip)]
pub virtual_quote_reserves: u64,
#[borsh(skip)]
pub real_quote_reserves: u64,
/// Whether this is a cashback coin (cashback_fee_basis_points > 0)
#[borsh(skip)]
pub is_cashback_coin: bool,
#[borsh(skip)]
pub is_created_buy: bool,
#[borsh(skip)]
pub spendable_sol_in: u64,
#[borsh(skip)]
pub spendable_quote_in: u64,
#[borsh(skip)]
pub min_tokens_out: u64,
#[borsh(skip)]
pub quote_token_program: Pubkey,
#[borsh(skip)]
pub associated_token_program: Pubkey,
#[borsh(skip)]
pub associated_quote_fee_recipient: Pubkey,
#[borsh(skip)]
pub buyback_fee_recipient: Pubkey,
#[borsh(skip)]
pub associated_quote_buyback_fee_recipient: Pubkey,
#[borsh(skip)]
pub associated_quote_bonding_curve: Pubkey,
#[borsh(skip)]
pub associated_quote_user: Pubkey,
#[borsh(skip)]
pub associated_creator_vault: Pubkey,
#[borsh(skip)]
pub sharing_config: Pubkey,
#[borsh(skip)]
pub associated_user_volume_accumulator: Pubkey,
}
/// Borsh byte length of TradeEvent fixed fields (IDL order; excludes ix_name and following variable part).
@@ -246,7 +290,7 @@ pub struct PumpFunMigrateEvent {
// ---------- pump-fees IDL: `idls/pump_fees.json` (Program `pfeeUx...`) ----------
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
pub struct PumpFeesShareholder {
pub address: Pubkey,
pub share_bps: u16,
@@ -62,6 +62,12 @@ pub struct PumpSwapBuyEvent {
pub base_token_program: Pubkey,
#[borsh(skip)]
pub quote_token_program: Pubkey,
#[borsh(skip)]
pub pool_v2: Pubkey,
#[borsh(skip)]
pub fee_recipient: Pubkey,
#[borsh(skip)]
pub fee_recipient_quote_token_account: Pubkey,
}
/// Minimum bytes through `last_update_timestamp` (Anchor/Borsh layout, bool = 1 byte).
@@ -117,6 +123,12 @@ pub struct PumpSwapSellEvent {
pub base_token_program: Pubkey,
#[borsh(skip)]
pub quote_token_program: Pubkey,
#[borsh(skip)]
pub pool_v2: Pubkey,
#[borsh(skip)]
pub fee_recipient: Pubkey,
#[borsh(skip)]
pub fee_recipient_quote_token_account: Pubkey,
}
pub const PUMP_SWAP_SELL_EVENT_LOG_MIN: usize = 352;
+37
View File
@@ -65,6 +65,24 @@ mod tests {
t.amount = 200;
t.max_sol_cost = 150;
t.min_sol_output = 0;
t.spendable_sol_in = 11;
t.spendable_quote_in = 12;
t.min_tokens_out = 13;
t.is_created_buy = true;
t.global = Pubkey::new_unique();
t.bonding_curve_v2 = Pubkey::new_unique();
t.associated_user = Pubkey::new_unique();
t.system_program = Pubkey::new_unique();
t.event_authority = Pubkey::new_unique();
t.program = Pubkey::new_unique();
t.global_volume_accumulator = Pubkey::new_unique();
t.user_volume_accumulator = Pubkey::new_unique();
t.fee_config = Pubkey::new_unique();
t.fee_program = Pubkey::new_unique();
t.quote_mint = Pubkey::new_unique();
t.quote_amount = 14;
t.virtual_quote_reserves = 15;
t.real_quote_reserves = 16;
t.is_buy = true;
let ev = convert_parser_event(PbDexEvent::PumpFunTrade(t), None, 999).expect("convert");
@@ -77,6 +95,25 @@ mod tests {
assert_eq!(st.amount, 200);
assert_eq!(st.max_sol_cost, 150);
assert_eq!(st.min_sol_output, 0);
assert_eq!(st.spendable_sol_in, 11);
assert_eq!(st.spendable_quote_in, 12);
assert_eq!(st.min_tokens_out, 13);
assert!(st.is_created_buy);
assert!(st.is_dev_create_token_trade);
assert_ne!(st.global, Pubkey::default());
assert_ne!(st.bonding_curve_v2, Pubkey::default());
assert_ne!(st.associated_user, Pubkey::default());
assert_ne!(st.system_program, Pubkey::default());
assert_ne!(st.event_authority, Pubkey::default());
assert_ne!(st.program, Pubkey::default());
assert_ne!(st.global_volume_accumulator, Pubkey::default());
assert_ne!(st.user_volume_accumulator, Pubkey::default());
assert_ne!(st.fee_config, Pubkey::default());
assert_ne!(st.fee_program, Pubkey::default());
assert_ne!(st.quote_mint, Pubkey::default());
assert_eq!(st.quote_amount, 14);
assert_eq!(st.virtual_quote_reserves, 15);
assert_eq!(st.real_quote_reserves, 16);
assert!(st.is_buy);
}
_ => panic!("expected PumpFunTradeEvent"),
@@ -379,6 +379,9 @@ pub(crate) fn pumpswap_buy_full_from_parser(
coin_creator_vault_authority: b.coin_creator_vault_authority,
base_token_program: b.base_token_program,
quote_token_program: b.quote_token_program,
pool_v2: b.pool_v2,
fee_recipient: b.fee_recipient,
fee_recipient_quote_token_account: b.fee_recipient_quote_token_account,
}
}
@@ -422,6 +425,9 @@ pub(crate) fn pumpswap_sell_full_from_parser(
coin_creator_vault_authority: s.coin_creator_vault_authority,
base_token_program: s.base_token_program,
quote_token_program: s.quote_token_program,
pool_v2: s.pool_v2,
fee_recipient: s.fee_recipient,
fee_recipient_quote_token_account: s.fee_recipient_quote_token_account,
}
}
@@ -546,19 +552,51 @@ pub(crate) fn pumpfun_trade_from_parser_with_event_type(
total_claimed_tokens: t.total_claimed_tokens,
current_sol_volume: t.current_sol_volume,
last_update_timestamp: t.last_update_timestamp,
global: t.global,
bonding_curve: t.bonding_curve,
bonding_curve_v2: t.bonding_curve_v2,
associated_bonding_curve: t.associated_bonding_curve,
associated_user: t.associated_user,
system_program: t.system_program,
token_program: t.token_program,
quote_token_program: t.quote_token_program,
associated_token_program: t.associated_token_program,
creator_vault: t.creator_vault,
associated_quote_fee_recipient: t.associated_quote_fee_recipient,
buyback_fee_recipient: t.buyback_fee_recipient,
associated_quote_buyback_fee_recipient: t.associated_quote_buyback_fee_recipient,
associated_quote_bonding_curve: t.associated_quote_bonding_curve,
associated_quote_user: t.associated_quote_user,
associated_creator_vault: t.associated_creator_vault,
sharing_config: t.sharing_config,
event_authority: t.event_authority,
program: t.program,
global_volume_accumulator: t.global_volume_accumulator,
user_volume_accumulator: t.user_volume_accumulator,
associated_user_volume_accumulator: t.associated_user_volume_accumulator,
fee_config: t.fee_config,
fee_program: t.fee_program,
account: t.account,
ix_name: t.ix_name,
mayhem_mode: t.mayhem_mode,
cashback_fee_basis_points: t.cashback_fee_basis_points,
cashback: t.cashback,
buyback_fee_basis_points: t.buyback_fee_basis_points,
buyback_fee: t.buyback_fee,
shareholders: t.shareholders.into_iter().map(pump_fees_shareholder_from_parser).collect(),
quote_mint: t.quote_mint,
quote_amount: t.quote_amount,
virtual_quote_reserves: t.virtual_quote_reserves,
real_quote_reserves: t.real_quote_reserves,
is_cashback_coin: t.is_cashback_coin,
is_created_buy: t.is_created_buy,
is_dev_create_token_trade: t.is_created_buy,
amount: t.amount,
max_sol_cost: t.max_sol_cost,
min_sol_output: t.min_sol_output,
spendable_sol_in: t.spendable_sol_in,
spendable_quote_in: t.spendable_quote_in,
min_tokens_out: t.min_tokens_out,
..Default::default()
};
DexEvent::PumpFunTradeEvent(st)