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