mirror of
https://github.com/0xfnzero/solana-streamer.git
synced 2026-08-08 14:47:45 +00:00
feat: support Pump.fun V2 (Mayhem Mode) and update protocol structures for Nov 11 breaking changes
This commit is contained in:
@@ -25,7 +25,7 @@ pub fn merge(instruction_event: &mut DexEvent, cpi_log_event: DexEvent) {
|
||||
_ => {}
|
||||
},
|
||||
DexEvent::PumpFunCreateTokenEvent(e) => match cpi_log_event {
|
||||
DexEvent::PumpFunCreateTokenEvent(cpie) => {
|
||||
DexEvent::PumpFunCreateV2TokenEvent(cpie) => {
|
||||
e.mint = cpie.mint;
|
||||
e.bonding_curve = cpie.bonding_curve;
|
||||
e.user = cpie.user;
|
||||
@@ -35,6 +35,8 @@ pub fn merge(instruction_event: &mut DexEvent, cpi_log_event: DexEvent) {
|
||||
e.virtual_sol_reserves = cpie.virtual_sol_reserves;
|
||||
e.real_token_reserves = cpie.real_token_reserves;
|
||||
e.token_total_supply = cpie.token_total_supply;
|
||||
e.token_program = cpie.token_program;
|
||||
e.is_mayhem_mode = cpie.is_mayhem_mode;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
|
||||
@@ -22,20 +22,15 @@ pub struct PumpFunCreateTokenEvent {
|
||||
pub real_token_reserves: u64,
|
||||
pub token_total_supply: u64,
|
||||
#[borsh(skip)]
|
||||
pub token_program: Pubkey,
|
||||
#[borsh(skip)]
|
||||
pub is_mayhem_mode: bool,
|
||||
#[borsh(skip)]
|
||||
pub mint_authority: Pubkey,
|
||||
#[borsh(skip)]
|
||||
pub associated_bonding_curve: Pubkey,
|
||||
}
|
||||
|
||||
pub const PUMPFUN_CREATE_TOKEN_EVENT_LOG_SIZE: usize = 257;
|
||||
|
||||
pub fn pumpfun_create_token_event_log_decode(data: &[u8]) -> Option<PumpFunCreateTokenEvent> {
|
||||
if data.len() < PUMPFUN_CREATE_TOKEN_EVENT_LOG_SIZE {
|
||||
return None;
|
||||
}
|
||||
borsh::from_slice::<PumpFunCreateTokenEvent>(&data[..PUMPFUN_CREATE_TOKEN_EVENT_LOG_SIZE]).ok()
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
|
||||
pub struct PumpFunCreateV2TokenEvent {
|
||||
#[borsh(skip)]
|
||||
@@ -60,13 +55,127 @@ pub struct PumpFunCreateV2TokenEvent {
|
||||
pub associated_bonding_curve: Pubkey,
|
||||
}
|
||||
|
||||
pub const PUMPFUN_CREATE_V2_TOKEN_EVENT_LOG_SIZE: usize = 257 + 32 + 1;
|
||||
|
||||
pub fn pumpfun_create_v2_token_event_log_decode(data: &[u8]) -> Option<PumpFunCreateV2TokenEvent> {
|
||||
if data.len() < PUMPFUN_CREATE_V2_TOKEN_EVENT_LOG_SIZE {
|
||||
let mut offset = 0;
|
||||
|
||||
// 解析 name 字符串: [长度(4字节 u32)][字符串内容]
|
||||
if data.len() < offset + 4 {
|
||||
return None;
|
||||
}
|
||||
borsh::from_slice::<PumpFunCreateV2TokenEvent>(&data[..PUMPFUN_CREATE_V2_TOKEN_EVENT_LOG_SIZE]).ok()
|
||||
let name_len = u32::from_le_bytes(data[offset..offset + 4].try_into().ok()?) as usize;
|
||||
offset += 4;
|
||||
if data.len() < offset + name_len {
|
||||
return None;
|
||||
}
|
||||
let name = String::from_utf8(data[offset..offset + name_len].to_vec()).ok()?;
|
||||
offset += name_len;
|
||||
|
||||
// 解析 symbol 字符串
|
||||
if data.len() < offset + 4 {
|
||||
return None;
|
||||
}
|
||||
let symbol_len = u32::from_le_bytes(data[offset..offset + 4].try_into().ok()?) as usize;
|
||||
offset += 4;
|
||||
if data.len() < offset + symbol_len {
|
||||
return None;
|
||||
}
|
||||
let symbol = String::from_utf8(data[offset..offset + symbol_len].to_vec()).ok()?;
|
||||
offset += symbol_len;
|
||||
|
||||
// 解析 uri 字符串
|
||||
if data.len() < offset + 4 {
|
||||
return None;
|
||||
}
|
||||
let uri_len = u32::from_le_bytes(data[offset..offset + 4].try_into().ok()?) as usize;
|
||||
offset += 4;
|
||||
if data.len() < offset + uri_len {
|
||||
return None;
|
||||
}
|
||||
let uri = String::from_utf8(data[offset..offset + uri_len].to_vec()).ok()?;
|
||||
offset += uri_len;
|
||||
|
||||
// 解析 Pubkey 字段 (每个32字节)
|
||||
if data.len() < offset + 32 {
|
||||
return None;
|
||||
}
|
||||
let mint = Pubkey::new_from_array(data[offset..offset + 32].try_into().ok()?);
|
||||
offset += 32;
|
||||
|
||||
if data.len() < offset + 32 {
|
||||
return None;
|
||||
}
|
||||
let bonding_curve = Pubkey::new_from_array(data[offset..offset + 32].try_into().ok()?);
|
||||
offset += 32;
|
||||
|
||||
if data.len() < offset + 32 {
|
||||
return None;
|
||||
}
|
||||
let user = Pubkey::new_from_array(data[offset..offset + 32].try_into().ok()?);
|
||||
offset += 32;
|
||||
|
||||
if data.len() < offset + 32 {
|
||||
return None;
|
||||
}
|
||||
let creator = Pubkey::new_from_array(data[offset..offset + 32].try_into().ok()?);
|
||||
offset += 32;
|
||||
|
||||
// 解析数值字段
|
||||
if data.len() < offset + 8 {
|
||||
return None;
|
||||
}
|
||||
let timestamp = i64::from_le_bytes(data[offset..offset + 8].try_into().ok()?);
|
||||
offset += 8;
|
||||
|
||||
if data.len() < offset + 8 {
|
||||
return None;
|
||||
}
|
||||
let virtual_token_reserves = u64::from_le_bytes(data[offset..offset + 8].try_into().ok()?);
|
||||
offset += 8;
|
||||
|
||||
if data.len() < offset + 8 {
|
||||
return None;
|
||||
}
|
||||
let virtual_sol_reserves = u64::from_le_bytes(data[offset..offset + 8].try_into().ok()?);
|
||||
offset += 8;
|
||||
|
||||
if data.len() < offset + 8 {
|
||||
return None;
|
||||
}
|
||||
let real_token_reserves = u64::from_le_bytes(data[offset..offset + 8].try_into().ok()?);
|
||||
offset += 8;
|
||||
|
||||
if data.len() < offset + 8 {
|
||||
return None;
|
||||
}
|
||||
let token_total_supply = u64::from_le_bytes(data[offset..offset + 8].try_into().ok()?);
|
||||
offset += 8;
|
||||
|
||||
// 如果数据长度足够,解析 V2 版本的额外字段: token_program (32字节) + is_mayhem_mode (1字节)
|
||||
let (token_program, is_mayhem_mode) = if data.len() >= offset + 33 {
|
||||
let token_program = Pubkey::new_from_array(data[offset..offset + 32].try_into().ok()?);
|
||||
let is_mayhem_mode = data[offset + 32] == 1;
|
||||
(token_program, is_mayhem_mode)
|
||||
} else {
|
||||
(Pubkey::default(), false)
|
||||
};
|
||||
|
||||
Some(PumpFunCreateV2TokenEvent {
|
||||
name,
|
||||
symbol,
|
||||
uri,
|
||||
mint,
|
||||
bonding_curve,
|
||||
user,
|
||||
creator,
|
||||
timestamp,
|
||||
virtual_token_reserves,
|
||||
virtual_sol_reserves,
|
||||
real_token_reserves,
|
||||
token_total_supply,
|
||||
token_program,
|
||||
is_mayhem_mode,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use crate::streaming::event_parser::{
|
||||
common::{EventMetadata, EventType},
|
||||
protocols::pumpfun::{
|
||||
discriminators, pumpfun_create_token_event_log_decode, pumpfun_create_v2_token_event_log_decode,
|
||||
pumpfun_migrate_event_log_decode, pumpfun_trade_event_log_decode, PumpFunCreateTokenEvent,
|
||||
PumpFunCreateV2TokenEvent, PumpFunMigrateEvent, PumpFunTradeEvent,
|
||||
discriminators, pumpfun_create_v2_token_event_log_decode, pumpfun_migrate_event_log_decode,
|
||||
pumpfun_trade_event_log_decode, PumpFunCreateTokenEvent, PumpFunCreateV2TokenEvent,
|
||||
PumpFunMigrateEvent, PumpFunTradeEvent,
|
||||
},
|
||||
DexEvent,
|
||||
};
|
||||
@@ -23,21 +23,13 @@ pub fn parse_pumpfun_instruction_data(
|
||||
metadata: EventMetadata,
|
||||
) -> Option<DexEvent> {
|
||||
match discriminator {
|
||||
discriminators::CREATE_TOKEN_IX => {
|
||||
parse_create_token_instruction(data, accounts, metadata)
|
||||
}
|
||||
discriminators::CREATE_TOKEN_IX => parse_create_token_instruction(data, accounts, metadata),
|
||||
discriminators::CREATE_V2_TOKEN_IX => {
|
||||
parse_create_v2_token_instruction(data, accounts, metadata)
|
||||
}
|
||||
discriminators::BUY_IX => {
|
||||
parse_buy_instruction(data, accounts, metadata)
|
||||
}
|
||||
discriminators::SELL_IX => {
|
||||
parse_sell_instruction(data, accounts, metadata)
|
||||
}
|
||||
discriminators::MIGRATE_IX => {
|
||||
parse_migrate_instruction(data, accounts, metadata)
|
||||
}
|
||||
discriminators::BUY_IX => parse_buy_instruction(data, accounts, metadata),
|
||||
discriminators::SELL_IX => parse_sell_instruction(data, accounts, metadata),
|
||||
discriminators::MIGRATE_IX => parse_migrate_instruction(data, accounts, metadata),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -51,12 +43,8 @@ pub fn parse_pumpfun_inner_instruction_data(
|
||||
metadata: EventMetadata,
|
||||
) -> Option<DexEvent> {
|
||||
match discriminator {
|
||||
discriminators::CREATE_TOKEN_EVENT => {
|
||||
parse_create_token_inner_instruction(data, metadata)
|
||||
}
|
||||
discriminators::TRADE_EVENT => {
|
||||
parse_trade_inner_instruction(data, metadata)
|
||||
}
|
||||
discriminators::CREATE_TOKEN_EVENT => parse_create_token_inner_instruction(data, metadata),
|
||||
discriminators::TRADE_EVENT => parse_trade_inner_instruction(data, metadata),
|
||||
discriminators::COMPLETE_PUMP_AMM_MIGRATION_EVENT => {
|
||||
parse_migrate_inner_instruction(data, metadata)
|
||||
}
|
||||
@@ -64,7 +52,6 @@ pub fn parse_pumpfun_inner_instruction_data(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 解析 PumpFun 账户数据
|
||||
///
|
||||
/// 根据判别器路由到具体的账户解析函数
|
||||
@@ -75,16 +62,19 @@ pub fn parse_pumpfun_account_data(
|
||||
) -> Option<crate::streaming::event_parser::DexEvent> {
|
||||
match discriminator {
|
||||
discriminators::BONDING_CURVE_ACCOUNT => {
|
||||
crate::streaming::event_parser::protocols::pumpfun::types::bonding_curve_parser(account, metadata)
|
||||
crate::streaming::event_parser::protocols::pumpfun::types::bonding_curve_parser(
|
||||
account, metadata,
|
||||
)
|
||||
}
|
||||
discriminators::GLOBAL_ACCOUNT => {
|
||||
crate::streaming::event_parser::protocols::pumpfun::types::global_parser(account, metadata)
|
||||
crate::streaming::event_parser::protocols::pumpfun::types::global_parser(
|
||||
account, metadata,
|
||||
)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 解析迁移事件
|
||||
fn parse_migrate_inner_instruction(data: &[u8], mut metadata: EventMetadata) -> Option<DexEvent> {
|
||||
metadata.event_type = EventType::PumpFunMigrate;
|
||||
@@ -101,8 +91,8 @@ fn parse_create_token_inner_instruction(
|
||||
mut metadata: EventMetadata,
|
||||
) -> Option<DexEvent> {
|
||||
metadata.event_type = EventType::PumpFunCreateToken;
|
||||
if let Some(event) = pumpfun_create_token_event_log_decode(data) {
|
||||
Some(DexEvent::PumpFunCreateTokenEvent(PumpFunCreateTokenEvent { metadata, ..event }))
|
||||
if let Some(event) = pumpfun_create_v2_token_event_log_decode(data) {
|
||||
Some(DexEvent::PumpFunCreateV2TokenEvent(PumpFunCreateV2TokenEvent { metadata, ..event }))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user