feat(pump): support compact trades, coin quotes and synthetic migration

This commit is contained in:
0xfnzero
2026-10-08 07:57:20 +08:00
parent edba2123e1
commit 7b95748474
12 changed files with 215 additions and 0 deletions
+9
View File
@@ -652,3 +652,12 @@ MIT License
- [English](README.md)
- [中文](README_CN.md)
## Pump upgrade (October 2026)
Compact Pump v3 and PumpSwap v2 trades use their new 17-account layouts. New typed events cover `PumpFunPostCompleteBuy`, `PumpFunComplete`, `PumpFunSweepBondingCurveFee` and `PumpSwapSweepPoolFee`, with program-scoped log and CPI parsing. Historical SOL CompleteEvent payloads remain supported. Curve/pool retained fees and synthetic counters are exposed; optional historical tails default to zero.
For a synthetic completing buy, retain TradeEvent **and** PostCompleteBuyEvent and aggregate execution amounts within the same invocation. CompleteEvent is the completion notification. For `multi_hop_swap`, retain each venue's trade events; different venues are not merged into one fill. The multi-hop intent decoder exposes the fixed user accounts, input/minimum limits and 5 roles per hop; these limits are not actual executed amounts. Streamer forwards the typed events and account fields through its parser bridge (its re-exported `parser_sdk` also provides the intent decoder).
Reference: [pump-public-docs](https://github.com/pump-fun/pump-public-docs/tree/8cda1fa30ea658b20909d8aedf002047119388d2). Validation uses offline official IDL fixtures; no live trade is sent by the tests.
@@ -251,6 +251,10 @@ fn push_streamer_event_sdk_grpc_types(
St::PumpFeesUpdateFeeConfig => out.push(Sdk::PumpFeesUpdateFeeConfig),
St::PumpFeesUpdateFeeShares => out.push(Sdk::PumpFeesUpdateFeeShares),
St::PumpFeesUpsertFeeTiers => out.push(Sdk::PumpFeesUpsertFeeTiers),
St::PumpFunPostCompleteBuy => out.push(Sdk::PumpFunPostCompleteBuy),
St::PumpFunSweepBondingCurveFee => out.push(Sdk::PumpFunSweepBondingCurveFee),
St::PumpFunComplete => out.push(Sdk::PumpFunComplete),
St::PumpSwapSweepPoolFee => out.push(Sdk::PumpSwapSweepPoolFee),
St::PumpFunMigrateBondingCurveCreator => out.push(Sdk::PumpFunMigrateBondingCurveCreator),
St::PumpSwapBuy => out.push(Sdk::PumpSwapBuy),
St::PumpSwapSell => out.push(Sdk::PumpSwapSell),
@@ -373,6 +377,9 @@ fn push_protocol_sdk_grpc_event_types(protocol: &Protocol, out: &mut Vec<SdkGrpc
Sdk::PumpFunCreateV2,
Sdk::PumpFunMigrate,
Sdk::PumpFunMigrateBondingCurveCreator,
Sdk::PumpFunPostCompleteBuy,
Sdk::PumpFunSweepBondingCurveFee,
Sdk::PumpFunComplete,
// Historical compatibility: streamer PumpFun also included PumpFees.
Sdk::PumpFeesCreateFeeSharingConfig,
Sdk::PumpFeesInitializeFeeConfig,
@@ -400,6 +407,7 @@ fn push_protocol_sdk_grpc_event_types(protocol: &Protocol, out: &mut Vec<SdkGrpc
Sdk::PumpSwapBuy,
Sdk::PumpSwapSell,
Sdk::PumpSwapCreatePool,
Sdk::PumpSwapSweepPoolFee,
Sdk::PumpSwapLiquidityAdded,
Sdk::PumpSwapLiquidityRemoved,
]),
@@ -73,6 +73,11 @@ pub enum ProtocolType {
BorshDeserialize,
)]
pub enum EventType {
PumpFunComplete,
PumpFunPostCompleteBuy,
PumpFunSweepBondingCurveFee,
PumpSwapSweepPoolFee,
// PumpSwap events
#[default]
PumpSwapBuy,
@@ -170,6 +170,10 @@ pub enum DexEvent {
// Appended to preserve existing serialized enum variant indices.
TransactionCostEvent(TransactionCostEvent),
RaydiumCpmmCollectCreatorFeeEvent(RaydiumCpmmCollectCreatorFeeEvent),
PumpFunPostCompleteBuyEvent(PumpFunPostCompleteBuyEvent),
PumpFunSweepBondingCurveFeeEvent(PumpFunSweepBondingCurveFeeEvent),
PumpFunCompleteEvent(PumpFunCompleteEvent),
PumpSwapSweepPoolFeeEvent(PumpSwapSweepPoolFeeEvent),
}
/// Macro to generate metadata accessors for all DexEvent variants
@@ -192,6 +196,10 @@ macro_rules! impl_dex_event_metadata {
}
impl_dex_event_metadata!(
PumpFunPostCompleteBuyEvent,
PumpFunSweepBondingCurveFeeEvent,
PumpFunCompleteEvent,
PumpSwapSweepPoolFeeEvent,
// Bonk events
BonkTradeEvent,
BonkPoolCreateEvent,
@@ -7,6 +7,9 @@ use crate::streaming::event_parser::protocols::pumpfun::types::{BondingCurve, Gl
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
pub struct PumpFunCreateTokenEvent {
#[borsh(skip)]
#[serde(default)]
pub depth: u8,
#[borsh(skip)]
pub metadata: EventMetadata,
pub name: String,
@@ -144,6 +147,9 @@ pub struct PumpFunCreateV2TokenEvent {
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
pub struct PumpFunTradeEvent {
#[borsh(skip)]
#[serde(default)]
pub creator_fee_unclaimed: u64,
#[borsh(skip)]
pub metadata: EventMetadata,
pub mint: Pubkey,
@@ -522,6 +528,7 @@ pub struct PumpFunFeeConfigAccountEvent {
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct PumpFunFeeConfig {
pub exotic_flat_fees: PumpFeesFees,
pub bump: u8,
pub admin: Pubkey,
pub flat_fees: PumpFeesFees,
@@ -623,3 +630,46 @@ pub mod discriminators {
pub const BONDING_CURVE_ACCOUNT: &[u8] = &[23, 183, 248, 55, 96, 216, 172, 96];
pub const GLOBAL_ACCOUNT: &[u8] = &[167, 232, 232, 177, 200, 108, 114, 127];
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct PumpFunPostCompleteBuyEvent {
pub metadata: EventMetadata,
pub user: Pubkey,
pub mint: Pubkey,
pub bonding_curve: Pubkey,
pub quote_mint: Pubkey,
pub timestamp: i64,
pub base_out: u64,
pub quote_in: u64,
pub fee_basis_points: u64,
pub fee: u64,
pub creator_fee_basis_points: u64,
pub creator_fee: u64,
pub buyback_fee: u64,
pub pool_base_reserves_before: u64,
pub pool_quote_reserves_before: u64,
pub pool_base_reserves_after: u64,
pub pool_quote_reserves_after: u64,
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct PumpFunSweepBondingCurveFeeEvent {
pub metadata: EventMetadata,
pub timestamp: i64,
pub mint: Pubkey,
pub bonding_curve: Pubkey,
pub quote_mint: Pubkey,
pub recipient: Pubkey,
pub amount: u64,
pub bucket: u8,
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct PumpFunCompleteEvent {
pub metadata: EventMetadata,
pub user: Pubkey,
pub mint: Pubkey,
pub bonding_curve: Pubkey,
pub timestamp: i64,
pub quote_mint: Pubkey,
}
@@ -4,6 +4,25 @@ use solana_sdk::pubkey::Pubkey;
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
pub struct BondingCurve {
#[borsh(skip)]
#[serde(default)]
pub creator_fee: u64,
#[borsh(skip)]
#[serde(default)]
pub protocol_fees: u64,
#[borsh(skip)]
#[serde(default)]
pub depth: u8,
#[borsh(skip)]
#[serde(default)]
pub initial_virtual_quote_reserves: u64,
#[borsh(skip)]
#[serde(default)]
pub post_complete_base_out: u64,
#[borsh(skip)]
#[serde(default)]
pub post_complete_quote_in: u64,
pub virtual_token_reserves: u64,
pub virtual_quote_reserves: u64,
pub real_token_reserves: u64,
@@ -28,6 +47,22 @@ pub const BONDING_CURVE_SIZE: usize = BONDING_CURVE_CREATOR_FEE_SIZE + 1;
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
pub struct Global {
#[borsh(skip)]
#[serde(default)]
pub creator_fee_configurable: bool,
#[borsh(skip)]
#[serde(default)]
pub max_configurable_creator_fee_bps: u64,
#[borsh(skip)]
#[serde(default)]
pub holder_reward_claim_authority: Pubkey,
#[borsh(skip)]
#[serde(default)]
pub is_holder_reward_enabled: bool,
#[borsh(skip)]
#[serde(default)]
pub max_curve_depth: u8,
pub initialized: bool,
pub authority: Pubkey,
pub fee_recipient: Pubkey,
@@ -8,6 +8,9 @@ use crate::streaming::event_parser::protocols::pumpswap::types::{GlobalConfig, P
/// 买入事件
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
pub struct PumpSwapBuyEvent {
#[borsh(skip)]
#[serde(default)]
pub creator_fee_unclaimed: u64,
#[borsh(skip)]
pub metadata: EventMetadata,
pub timestamp: i64,
@@ -92,6 +95,9 @@ pub const PUMP_SWAP_BUY_EVENT_LOG_SIZE: usize = PUMP_SWAP_BUY_EVENT_LOG_MIN;
/// 卖出事件
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
pub struct PumpSwapSellEvent {
#[borsh(skip)]
#[serde(default)]
pub creator_fee_unclaimed: u64,
#[borsh(skip)]
pub metadata: EventMetadata,
pub timestamp: i64,
@@ -375,3 +381,16 @@ pub mod discriminators {
pub const GLOBAL_CONFIG_ACCOUNT: &[u8] = &[149, 8, 156, 202, 160, 252, 176, 217];
pub const POOL_ACCOUNT: &[u8] = &[241, 154, 109, 4, 17, 177, 109, 188];
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct PumpSwapSweepPoolFeeEvent {
pub metadata: EventMetadata,
pub timestamp: i64,
pub pool: Pubkey,
pub base_mint: Pubkey,
pub quote_mint: Pubkey,
pub recipient: Pubkey,
pub payer: Pubkey,
pub amount: u64,
pub bucket: u8,
}
@@ -21,6 +21,12 @@ pub const GLOBAL_CONFIG_SIZE: usize = 32 + 8 + 8 + 1 + 32 * 8 + 8 + 32 + 32 + 32
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
pub struct Pool {
#[borsh(skip)]
#[serde(default)]
pub protocol_fees: u64,
#[borsh(skip)]
#[serde(default)]
pub creator_fees: u64,
pub pool_bump: u8,
pub index: u16,
pub creator: Pubkey,
@@ -195,6 +195,8 @@ pub(crate) fn pumpswap_global_config_from_pb(
pub(crate) fn pumpswap_pool_from_pb(p: sol_parser_sdk::core::events::PumpSwapPool) -> Pool {
Pool {
creator_fees: p.creator_fees,
protocol_fees: p.protocol_fees,
pool_bump: p.pool_bump,
index: p.index,
creator: p.creator,
@@ -23,6 +23,60 @@ pub(crate) fn convert_parser_event(
recv_wall_us: i64,
) -> Option<DexEvent> {
match ev {
PbDexEvent::PumpFunPostCompleteBuy(e) => {
let meta = adapt_pm(
e.metadata.clone(),
bt,
recv_wall_us,
ProtocolType::PumpFun,
EventType::PumpFunPostCompleteBuy,
pump_program(),
);
Some(DexEvent::PumpFunPostCompleteBuyEvent(crate::streaming::event_parser::protocols::pumpfun::events::PumpFunPostCompleteBuyEvent{metadata:meta,user:e.user,mint:e.mint,bonding_curve:e.bonding_curve,quote_mint:e.quote_mint,timestamp:e.timestamp,base_out:e.base_out,quote_in:e.quote_in,fee_basis_points:e.fee_basis_points,fee:e.fee,creator_fee_basis_points:e.creator_fee_basis_points,creator_fee:e.creator_fee,buyback_fee:e.buyback_fee,pool_base_reserves_before:e.pool_base_reserves_before,pool_quote_reserves_before:e.pool_quote_reserves_before,pool_base_reserves_after:e.pool_base_reserves_after,pool_quote_reserves_after:e.pool_quote_reserves_after,}))
}
PbDexEvent::PumpFunSweepBondingCurveFee(e) => {
let meta = adapt_pm(
e.metadata.clone(),
bt,
recv_wall_us,
ProtocolType::PumpFun,
EventType::PumpFunSweepBondingCurveFee,
pump_program(),
);
Some(DexEvent::PumpFunSweepBondingCurveFeeEvent(crate::streaming::event_parser::protocols::pumpfun::events::PumpFunSweepBondingCurveFeeEvent{metadata:meta,timestamp:e.timestamp,mint:e.mint,bonding_curve:e.bonding_curve,quote_mint:e.quote_mint,recipient:e.recipient,amount:e.amount,bucket:e.bucket,}))
}
PbDexEvent::PumpFunComplete(e) => {
let meta = adapt_pm(
e.metadata.clone(),
bt,
recv_wall_us,
ProtocolType::PumpFun,
EventType::PumpFunComplete,
pump_program(),
);
Some(DexEvent::PumpFunCompleteEvent(
crate::streaming::event_parser::protocols::pumpfun::events::PumpFunCompleteEvent {
metadata: meta,
user: e.user,
mint: e.mint,
bonding_curve: e.bonding_curve,
timestamp: e.timestamp,
quote_mint: e.quote_mint,
},
))
}
PbDexEvent::PumpSwapSweepPoolFee(e) => {
let meta = adapt_pm(
e.metadata.clone(),
bt,
recv_wall_us,
ProtocolType::PumpSwap,
EventType::PumpSwapSweepPoolFee,
pumpswap_program(),
);
Some(DexEvent::PumpSwapSweepPoolFeeEvent(crate::streaming::event_parser::protocols::pumpswap::events::PumpSwapSweepPoolFeeEvent{metadata:meta,timestamp:e.timestamp,pool:e.pool,base_mint:e.base_mint,quote_mint:e.quote_mint,recipient:e.recipient,payer:e.payer,amount:e.amount,bucket:e.bucket,}))
}
PbDexEvent::PumpFunTrade(t) => Some(pumpfun_trade_from_parser_with_event_type(
t,
bt,
+2
View File
@@ -419,6 +419,8 @@ mod tests {
owner: Pubkey::new_unique(),
rent_epoch: 0,
pool: PbPumpSwapPool {
protocol_fees: 0,
creator_fees: 0,
pool_bump: 7,
index: 42,
creator: Pubkey::new_unique(),
@@ -58,6 +58,7 @@ pub(crate) fn pumpfun_create_token_from_parser(
virtual_quote_reserves: c.virtual_quote_reserves,
creator_fee_bps: c.creator_fee_bps,
is_holder_reward: c.is_holder_reward,
depth: c.depth,
ix_name: "create".to_string(),
..Default::default()
}
@@ -356,6 +357,11 @@ pub(crate) fn pumpfun_global_account_from_parser(
buyback_basis_points: e.global.buyback_basis_points,
initial_virtual_quote_reserves: e.global.initial_virtual_quote_reserves,
whitelisted_quote_mints: e.global.whitelisted_quote_mints,
creator_fee_configurable: e.global.creator_fee_configurable,
max_configurable_creator_fee_bps: e.global.max_configurable_creator_fee_bps,
holder_reward_claim_authority: e.global.holder_reward_claim_authority,
is_holder_reward_enabled: e.global.is_holder_reward_enabled,
max_curve_depth: e.global.max_curve_depth,
},
}
}
@@ -372,6 +378,13 @@ pub(crate) fn pumpfun_bonding_curve_account_from_parser(
owner: pump_program(),
rent_epoch: 0,
bonding_curve: BondingCurve {
creator_fee: e.bonding_curve.creator_fee,
protocol_fees: e.bonding_curve.protocol_fees,
depth: e.bonding_curve.depth,
initial_virtual_quote_reserves: e.bonding_curve.initial_virtual_quote_reserves,
post_complete_base_out: e.bonding_curve.post_complete_base_out,
post_complete_quote_in: e.bonding_curve.post_complete_quote_in,
virtual_token_reserves: e.bonding_curve.virtual_token_reserves,
virtual_quote_reserves: e.bonding_curve.virtual_quote_reserves,
real_token_reserves: e.bonding_curve.real_token_reserves,
@@ -401,6 +414,7 @@ pub(crate) fn pumpfun_fee_config_account_from_parser(
owner: pump_program(),
rent_epoch: 0,
fee_config: PumpFunFeeConfig {
exotic_flat_fees: pump_fees_fees_from_parser(e.fee_config.exotic_flat_fees),
bump: e.fee_config.bump,
admin: e.fee_config.admin,
flat_fees: pump_fees_fees_from_parser(e.fee_config.flat_fees),
@@ -533,6 +547,7 @@ pub(crate) fn pumpswap_buy_full_from_parser(
buyback_fee_basis_points: b.buyback_fee_basis_points,
buyback_fee: b.buyback_fee,
virtual_quote_reserves: b.virtual_quote_reserves,
creator_fee_unclaimed: b.creator_fee_unclaimed,
can_boost: b.can_boost,
base_supply: b.base_supply,
holder_rewards_bps: b.holder_rewards_bps,
@@ -586,6 +601,7 @@ pub(crate) fn pumpswap_sell_full_from_parser(
buyback_fee_basis_points: s.buyback_fee_basis_points,
buyback_fee: s.buyback_fee,
virtual_quote_reserves: s.virtual_quote_reserves,
creator_fee_unclaimed: s.creator_fee_unclaimed,
can_boost: s.can_boost,
base_supply: s.base_supply,
holder_rewards_bps: s.holder_rewards_bps,
@@ -757,6 +773,7 @@ pub(crate) fn pumpfun_trade_from_parser_with_event_type(
quote_mint: normalize_pumpfun_quote_mint(t.quote_mint),
quote_amount: t.quote_amount,
virtual_quote_reserves: t.virtual_quote_reserves,
creator_fee_unclaimed: t.creator_fee_unclaimed,
real_quote_reserves: t.real_quote_reserves,
holder_rewards_bps: t.holder_rewards_bps,
holder_rewards: t.holder_rewards,