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(data, accounts, signature, slot, tx_index, block_time_us, grpc_recv_us);
51 }
52 if outer_disc == discriminators::CREATE {
53 return parse_create_instruction(data, accounts, signature, slot, tx_index, block_time_us, grpc_recv_us);
54 }
55
56 if instruction_data.len() >= 16 {
58 let cpi_disc: [u8; 8] = instruction_data[8..16].try_into().ok()?;
59 if cpi_disc == discriminators::MIGRATE_EVENT_LOG {
60 return parse_migrate_log_instruction(
61 &instruction_data[16..],
62 accounts,
63 signature,
64 slot,
65 tx_index,
66 block_time_us,
67 grpc_recv_us,
68 );
69 }
70 }
71 None
72}
73
74#[allow(dead_code)]
84fn parse_buy_instruction(
85 data: &[u8],
86 accounts: &[Pubkey],
87 signature: Signature,
88 slot: u64,
89 tx_index: u64,
90 block_time_us: Option<i64>,
91 grpc_recv_us: i64,
92) -> Option<DexEvent> {
93 if accounts.len() < 7 {
94 return None;
95 }
96
97 let (sol_amount, token_amount) = if data.len() >= 16 {
99 (read_u64_le(data, 0).unwrap_or(0), read_u64_le(data, 8).unwrap_or(0))
100 } else {
101 (0, 0)
102 };
103
104 let mint = get_account(accounts, 2)?;
105 let metadata = create_metadata(
106 signature, slot, tx_index,
107 block_time_us.unwrap_or_default(), grpc_recv_us
108 );
109
110 Some(DexEvent::PumpFunTrade(PumpFunTradeEvent {
111 metadata,
112 mint,
113 is_buy: true,
114 bonding_curve: get_account(accounts, 3).unwrap_or_default(),
115 user: get_account(accounts, 6).unwrap_or_default(),
116 sol_amount,
117 token_amount,
118 fee_recipient: get_account(accounts, 1).unwrap_or_default(),
119 ..Default::default()
120 }))
121}
122
123#[allow(dead_code)]
132fn parse_sell_instruction(
133 data: &[u8],
134 accounts: &[Pubkey],
135 signature: Signature,
136 slot: u64,
137 tx_index: u64,
138 block_time_us: Option<i64>,
139 grpc_recv_us: i64,
140) -> Option<DexEvent> {
141 if accounts.len() < 7 {
142 return None;
143 }
144
145 let (token_amount, sol_amount) = if data.len() >= 16 {
147 (read_u64_le(data, 0).unwrap_or(0), read_u64_le(data, 8).unwrap_or(0))
148 } else {
149 (0, 0)
150 };
151
152 let mint = get_account(accounts, 2)?;
153 let metadata = create_metadata(
154 signature, slot, tx_index,
155 block_time_us.unwrap_or_default(), grpc_recv_us
156 );
157
158 Some(DexEvent::PumpFunTrade(PumpFunTradeEvent {
159 metadata,
160 mint,
161 is_buy: false,
162 bonding_curve: get_account(accounts, 3).unwrap_or_default(),
163 user: get_account(accounts, 6).unwrap_or_default(),
164 sol_amount,
165 token_amount,
166 fee_recipient: get_account(accounts, 1).unwrap_or_default(),
167 ..Default::default()
168 }))
169}
170
171fn parse_create_instruction(
177 data: &[u8],
178 accounts: &[Pubkey],
179 signature: Signature,
180 slot: u64,
181 tx_index: u64,
182 block_time_us: Option<i64>,
183 grpc_recv_us: i64,
184) -> Option<DexEvent> {
185 if accounts.len() < 8 {
186 return None;
187 }
188
189 let mut offset = 0;
190
191 let name = if let Some((s, len)) = read_str_unchecked(data, offset) {
194 offset += len;
195 s.to_string()
196 } else {
197 String::new()
198 };
199
200 let symbol = if let Some((s, len)) = read_str_unchecked(data, offset) {
201 offset += len;
202 s.to_string()
203 } else {
204 String::new()
205 };
206
207 let uri = if let Some((s, len)) = read_str_unchecked(data, offset) {
208 offset += len;
209 s.to_string()
210 } else {
211 String::new()
212 };
213
214 let creator = if offset + 32 <= data.len() {
215 read_pubkey(data, offset).unwrap_or_default()
216 } else {
217 Pubkey::default()
218 };
219
220 let mint = get_account(accounts, 0)?;
221 let metadata = create_metadata(
222 signature, slot, tx_index,
223 block_time_us.unwrap_or_default(), grpc_recv_us
224 );
225
226 Some(DexEvent::PumpFunCreate(PumpFunCreateTokenEvent {
227 metadata,
228 name,
229 symbol,
230 uri,
231 mint,
232 bonding_curve: get_account(accounts, 2).unwrap_or_default(),
233 user: get_account(accounts, 7).unwrap_or_default(),
234 creator,
235 ..Default::default()
236 }))
237}
238
239fn parse_create_v2_instruction(
247 data: &[u8],
248 accounts: &[Pubkey],
249 signature: Signature,
250 slot: u64,
251 tx_index: u64,
252 block_time_us: Option<i64>,
253 grpc_recv_us: i64,
254) -> Option<DexEvent> {
255 const CREATE_V2_MIN_ACCOUNTS: usize = 16;
256 if accounts.len() < CREATE_V2_MIN_ACCOUNTS {
257 return None;
258 }
259 let acc = &accounts[0..CREATE_V2_MIN_ACCOUNTS];
260
261 let mut offset = 0;
262 let name = if let Some((s, len)) = read_str_unchecked(data, offset) {
263 offset += len;
264 s.to_string()
265 } else {
266 String::new()
267 };
268 let symbol = if let Some((s, len)) = read_str_unchecked(data, offset) {
269 offset += len;
270 s.to_string()
271 } else {
272 String::new()
273 };
274 let uri = if let Some((s, len)) = read_str_unchecked(data, offset) {
275 offset += len;
276 s.to_string()
277 } else {
278 String::new()
279 };
280 let creator = if offset + 32 <= data.len() {
281 read_pubkey(data, offset).unwrap_or_default()
282 } else {
283 Pubkey::default()
284 };
285
286 let mint = acc[0];
287 let metadata = create_metadata(
288 signature, slot, tx_index,
289 block_time_us.unwrap_or_default(), grpc_recv_us,
290 );
291
292 Some(DexEvent::PumpFunCreateV2(PumpFunCreateV2TokenEvent {
293 metadata,
294 name,
295 symbol,
296 uri,
297 mint,
298 bonding_curve: acc[2],
299 user: acc[5],
300 creator,
301 mint_authority: acc[1],
302 associated_bonding_curve: acc[3],
303 global: acc[4],
304 system_program: acc[6],
305 token_program: acc[7],
306 associated_token_program: acc[8],
307 mayhem_program_id: acc[9],
308 global_params: acc[10],
309 sol_vault: acc[11],
310 mayhem_state: acc[12],
311 mayhem_token_vault: acc[13],
312 event_authority: acc[14],
313 program: acc[15],
314 ..Default::default()
315 }))
316}
317
318#[allow(unused_variables)]
320fn parse_migrate_log_instruction(
321 data: &[u8],
322 accounts: &[Pubkey],
323 signature: Signature,
324 slot: u64,
325 tx_index: u64,
326 block_time_us: Option<i64>,
327 rpc_recv_us: i64,
328) -> Option<DexEvent> {
329 let mut offset = 0;
330
331 let user = read_pubkey(data, offset)?;
333 offset += 32;
334
335 let mint = read_pubkey(data, offset)?;
337 offset += 32;
338
339 let mint_amount = read_u64_le(data, offset)?;
341 offset += 8;
342
343 let sol_amount = read_u64_le(data, offset)?;
345 offset += 8;
346
347 let pool_migration_fee = read_u64_le(data, offset)?;
349 offset += 8;
350
351 let bonding_curve = read_pubkey(data, offset)?;
353 offset += 32;
354
355 let timestamp = read_u64_le(data, offset)? as i64;
357 offset += 8;
358
359 let pool = read_pubkey(data, offset)?;
361
362 let metadata =
363 create_metadata(signature, slot, tx_index, block_time_us.unwrap_or_default(), rpc_recv_us);
364
365 Some(DexEvent::PumpFunMigrate(PumpFunMigrateEvent {
366 metadata,
367 user,
368 mint,
369 mint_amount,
370 sol_amount,
371 pool_migration_fee,
372 bonding_curve,
373 timestamp,
374 pool,
375 }))
376}