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] = [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/// `CreateFeeSharingConfigEvent`:数据为去掉 8 字节 discriminator 后的 Borsh 体。
160#[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); // pool None
384        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); // Active
390        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}