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] =
26 [87, 124, 52, 191, 52, 38, 214, 232];
27}
28
29pub const PROGRAM_ID_PUBKEY: Pubkey = program_ids::PUMPFUN_PROGRAM_ID;
31
32pub fn parse_instruction(
37 instruction_data: &[u8],
38 accounts: &[Pubkey],
39 signature: Signature,
40 slot: u64,
41 tx_index: u64,
42 block_time_us: Option<i64>,
43 grpc_recv_us: i64,
44) -> Option<DexEvent> {
45 if instruction_data.len() < 8 {
46 return None;
47 }
48 let outer_disc: [u8; 8] = instruction_data[0..8].try_into().ok()?;
49 let data = &instruction_data[8..];
50
51 if outer_disc == discriminators::CREATE_V2 {
53 return parse_create_v2_instruction(
54 data,
55 accounts,
56 signature,
57 slot,
58 tx_index,
59 block_time_us,
60 grpc_recv_us,
61 );
62 }
63 if outer_disc == discriminators::CREATE {
64 return parse_create_instruction(
65 data,
66 accounts,
67 signature,
68 slot,
69 tx_index,
70 block_time_us,
71 grpc_recv_us,
72 );
73 }
74
75 if instruction_data.len() >= 16 {
77 let cpi_disc: [u8; 8] = instruction_data[8..16].try_into().ok()?;
78 if cpi_disc == discriminators::MIGRATE_EVENT_LOG {
79 return parse_migrate_log_instruction(
80 &instruction_data[16..],
81 accounts,
82 signature,
83 slot,
84 tx_index,
85 block_time_us,
86 grpc_recv_us,
87 );
88 }
89 }
90 None
91}
92
93#[allow(dead_code)]
103fn parse_buy_instruction(
104 data: &[u8],
105 accounts: &[Pubkey],
106 signature: Signature,
107 slot: u64,
108 tx_index: u64,
109 block_time_us: Option<i64>,
110 grpc_recv_us: i64,
111) -> Option<DexEvent> {
112 if accounts.len() < 7 {
113 return None;
114 }
115
116 let (sol_amount, token_amount) = if data.len() >= 16 {
118 (read_u64_le(data, 0).unwrap_or(0), read_u64_le(data, 8).unwrap_or(0))
119 } else {
120 (0, 0)
121 };
122
123 let mint = get_account(accounts, 2)?;
124 let metadata =
125 create_metadata(signature, slot, tx_index, block_time_us.unwrap_or_default(), grpc_recv_us);
126
127 Some(DexEvent::PumpFunTrade(PumpFunTradeEvent {
128 metadata,
129 mint,
130 is_buy: true,
131 bonding_curve: get_account(accounts, 3).unwrap_or_default(),
132 user: get_account(accounts, 6).unwrap_or_default(),
133 sol_amount,
134 token_amount,
135 fee_recipient: get_account(accounts, 1).unwrap_or_default(),
136 ..Default::default()
137 }))
138}
139
140#[allow(dead_code)]
149fn parse_sell_instruction(
150 data: &[u8],
151 accounts: &[Pubkey],
152 signature: Signature,
153 slot: u64,
154 tx_index: u64,
155 block_time_us: Option<i64>,
156 grpc_recv_us: i64,
157) -> Option<DexEvent> {
158 if accounts.len() < 7 {
159 return None;
160 }
161
162 let (token_amount, sol_amount) = if data.len() >= 16 {
164 (read_u64_le(data, 0).unwrap_or(0), read_u64_le(data, 8).unwrap_or(0))
165 } else {
166 (0, 0)
167 };
168
169 let mint = get_account(accounts, 2)?;
170 let metadata =
171 create_metadata(signature, slot, tx_index, block_time_us.unwrap_or_default(), grpc_recv_us);
172
173 Some(DexEvent::PumpFunTrade(PumpFunTradeEvent {
174 metadata,
175 mint,
176 is_buy: false,
177 bonding_curve: get_account(accounts, 3).unwrap_or_default(),
178 user: get_account(accounts, 6).unwrap_or_default(),
179 sol_amount,
180 token_amount,
181 fee_recipient: get_account(accounts, 1).unwrap_or_default(),
182 ..Default::default()
183 }))
184}
185
186fn parse_create_instruction(
192 data: &[u8],
193 accounts: &[Pubkey],
194 signature: Signature,
195 slot: u64,
196 tx_index: u64,
197 block_time_us: Option<i64>,
198 grpc_recv_us: i64,
199) -> Option<DexEvent> {
200 if accounts.len() < 8 {
201 return None;
202 }
203
204 let mut offset = 0;
205
206 let name = if let Some((s, len)) = read_str_unchecked(data, offset) {
209 offset += len;
210 s.to_string()
211 } else {
212 String::new()
213 };
214
215 let symbol = if let Some((s, len)) = read_str_unchecked(data, offset) {
216 offset += len;
217 s.to_string()
218 } else {
219 String::new()
220 };
221
222 let uri = if let Some((s, len)) = read_str_unchecked(data, offset) {
223 offset += len;
224 s.to_string()
225 } else {
226 String::new()
227 };
228
229 if data.len() < offset + 32 + 32 + 32 + 32 {
231 return None;
232 }
233
234 let mint = read_pubkey(data, offset).unwrap_or_default();
235 offset += 32;
236
237 let bonding_curve = read_pubkey(data, offset).unwrap_or_default();
238 offset += 32;
239
240 let user = read_pubkey(data, offset).unwrap_or_default();
241 offset += 32;
242
243 let creator = read_pubkey(data, offset).unwrap_or_default();
244
245 let metadata =
246 create_metadata(signature, slot, tx_index, block_time_us.unwrap_or_default(), grpc_recv_us);
247
248 Some(DexEvent::PumpFunCreate(PumpFunCreateTokenEvent {
249 metadata,
250 name,
251 symbol,
252 uri,
253 mint,
254 bonding_curve,
255 user,
256 creator,
257 ..Default::default()
258 }))
259}
260
261fn parse_create_v2_instruction(
270 data: &[u8],
271 accounts: &[Pubkey],
272 signature: Signature,
273 slot: u64,
274 tx_index: u64,
275 block_time_us: Option<i64>,
276 grpc_recv_us: i64,
277) -> Option<DexEvent> {
278 const CREATE_V2_MIN_ACCOUNTS: usize = 16;
279 if accounts.len() < CREATE_V2_MIN_ACCOUNTS {
280 return None;
281 }
282 let acc = &accounts[0..CREATE_V2_MIN_ACCOUNTS];
283
284 let mut offset = 0usize;
286 let name = if let Some((s, len)) = read_str_unchecked(data, offset) {
287 offset += len;
288 s.to_string()
289 } else {
290 String::new()
291 };
292 let symbol = if let Some((s, len)) = read_str_unchecked(data, offset) {
293 offset += len;
294 s.to_string()
295 } else {
296 String::new()
297 };
298 let uri = if let Some((s, len)) = read_str_unchecked(data, offset) {
299 offset += len;
300 s.to_string()
301 } else {
302 String::new()
303 };
304 if data.len() < offset + 32 + 1 {
305 return None;
306 }
307 let creator = read_pubkey(data, offset)?;
308 offset += 32;
309 let is_mayhem_mode = read_bool(data, offset)?;
310 offset += 1;
311 let is_cashback_enabled = read_option_bool_idl(data, offset).unwrap_or(false);
312
313 let mint = acc[0];
314 let bonding_curve = acc[2];
315 let user = acc[5];
316
317 let metadata =
318 create_metadata(signature, slot, tx_index, block_time_us.unwrap_or_default(), grpc_recv_us);
319
320 Some(DexEvent::PumpFunCreateV2(PumpFunCreateV2TokenEvent {
321 metadata,
322 name,
323 symbol,
324 uri,
325 mint,
326 bonding_curve,
327 user,
328 creator,
329 mint_authority: acc[1],
330 associated_bonding_curve: acc[3],
331 global: acc[4],
332 system_program: acc[6],
333 token_program: acc[7],
334 associated_token_program: acc[8],
335 mayhem_program_id: acc[9],
336 global_params: acc[10],
337 sol_vault: acc[11],
338 mayhem_state: acc[12],
339 mayhem_token_vault: acc[13],
340 event_authority: acc[14],
341 program: acc[15],
342 is_mayhem_mode,
343 is_cashback_enabled,
344 ..Default::default()
345 }))
346}
347
348#[allow(unused_variables)]
350fn parse_migrate_log_instruction(
351 data: &[u8],
352 accounts: &[Pubkey],
353 signature: Signature,
354 slot: u64,
355 tx_index: u64,
356 block_time_us: Option<i64>,
357 rpc_recv_us: i64,
358) -> Option<DexEvent> {
359 let mut offset = 0;
360
361 let user = read_pubkey(data, offset)?;
363 offset += 32;
364
365 let mint = read_pubkey(data, offset)?;
367 offset += 32;
368
369 let mint_amount = read_u64_le(data, offset)?;
371 offset += 8;
372
373 let sol_amount = read_u64_le(data, offset)?;
375 offset += 8;
376
377 let pool_migration_fee = read_u64_le(data, offset)?;
379 offset += 8;
380
381 let bonding_curve = read_pubkey(data, offset)?;
383 offset += 32;
384
385 let timestamp = read_u64_le(data, offset)? as i64;
387 offset += 8;
388
389 let pool = read_pubkey(data, offset)?;
391
392 let metadata =
393 create_metadata(signature, slot, tx_index, block_time_us.unwrap_or_default(), rpc_recv_us);
394
395 Some(DexEvent::PumpFunMigrate(PumpFunMigrateEvent {
396 metadata,
397 user,
398 mint,
399 mint_amount,
400 sol_amount,
401 pool_migration_fee,
402 bonding_curve,
403 timestamp,
404 pool,
405 }))
406}