1use crate::core::events::*;
5use solana_sdk::pubkey::Pubkey;
6
7pub const CREATE_FEE_SHARING_CONFIG_EVENT_DISC: [u8; 8] = [133, 105, 170, 200, 184, 116, 251, 88];
8pub const INITIALIZE_FEE_CONFIG_EVENT_DISC: [u8; 8] = [89, 138, 244, 230, 10, 56, 226, 126];
9pub const RESET_FEE_SHARING_CONFIG_EVENT_DISC: [u8; 8] = [203, 204, 151, 226, 120, 55, 214, 243];
10pub const REVOKE_FEE_SHARING_AUTHORITY_EVENT_DISC: [u8; 8] = [114, 23, 101, 60, 14, 190, 153, 62];
11pub const TRANSFER_FEE_SHARING_AUTHORITY_EVENT_DISC: [u8; 8] =
12 [124, 143, 198, 245, 77, 184, 8, 236];
13pub const UPDATE_ADMIN_EVENT_DISC: [u8; 8] = [225, 152, 171, 87, 246, 63, 66, 234];
14pub const UPDATE_FEE_CONFIG_EVENT_DISC: [u8; 8] = [90, 23, 65, 35, 62, 244, 188, 208];
15pub const UPDATE_FEE_SHARES_EVENT_DISC: [u8; 8] = [21, 186, 196, 184, 91, 228, 225, 203];
16pub const UPSERT_FEE_TIERS_EVENT_DISC: [u8; 8] = [171, 89, 169, 187, 122, 186, 33, 204];
17
18#[inline(always)]
19pub const fn discriminant_u64(disc: &[u8; 8]) -> u64 {
20 u64::from_le_bytes(*disc)
21}
22
23const MAX_SHAREHOLDERS: usize = 64;
24const MAX_FEE_TIERS: usize = 64;
25
26#[inline(always)]
27fn read_i64_at(data: &[u8], o: &mut usize) -> Option<i64> {
28 if data.len() < *o + 8 {
29 return None;
30 }
31 let v = i64::from_le_bytes(data[*o..*o + 8].try_into().ok()?);
32 *o += 8;
33 Some(v)
34}
35
36#[inline(always)]
37fn read_u8_at(data: &[u8], o: &mut usize) -> Option<u8> {
38 let v = *data.get(*o)?;
39 *o += 1;
40 Some(v)
41}
42
43#[inline(always)]
44fn read_u16_at(data: &[u8], o: &mut usize) -> Option<u16> {
45 if data.len() < *o + 2 {
46 return None;
47 }
48 let v = u16::from_le_bytes(data[*o..*o + 2].try_into().ok()?);
49 *o += 2;
50 Some(v)
51}
52
53#[inline(always)]
54fn read_u32_at(data: &[u8], o: &mut usize) -> Option<u32> {
55 if data.len() < *o + 4 {
56 return None;
57 }
58 let v = u32::from_le_bytes(data[*o..*o + 4].try_into().ok()?);
59 *o += 4;
60 Some(v)
61}
62
63#[inline(always)]
64fn read_u64_at(data: &[u8], o: &mut usize) -> Option<u64> {
65 if data.len() < *o + 8 {
66 return None;
67 }
68 let v = u64::from_le_bytes(data[*o..*o + 8].try_into().ok()?);
69 *o += 8;
70 Some(v)
71}
72
73#[inline(always)]
74fn read_u128_at(data: &[u8], o: &mut usize) -> Option<u128> {
75 if data.len() < *o + 16 {
76 return None;
77 }
78 let v = u128::from_le_bytes(data[*o..*o + 16].try_into().ok()?);
79 *o += 16;
80 Some(v)
81}
82
83#[inline(always)]
84fn read_pubkey_at(data: &[u8], o: &mut usize) -> Option<Pubkey> {
85 if data.len() < *o + 32 {
86 return None;
87 }
88 let pk = Pubkey::new_from_array(data[*o..*o + 32].try_into().ok()?);
89 *o += 32;
90 Some(pk)
91}
92
93#[inline(always)]
94fn read_option_pubkey_at(data: &[u8], o: &mut usize) -> Option<Option<Pubkey>> {
95 let tag = *data.get(*o)?;
96 *o += 1;
97 match tag {
98 0 => Some(None),
99 1 => Some(Some(read_pubkey_at(data, o)?)),
100 _ => None,
101 }
102}
103
104#[inline(always)]
105fn read_config_status_at(data: &[u8], o: &mut usize) -> Option<PumpFeesConfigStatus> {
106 let b = *data.get(*o)?;
107 *o += 1;
108 match b {
109 0 => Some(PumpFeesConfigStatus::Paused),
110 1 => Some(PumpFeesConfigStatus::Active),
111 _ => None,
112 }
113}
114
115#[inline(always)]
116pub(crate) fn read_fees_at(data: &[u8], o: &mut usize) -> Option<PumpFeesFees> {
117 Some(PumpFeesFees {
118 lp_fee_bps: read_u64_at(data, o)?,
119 protocol_fee_bps: read_u64_at(data, o)?,
120 creator_fee_bps: read_u64_at(data, o)?,
121 })
122}
123
124#[inline(always)]
125pub(crate) fn read_shareholders_vec(
126 data: &[u8],
127 o: &mut usize,
128) -> Option<Vec<PumpFeesShareholder>> {
129 let n = read_u32_at(data, o)? as usize;
130 if n > MAX_SHAREHOLDERS {
131 return None;
132 }
133 let mut v = Vec::with_capacity(n);
134 for _ in 0..n {
135 v.push(PumpFeesShareholder {
136 address: read_pubkey_at(data, o)?,
137 share_bps: read_u16_at(data, o)?,
138 });
139 }
140 Some(v)
141}
142
143#[inline(always)]
144pub(crate) fn read_fee_tiers_vec(data: &[u8], o: &mut usize) -> Option<Vec<PumpFeesFeeTier>> {
145 let n = read_u32_at(data, o)? as usize;
146 if n > MAX_FEE_TIERS {
147 return None;
148 }
149 let mut v = Vec::with_capacity(n);
150 for _ in 0..n {
151 v.push(PumpFeesFeeTier {
152 market_cap_lamports_threshold: read_u128_at(data, o)?,
153 fees: read_fees_at(data, o)?,
154 });
155 }
156 Some(v)
157}
158
159#[inline]
161pub fn parse_create_fee_sharing_config_from_data(
162 data: &[u8],
163 metadata: EventMetadata,
164) -> Option<DexEvent> {
165 let mut o = 0usize;
166 let timestamp = read_i64_at(data, &mut o)?;
167 let mint = read_pubkey_at(data, &mut o)?;
168 let bonding_curve = read_pubkey_at(data, &mut o)?;
169 let pool = read_option_pubkey_at(data, &mut o)?;
170 let sharing_config = read_pubkey_at(data, &mut o)?;
171 let admin = read_pubkey_at(data, &mut o)?;
172 let initial_shareholders = read_shareholders_vec(data, &mut o)?;
173 let status = read_config_status_at(data, &mut o)?;
174 if o != data.len() {
175 return None;
176 }
177 Some(DexEvent::PumpFeesCreateFeeSharingConfig(PumpFeesCreateFeeSharingConfigEvent {
178 metadata,
179 timestamp,
180 mint,
181 bonding_curve,
182 pool,
183 sharing_config,
184 admin,
185 initial_shareholders,
186 status,
187 }))
188}
189
190#[inline]
191pub fn parse_initialize_fee_config_from_data(
192 data: &[u8],
193 metadata: EventMetadata,
194) -> Option<DexEvent> {
195 let mut o = 0usize;
196 let timestamp = read_i64_at(data, &mut o)?;
197 let admin = read_pubkey_at(data, &mut o)?;
198 let fee_config = read_pubkey_at(data, &mut o)?;
199 if o != data.len() {
200 return None;
201 }
202 Some(DexEvent::PumpFeesInitializeFeeConfig(PumpFeesInitializeFeeConfigEvent {
203 metadata,
204 timestamp,
205 admin,
206 fee_config,
207 }))
208}
209
210#[inline]
211pub fn parse_reset_fee_sharing_config_from_data(
212 data: &[u8],
213 metadata: EventMetadata,
214) -> Option<DexEvent> {
215 let mut o = 0usize;
216 let timestamp = read_i64_at(data, &mut o)?;
217 let mint = read_pubkey_at(data, &mut o)?;
218 let sharing_config = read_pubkey_at(data, &mut o)?;
219 let old_admin = read_pubkey_at(data, &mut o)?;
220 let old_shareholders = read_shareholders_vec(data, &mut o)?;
221 let new_admin = read_pubkey_at(data, &mut o)?;
222 let new_shareholders = read_shareholders_vec(data, &mut o)?;
223 if o != data.len() {
224 return None;
225 }
226 Some(DexEvent::PumpFeesResetFeeSharingConfig(PumpFeesResetFeeSharingConfigEvent {
227 metadata,
228 timestamp,
229 mint,
230 sharing_config,
231 old_admin,
232 old_shareholders,
233 new_admin,
234 new_shareholders,
235 }))
236}
237
238#[inline]
239pub fn parse_revoke_fee_sharing_authority_from_data(
240 data: &[u8],
241 metadata: EventMetadata,
242) -> Option<DexEvent> {
243 let mut o = 0usize;
244 let timestamp = read_i64_at(data, &mut o)?;
245 let mint = read_pubkey_at(data, &mut o)?;
246 let sharing_config = read_pubkey_at(data, &mut o)?;
247 let admin = read_pubkey_at(data, &mut o)?;
248 if o != data.len() {
249 return None;
250 }
251 Some(DexEvent::PumpFeesRevokeFeeSharingAuthority(PumpFeesRevokeFeeSharingAuthorityEvent {
252 metadata,
253 timestamp,
254 mint,
255 sharing_config,
256 admin,
257 }))
258}
259
260#[inline]
261pub fn parse_transfer_fee_sharing_authority_from_data(
262 data: &[u8],
263 metadata: EventMetadata,
264) -> Option<DexEvent> {
265 let mut o = 0usize;
266 let timestamp = read_i64_at(data, &mut o)?;
267 let mint = read_pubkey_at(data, &mut o)?;
268 let sharing_config = read_pubkey_at(data, &mut o)?;
269 let old_admin = read_pubkey_at(data, &mut o)?;
270 let new_admin = read_pubkey_at(data, &mut o)?;
271 if o != data.len() {
272 return None;
273 }
274 Some(DexEvent::PumpFeesTransferFeeSharingAuthority(PumpFeesTransferFeeSharingAuthorityEvent {
275 metadata,
276 timestamp,
277 mint,
278 sharing_config,
279 old_admin,
280 new_admin,
281 }))
282}
283
284#[inline]
285pub fn parse_update_admin_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
286 let mut o = 0usize;
287 let timestamp = read_i64_at(data, &mut o)?;
288 let old_admin = read_pubkey_at(data, &mut o)?;
289 let new_admin = read_pubkey_at(data, &mut o)?;
290 if o != data.len() {
291 return None;
292 }
293 Some(DexEvent::PumpFeesUpdateAdmin(PumpFeesUpdateAdminEvent {
294 metadata,
295 timestamp,
296 old_admin,
297 new_admin,
298 }))
299}
300
301#[inline]
302pub fn parse_update_fee_config_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
303 let mut o = 0usize;
304 let timestamp = read_i64_at(data, &mut o)?;
305 let admin = read_pubkey_at(data, &mut o)?;
306 let fee_config = read_pubkey_at(data, &mut o)?;
307 let fee_tiers = read_fee_tiers_vec(data, &mut o)?;
308 let flat_fees = read_fees_at(data, &mut o)?;
309 if o != data.len() {
310 return None;
311 }
312 Some(DexEvent::PumpFeesUpdateFeeConfig(PumpFeesUpdateFeeConfigEvent {
313 metadata,
314 timestamp,
315 admin,
316 fee_config,
317 fee_tiers,
318 flat_fees,
319 }))
320}
321
322#[inline]
323pub fn parse_update_fee_shares_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
324 let mut o = 0usize;
325 let timestamp = read_i64_at(data, &mut o)?;
326 let mint = read_pubkey_at(data, &mut o)?;
327 let sharing_config = read_pubkey_at(data, &mut o)?;
328 let admin = read_pubkey_at(data, &mut o)?;
329 let new_shareholders = read_shareholders_vec(data, &mut o)?;
330 if o != data.len() {
331 return None;
332 }
333 Some(DexEvent::PumpFeesUpdateFeeShares(PumpFeesUpdateFeeSharesEvent {
334 metadata,
335 timestamp,
336 mint,
337 sharing_config,
338 admin,
339 bonding_curve: Pubkey::default(),
340 pump_creator_vault: Pubkey::default(),
341 new_shareholders,
342 }))
343}
344
345#[inline]
346pub fn parse_upsert_fee_tiers_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
347 let mut o = 0usize;
348 let timestamp = read_i64_at(data, &mut o)?;
349 let admin = read_pubkey_at(data, &mut o)?;
350 let fee_config = read_pubkey_at(data, &mut o)?;
351 let fee_tiers = read_fee_tiers_vec(data, &mut o)?;
352 let offset = read_u8_at(data, &mut o)?;
353 if o != data.len() {
354 return None;
355 }
356 Some(DexEvent::PumpFeesUpsertFeeTiers(PumpFeesUpsertFeeTiersEvent {
357 metadata,
358 timestamp,
359 admin,
360 fee_config,
361 fee_tiers,
362 offset,
363 }))
364}
365
366#[cfg(test)]
367mod tests {
368 use super::*;
369 use solana_sdk::signature::Signature;
370
371 #[test]
372 fn create_fee_sharing_roundtrip() {
373 let ts: i64 = 1_777_920_719;
374 let mint = Pubkey::new_unique();
375 let bonding_curve = Pubkey::new_unique();
376 let sharing_config = Pubkey::new_unique();
377 let admin = Pubkey::new_unique();
378 let sh_addr = Pubkey::new_unique();
379 let mut buf = Vec::new();
380 buf.extend_from_slice(&ts.to_le_bytes());
381 buf.extend_from_slice(mint.as_ref());
382 buf.extend_from_slice(bonding_curve.as_ref());
383 buf.push(0u8); buf.extend_from_slice(sharing_config.as_ref());
385 buf.extend_from_slice(admin.as_ref());
386 buf.extend_from_slice(&(1u32).to_le_bytes());
387 buf.extend_from_slice(sh_addr.as_ref());
388 buf.extend_from_slice(&(10_000u16).to_le_bytes());
389 buf.push(1u8); let md = EventMetadata {
391 signature: Signature::default(),
392 slot: 0,
393 tx_index: 0,
394 block_time_us: 0,
395 grpc_recv_us: 0,
396 recent_blockhash: None,
397 };
398 let ev = parse_create_fee_sharing_config_from_data(&buf, md).unwrap();
399 match ev {
400 DexEvent::PumpFeesCreateFeeSharingConfig(e) => {
401 assert_eq!(e.timestamp, ts);
402 assert_eq!(e.initial_shareholders[0].address, sh_addr);
403 assert_eq!(e.status, PumpFeesConfigStatus::Active);
404 }
405 _ => panic!("variant"),
406 }
407 }
408
409 #[test]
410 fn update_fee_shares_roundtrip_program_data() {
411 let mint = Pubkey::new_unique();
412 let cfg = Pubkey::new_unique();
413 let adm = Pubkey::new_unique();
414 let mut buf = Vec::new();
415 buf.extend_from_slice(&100i64.to_le_bytes());
416 buf.extend_from_slice(mint.as_ref());
417 buf.extend_from_slice(cfg.as_ref());
418 buf.extend_from_slice(adm.as_ref());
419 buf.extend_from_slice(&(0u32).to_le_bytes());
420 let md = EventMetadata {
421 signature: Signature::default(),
422 slot: 0,
423 tx_index: 0,
424 block_time_us: 0,
425 grpc_recv_us: 0,
426 recent_blockhash: None,
427 };
428 assert!(matches!(
429 parse_update_fee_shares_from_data(&buf, md),
430 Some(DexEvent::PumpFeesUpdateFeeShares(_))
431 ));
432 }
433}