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 BUY_EXACT_SOL_IN: [u8; 8] = [56, 252, 116, 8, 158, 223, 205, 95];
20 pub const MIGRATE_EVENT_LOG: [u8; 8] = [189, 233, 93, 185, 92, 148, 234, 148];
22}
23
24pub const PROGRAM_ID_PUBKEY: Pubkey = program_ids::PUMPFUN_PROGRAM_ID;
26
27pub fn parse_instruction(
32 instruction_data: &[u8],
33 accounts: &[Pubkey],
34 signature: Signature,
35 slot: u64,
36 tx_index: u64,
37 block_time_us: Option<i64>,
38 grpc_recv_us: i64,
39) -> Option<DexEvent> {
40 if instruction_data.len() < 16 {
43 return None;
44 }
45
46 let cpi_discriminator: [u8; 8] = instruction_data[8..16].try_into().ok()?;
47 if cpi_discriminator == discriminators::MIGRATE_EVENT_LOG {
48 parse_migrate_log_instruction(
49 &instruction_data[16..],
50 accounts,
51 signature,
52 slot,
53 tx_index,
54 block_time_us,
55 grpc_recv_us,
56 )
57 } else {
58 None
59 }
60}
61
62#[allow(dead_code)]
68fn parse_buy_instruction(
69 data: &[u8],
70 accounts: &[Pubkey],
71 signature: Signature,
72 slot: u64,
73 tx_index: u64,
74 block_time_us: Option<i64>,
75 grpc_recv_us: i64,
76) -> Option<DexEvent> {
77 if accounts.len() < 7 {
78 return None;
79 }
80
81 let (sol_amount, token_amount) = if data.len() >= 16 {
83 (read_u64_le(data, 0).unwrap_or(0), read_u64_le(data, 8).unwrap_or(0))
84 } else {
85 (0, 0)
86 };
87
88 let mint = get_account(accounts, 2)?;
89 let metadata = create_metadata(
90 signature, slot, tx_index,
91 block_time_us.unwrap_or_default(), grpc_recv_us
92 );
93
94 Some(DexEvent::PumpFunTrade(PumpFunTradeEvent {
95 metadata,
96 mint,
97 is_buy: true,
98 bonding_curve: get_account(accounts, 3).unwrap_or_default(),
99 user: get_account(accounts, 6).unwrap_or_default(),
100 sol_amount,
101 token_amount,
102 fee_recipient: get_account(accounts, 1).unwrap_or_default(),
103 ..Default::default()
104 }))
105}
106
107#[allow(dead_code)]
113fn parse_sell_instruction(
114 data: &[u8],
115 accounts: &[Pubkey],
116 signature: Signature,
117 slot: u64,
118 tx_index: u64,
119 block_time_us: Option<i64>,
120 grpc_recv_us: i64,
121) -> Option<DexEvent> {
122 if accounts.len() < 7 {
123 return None;
124 }
125
126 let (token_amount, sol_amount) = if data.len() >= 16 {
128 (read_u64_le(data, 0).unwrap_or(0), read_u64_le(data, 8).unwrap_or(0))
129 } else {
130 (0, 0)
131 };
132
133 let mint = get_account(accounts, 2)?;
134 let metadata = create_metadata(
135 signature, slot, tx_index,
136 block_time_us.unwrap_or_default(), grpc_recv_us
137 );
138
139 Some(DexEvent::PumpFunTrade(PumpFunTradeEvent {
140 metadata,
141 mint,
142 is_buy: false,
143 bonding_curve: get_account(accounts, 3).unwrap_or_default(),
144 user: get_account(accounts, 6).unwrap_or_default(),
145 sol_amount,
146 token_amount,
147 fee_recipient: get_account(accounts, 1).unwrap_or_default(),
148 ..Default::default()
149 }))
150}
151
152#[allow(dead_code)]
158fn parse_create_instruction(
159 data: &[u8],
160 accounts: &[Pubkey],
161 signature: Signature,
162 slot: u64,
163 tx_index: u64,
164 block_time_us: Option<i64>,
165 grpc_recv_us: i64,
166) -> Option<DexEvent> {
167 if accounts.len() < 8 {
168 return None;
169 }
170
171 let mut offset = 0;
172
173 let name = if let Some((s, len)) = read_str_unchecked(data, offset) {
176 offset += len;
177 s.to_string()
178 } else {
179 String::new()
180 };
181
182 let symbol = if let Some((s, len)) = read_str_unchecked(data, offset) {
183 offset += len;
184 s.to_string()
185 } else {
186 String::new()
187 };
188
189 let uri = if let Some((s, len)) = read_str_unchecked(data, offset) {
190 offset += len;
191 s.to_string()
192 } else {
193 String::new()
194 };
195
196 let creator = if offset + 32 <= data.len() {
197 read_pubkey(data, offset).unwrap_or_default()
198 } else {
199 Pubkey::default()
200 };
201
202 let mint = get_account(accounts, 0)?;
203 let metadata = create_metadata(
204 signature, slot, tx_index,
205 block_time_us.unwrap_or_default(), grpc_recv_us
206 );
207
208 Some(DexEvent::PumpFunCreate(PumpFunCreateTokenEvent {
209 metadata,
210 name,
211 symbol,
212 uri,
213 mint,
214 bonding_curve: get_account(accounts, 2).unwrap_or_default(),
215 user: get_account(accounts, 7).unwrap_or_default(),
216 creator,
217 ..Default::default()
218 }))
219}
220
221#[allow(unused_variables)]
223fn parse_migrate_log_instruction(
224 data: &[u8],
225 accounts: &[Pubkey],
226 signature: Signature,
227 slot: u64,
228 tx_index: u64,
229 block_time_us: Option<i64>,
230 rpc_recv_us: i64,
231) -> Option<DexEvent> {
232 let mut offset = 0;
233
234 let user = read_pubkey(data, offset)?;
236 offset += 32;
237
238 let mint = read_pubkey(data, offset)?;
240 offset += 32;
241
242 let mint_amount = read_u64_le(data, offset)?;
244 offset += 8;
245
246 let sol_amount = read_u64_le(data, offset)?;
248 offset += 8;
249
250 let pool_migration_fee = read_u64_le(data, offset)?;
252 offset += 8;
253
254 let bonding_curve = read_pubkey(data, offset)?;
256 offset += 32;
257
258 let timestamp = read_u64_le(data, offset)? as i64;
260 offset += 8;
261
262 let pool = read_pubkey(data, offset)?;
264
265 let metadata =
266 create_metadata(signature, slot, tx_index, block_time_us.unwrap_or_default(), rpc_recv_us);
267
268 Some(DexEvent::PumpFunMigrate(PumpFunMigrateEvent {
269 metadata,
270 user,
271 mint,
272 mint_amount,
273 sol_amount,
274 pool_migration_fee,
275 bonding_curve,
276 timestamp,
277 pool,
278 }))
279}