use super::program_ids;
use super::utils::*;
use crate::core::events::*;
use solana_sdk::{pubkey::Pubkey, signature::Signature};
pub mod discriminators {
pub const BUY: [u8; 8] = [102, 6, 61, 18, 1, 218, 235, 234];
pub const SELL: [u8; 8] = [51, 230, 133, 164, 1, 127, 131, 173];
pub const CREATE: [u8; 8] = [24, 30, 200, 40, 5, 28, 7, 119];
pub const CREATE_V2: [u8; 8] = [214, 144, 76, 236, 95, 139, 49, 180];
pub const BUY_EXACT_SOL_IN: [u8; 8] = [56, 252, 116, 8, 158, 223, 205, 95];
pub const MIGRATE_EVENT_LOG: [u8; 8] = [189, 233, 93, 185, 92, 148, 234, 148];
}
pub const PROGRAM_ID_PUBKEY: Pubkey = program_ids::PUMPFUN_PROGRAM_ID;
pub fn parse_instruction(
instruction_data: &[u8],
accounts: &[Pubkey],
signature: Signature,
slot: u64,
tx_index: u64,
block_time_us: Option<i64>,
grpc_recv_us: i64,
) -> Option<DexEvent> {
if instruction_data.len() < 8 {
return None;
}
let outer_disc: [u8; 8] = instruction_data[0..8].try_into().ok()?;
let data = &instruction_data[8..];
if outer_disc == discriminators::CREATE_V2 {
return parse_create_v2_instruction(
data,
accounts,
signature,
slot,
tx_index,
block_time_us,
grpc_recv_us,
);
}
if outer_disc == discriminators::CREATE {
return parse_create_instruction(
data,
accounts,
signature,
slot,
tx_index,
block_time_us,
grpc_recv_us,
);
}
if instruction_data.len() >= 16 {
let cpi_disc: [u8; 8] = instruction_data[8..16].try_into().ok()?;
if cpi_disc == discriminators::MIGRATE_EVENT_LOG {
return parse_migrate_log_instruction(
&instruction_data[16..],
accounts,
signature,
slot,
tx_index,
block_time_us,
grpc_recv_us,
);
}
}
None
}
#[allow(dead_code)]
fn parse_buy_instruction(
data: &[u8],
accounts: &[Pubkey],
signature: Signature,
slot: u64,
tx_index: u64,
block_time_us: Option<i64>,
grpc_recv_us: i64,
) -> Option<DexEvent> {
if accounts.len() < 7 {
return None;
}
let (sol_amount, token_amount) = if data.len() >= 16 {
(read_u64_le(data, 0).unwrap_or(0), read_u64_le(data, 8).unwrap_or(0))
} else {
(0, 0)
};
let mint = get_account(accounts, 2)?;
let metadata =
create_metadata(signature, slot, tx_index, block_time_us.unwrap_or_default(), grpc_recv_us);
Some(DexEvent::PumpFunTrade(PumpFunTradeEvent {
metadata,
mint,
is_buy: true,
bonding_curve: get_account(accounts, 3).unwrap_or_default(),
user: get_account(accounts, 6).unwrap_or_default(),
sol_amount,
token_amount,
fee_recipient: get_account(accounts, 1).unwrap_or_default(),
..Default::default()
}))
}
#[allow(dead_code)]
fn parse_sell_instruction(
data: &[u8],
accounts: &[Pubkey],
signature: Signature,
slot: u64,
tx_index: u64,
block_time_us: Option<i64>,
grpc_recv_us: i64,
) -> Option<DexEvent> {
if accounts.len() < 7 {
return None;
}
let (token_amount, sol_amount) = if data.len() >= 16 {
(read_u64_le(data, 0).unwrap_or(0), read_u64_le(data, 8).unwrap_or(0))
} else {
(0, 0)
};
let mint = get_account(accounts, 2)?;
let metadata =
create_metadata(signature, slot, tx_index, block_time_us.unwrap_or_default(), grpc_recv_us);
Some(DexEvent::PumpFunTrade(PumpFunTradeEvent {
metadata,
mint,
is_buy: false,
bonding_curve: get_account(accounts, 3).unwrap_or_default(),
user: get_account(accounts, 6).unwrap_or_default(),
sol_amount,
token_amount,
fee_recipient: get_account(accounts, 1).unwrap_or_default(),
..Default::default()
}))
}
fn parse_create_instruction(
data: &[u8],
accounts: &[Pubkey],
signature: Signature,
slot: u64,
tx_index: u64,
block_time_us: Option<i64>,
grpc_recv_us: i64,
) -> Option<DexEvent> {
if accounts.len() < 8 {
return None;
}
let mut offset = 0;
let name = if let Some((s, len)) = read_str_unchecked(data, offset) {
offset += len;
s.to_string()
} else {
String::new()
};
let symbol = if let Some((s, len)) = read_str_unchecked(data, offset) {
offset += len;
s.to_string()
} else {
String::new()
};
let uri = if let Some((s, len)) = read_str_unchecked(data, offset) {
offset += len;
s.to_string()
} else {
String::new()
};
if data.len() < offset + 32 + 32 + 32 + 32 {
return None;
}
let mint = read_pubkey(data, offset).unwrap_or_default();
offset += 32;
let bonding_curve = read_pubkey(data, offset).unwrap_or_default();
offset += 32;
let user = read_pubkey(data, offset).unwrap_or_default();
offset += 32;
let creator = read_pubkey(data, offset).unwrap_or_default();
offset += 32;
let metadata =
create_metadata(signature, slot, tx_index, block_time_us.unwrap_or_default(), grpc_recv_us);
Some(DexEvent::PumpFunCreate(PumpFunCreateTokenEvent {
metadata,
name,
symbol,
uri,
mint,
bonding_curve,
user,
creator,
..Default::default()
}))
}
fn parse_create_v2_instruction(
data: &[u8],
accounts: &[Pubkey],
signature: Signature,
slot: u64,
tx_index: u64,
block_time_us: Option<i64>,
grpc_recv_us: i64,
) -> Option<DexEvent> {
const CREATE_V2_MIN_ACCOUNTS: usize = 16;
if accounts.len() < CREATE_V2_MIN_ACCOUNTS {
return None;
}
let acc = &accounts[0..CREATE_V2_MIN_ACCOUNTS];
let mut offset = 0usize;
let name = if let Some((s, len)) = read_str_unchecked(data, offset) {
offset += len;
s.to_string()
} else {
String::new()
};
let symbol = if let Some((s, len)) = read_str_unchecked(data, offset) {
offset += len;
s.to_string()
} else {
String::new()
};
let uri = if let Some((s, len)) = read_str_unchecked(data, offset) {
offset += len;
s.to_string()
} else {
String::new()
};
if data.len() < offset + 32 + 1 {
return None;
}
let creator = read_pubkey(data, offset)?;
offset += 32;
let is_mayhem_mode = read_bool(data, offset)?;
offset += 1;
let is_cashback_enabled = read_bool(data, offset).unwrap_or(false);
let mint = acc[0];
let bonding_curve = acc[2];
let user = acc[5];
let metadata =
create_metadata(signature, slot, tx_index, block_time_us.unwrap_or_default(), grpc_recv_us);
Some(DexEvent::PumpFunCreateV2(PumpFunCreateV2TokenEvent {
metadata,
name,
symbol,
uri,
mint,
bonding_curve,
user,
creator,
mint_authority: acc[1],
associated_bonding_curve: acc[3],
global: acc[4],
system_program: acc[6],
token_program: acc[7],
associated_token_program: acc[8],
mayhem_program_id: acc[9],
global_params: acc[10],
sol_vault: acc[11],
mayhem_state: acc[12],
mayhem_token_vault: acc[13],
event_authority: acc[14],
program: acc[15],
is_mayhem_mode,
is_cashback_enabled,
..Default::default()
}))
}
#[allow(unused_variables)]
fn parse_migrate_log_instruction(
data: &[u8],
accounts: &[Pubkey],
signature: Signature,
slot: u64,
tx_index: u64,
block_time_us: Option<i64>,
rpc_recv_us: i64,
) -> Option<DexEvent> {
let mut offset = 0;
let user = read_pubkey(data, offset)?;
offset += 32;
let mint = read_pubkey(data, offset)?;
offset += 32;
let mint_amount = read_u64_le(data, offset)?;
offset += 8;
let sol_amount = read_u64_le(data, offset)?;
offset += 8;
let pool_migration_fee = read_u64_le(data, offset)?;
offset += 8;
let bonding_curve = read_pubkey(data, offset)?;
offset += 32;
let timestamp = read_u64_le(data, offset)? as i64;
offset += 8;
let pool = read_pubkey(data, offset)?;
let metadata =
create_metadata(signature, slot, tx_index, block_time_us.unwrap_or_default(), rpc_recv_us);
Some(DexEvent::PumpFunMigrate(PumpFunMigrateEvent {
metadata,
user,
mint,
mint_amount,
sol_amount,
pool_migration_fee,
bonding_curve,
timestamp,
pool,
}))
}