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