1use super::program_ids;
6use super::utils::*;
7use crate::core::events::*;
8use solana_sdk::{pubkey::Pubkey, signature::Signature};
9
10pub mod discriminators {
12 pub const BUY: [u8; 8] = [102, 6, 61, 18, 1, 218, 235, 234];
14 pub const SELL: [u8; 8] = [51, 230, 133, 164, 1, 127, 131, 173];
16 pub const CREATE: [u8; 8] = [24, 30, 200, 40, 5, 28, 7, 119];
18 pub const CREATE_V2: [u8; 8] = [214, 144, 76, 236, 95, 139, 49, 180];
20 pub const BUY_EXACT_SOL_IN: [u8; 8] = [56, 252, 116, 8, 158, 223, 205, 95];
22 pub const MIGRATE_EVENT_LOG: [u8; 8] = [189, 233, 93, 185, 92, 148, 234, 148];
24 pub const MIGRATE_BONDING_CURVE_CREATOR: [u8; 8] = [87, 124, 52, 191, 52, 38, 214, 232];
26 pub const BUY_V2: [u8; 8] = [184, 23, 238, 97, 103, 197, 211, 61];
28 pub const SELL_V2: [u8; 8] = [93, 246, 130, 60, 231, 233, 64, 178];
30 pub const BUY_EXACT_QUOTE_IN_V2: [u8; 8] = [194, 171, 28, 70, 104, 77, 91, 47];
32}
33
34pub const PROGRAM_ID_PUBKEY: Pubkey = program_ids::PUMPFUN_PROGRAM_ID;
36
37pub fn parse_instruction(
42 instruction_data: &[u8],
43 accounts: &[Pubkey],
44 signature: Signature,
45 slot: u64,
46 tx_index: u64,
47 block_time_us: Option<i64>,
48 grpc_recv_us: i64,
49) -> Option<DexEvent> {
50 if instruction_data.len() < 8 {
51 return None;
52 }
53 let outer_disc: [u8; 8] = instruction_data[0..8].try_into().ok()?;
54 let data = &instruction_data[8..];
55
56 if outer_disc == discriminators::CREATE_V2 {
58 return parse_create_v2_instruction(
59 data,
60 accounts,
61 signature,
62 slot,
63 tx_index,
64 block_time_us,
65 grpc_recv_us,
66 );
67 }
68 if outer_disc == discriminators::CREATE {
69 return parse_create_instruction(
70 data,
71 accounts,
72 signature,
73 slot,
74 tx_index,
75 block_time_us,
76 grpc_recv_us,
77 );
78 }
79
80 if instruction_data.len() >= 16 {
82 let cpi_disc: [u8; 8] = instruction_data[8..16].try_into().ok()?;
83 if cpi_disc == discriminators::MIGRATE_EVENT_LOG {
84 return parse_migrate_log_instruction(
85 &instruction_data[16..],
86 accounts,
87 signature,
88 slot,
89 tx_index,
90 block_time_us,
91 grpc_recv_us,
92 );
93 }
94 }
95 None
96}
97
98#[allow(dead_code)]
108fn parse_buy_instruction(
109 data: &[u8],
110 accounts: &[Pubkey],
111 signature: Signature,
112 slot: u64,
113 tx_index: u64,
114 block_time_us: Option<i64>,
115 grpc_recv_us: i64,
116) -> Option<DexEvent> {
117 if accounts.len() < 7 {
118 return None;
119 }
120
121 let (sol_amount, token_amount) = if data.len() >= 16 {
123 (read_u64_le(data, 0).unwrap_or(0), read_u64_le(data, 8).unwrap_or(0))
124 } else {
125 (0, 0)
126 };
127
128 let mint = get_account(accounts, 2)?;
129 let metadata =
130 create_metadata(signature, slot, tx_index, block_time_us.unwrap_or_default(), grpc_recv_us);
131
132 Some(DexEvent::PumpFunTrade(PumpFunTradeEvent {
133 metadata,
134 mint,
135 is_buy: true,
136 bonding_curve: get_account(accounts, 3).unwrap_or_default(),
137 user: get_account(accounts, 6).unwrap_or_default(),
138 sol_amount,
139 token_amount,
140 fee_recipient: get_account(accounts, 1).unwrap_or_default(),
141 ..Default::default()
142 }))
143}
144
145#[allow(dead_code)]
154fn parse_sell_instruction(
155 data: &[u8],
156 accounts: &[Pubkey],
157 signature: Signature,
158 slot: u64,
159 tx_index: u64,
160 block_time_us: Option<i64>,
161 grpc_recv_us: i64,
162) -> Option<DexEvent> {
163 if accounts.len() < 7 {
164 return None;
165 }
166
167 let (token_amount, sol_amount) = if data.len() >= 16 {
169 (read_u64_le(data, 0).unwrap_or(0), read_u64_le(data, 8).unwrap_or(0))
170 } else {
171 (0, 0)
172 };
173
174 let mint = get_account(accounts, 2)?;
175 let metadata =
176 create_metadata(signature, slot, tx_index, block_time_us.unwrap_or_default(), grpc_recv_us);
177
178 Some(DexEvent::PumpFunTrade(PumpFunTradeEvent {
179 metadata,
180 mint,
181 is_buy: false,
182 bonding_curve: get_account(accounts, 3).unwrap_or_default(),
183 user: get_account(accounts, 6).unwrap_or_default(),
184 sol_amount,
185 token_amount,
186 fee_recipient: get_account(accounts, 1).unwrap_or_default(),
187 ..Default::default()
188 }))
189}
190
191fn parse_create_instruction(
197 data: &[u8],
198 accounts: &[Pubkey],
199 signature: Signature,
200 slot: u64,
201 tx_index: u64,
202 block_time_us: Option<i64>,
203 grpc_recv_us: i64,
204) -> Option<DexEvent> {
205 if accounts.len() < 8 {
206 return None;
207 }
208
209 let mut offset = 0;
210
211 let name = if let Some((s, len)) = read_str_unchecked(data, offset) {
214 offset += len;
215 s.to_string()
216 } else {
217 String::new()
218 };
219
220 let symbol = if let Some((s, len)) = read_str_unchecked(data, offset) {
221 offset += len;
222 s.to_string()
223 } else {
224 String::new()
225 };
226
227 let uri = if let Some((s, len)) = read_str_unchecked(data, offset) {
228 offset += len;
229 s.to_string()
230 } else {
231 String::new()
232 };
233
234 if data.len() < offset + 32 + 32 + 32 + 32 {
236 return None;
237 }
238
239 let mint = read_pubkey(data, offset).unwrap_or_default();
240 offset += 32;
241
242 let bonding_curve = read_pubkey(data, offset).unwrap_or_default();
243 offset += 32;
244
245 let user = read_pubkey(data, offset).unwrap_or_default();
246 offset += 32;
247
248 let creator = read_pubkey(data, offset).unwrap_or_default();
249
250 let metadata =
251 create_metadata(signature, slot, tx_index, block_time_us.unwrap_or_default(), grpc_recv_us);
252
253 Some(DexEvent::PumpFunCreate(PumpFunCreateTokenEvent {
254 metadata,
255 name,
256 symbol,
257 uri,
258 mint,
259 bonding_curve,
260 user,
261 creator,
262 ..Default::default()
263 }))
264}
265
266fn parse_create_v2_instruction(
275 data: &[u8],
276 accounts: &[Pubkey],
277 signature: Signature,
278 slot: u64,
279 tx_index: u64,
280 block_time_us: Option<i64>,
281 grpc_recv_us: i64,
282) -> Option<DexEvent> {
283 const CREATE_V2_MIN_ACCOUNTS: usize = 16;
284 if accounts.len() < CREATE_V2_MIN_ACCOUNTS {
285 return None;
286 }
287 let acc = &accounts[0..CREATE_V2_MIN_ACCOUNTS];
288
289 let mut offset = 0usize;
291 let name = if let Some((s, len)) = read_str_unchecked(data, offset) {
292 offset += len;
293 s.to_string()
294 } else {
295 String::new()
296 };
297 let symbol = if let Some((s, len)) = read_str_unchecked(data, offset) {
298 offset += len;
299 s.to_string()
300 } else {
301 String::new()
302 };
303 let uri = if let Some((s, len)) = read_str_unchecked(data, offset) {
304 offset += len;
305 s.to_string()
306 } else {
307 String::new()
308 };
309 if data.len() < offset + 32 + 1 {
310 return None;
311 }
312 let creator = read_pubkey(data, offset)?;
313 offset += 32;
314 let is_mayhem_mode = read_bool(data, offset)?;
315 offset += 1;
316 let is_cashback_enabled = read_option_bool_idl(data, offset).unwrap_or(false);
317
318 let mint = acc[0];
319 let bonding_curve = acc[2];
320 let user = acc[5];
321
322 let metadata =
323 create_metadata(signature, slot, tx_index, block_time_us.unwrap_or_default(), grpc_recv_us);
324
325 Some(DexEvent::PumpFunCreateV2(PumpFunCreateV2TokenEvent {
326 metadata,
327 name,
328 symbol,
329 uri,
330 mint,
331 bonding_curve,
332 user,
333 creator,
334 mint_authority: acc[1],
335 associated_bonding_curve: acc[3],
336 global: acc[4],
337 system_program: acc[6],
338 token_program: acc[7],
339 associated_token_program: acc[8],
340 mayhem_program_id: acc[9],
341 global_params: acc[10],
342 sol_vault: acc[11],
343 mayhem_state: acc[12],
344 mayhem_token_vault: acc[13],
345 event_authority: acc[14],
346 program: acc[15],
347 is_mayhem_mode,
348 is_cashback_enabled,
349 ..Default::default()
350 }))
351}
352
353#[allow(unused_variables)]
355fn parse_migrate_log_instruction(
356 data: &[u8],
357 accounts: &[Pubkey],
358 signature: Signature,
359 slot: u64,
360 tx_index: u64,
361 block_time_us: Option<i64>,
362 rpc_recv_us: i64,
363) -> Option<DexEvent> {
364 let mut offset = 0;
365
366 let user = read_pubkey(data, offset)?;
368 offset += 32;
369
370 let mint = read_pubkey(data, offset)?;
372 offset += 32;
373
374 let mint_amount = read_u64_le(data, offset)?;
376 offset += 8;
377
378 let sol_amount = read_u64_le(data, offset)?;
380 offset += 8;
381
382 let pool_migration_fee = read_u64_le(data, offset)?;
384 offset += 8;
385
386 let bonding_curve = read_pubkey(data, offset)?;
388 offset += 32;
389
390 let timestamp = read_u64_le(data, offset)? as i64;
392 offset += 8;
393
394 let pool = read_pubkey(data, offset)?;
396
397 let metadata =
398 create_metadata(signature, slot, tx_index, block_time_us.unwrap_or_default(), rpc_recv_us);
399
400 Some(DexEvent::PumpFunMigrate(PumpFunMigrateEvent {
401 metadata,
402 user,
403 mint,
404 mint_amount,
405 sol_amount,
406 pool_migration_fee,
407 bonding_curve,
408 timestamp,
409 pool,
410 }))
411}