Skip to main content

sol_parser_sdk/logs/
pump_fees.rs

1//! `pfeeUx...`(pump-fees)Program log `Program data` → [`DexEvent`](crate::core::events::DexEvent)。
2//! 判别子与字段布局对齐 `idls/pump_fees.json` Anchor events / types。
3
4use 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/// `CreateFeeSharingConfigEvent`:数据为去掉 8 字节 discriminator 后的 Borsh 体。
163#[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); // pool None
400        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); // Active
406        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}