Skip to main content

sol_parser_sdk/instr/
pump_fees.rs

1//! Pump Fees(`pfeeUx...`)外层指令:`idls/pump_fees.json`。Shred/gRPC 共用账户索引语义。
2
3use solana_sdk::pubkey::Pubkey;
4use solana_sdk::signature::Signature;
5
6use crate::core::events::*;
7use crate::logs::pump_fees::{read_fee_tiers_vec, read_fees_at, read_shareholders_vec};
8
9pub(crate) const CREATE_FEE_SHARING_IX: [u8; 8] = [195, 78, 86, 76, 111, 52, 251, 213];
10pub(crate) const INITIALIZE_FEE_CONFIG_IX: [u8; 8] = [62, 162, 20, 133, 121, 65, 145, 27];
11pub(crate) const RESET_FEE_SHARING_IX: [u8; 8] = [10, 2, 182, 95, 16, 127, 129, 186];
12pub(crate) const RESET_FEE_SHARING_V2_IX: [u8; 8] = [169, 245, 17, 209, 94, 91, 248, 128];
13pub(crate) const REVOKE_FEE_SHARING_IX: [u8; 8] = [18, 233, 158, 39, 185, 207, 58, 104];
14pub(crate) const TRANSFER_FEE_SHARING_IX: [u8; 8] = [202, 10, 75, 200, 164, 34, 210, 96];
15pub(crate) const UPDATE_ADMIN_IX: [u8; 8] = [161, 176, 40, 213, 60, 184, 179, 228];
16pub(crate) const UPDATE_FEE_CONFIG_IX: [u8; 8] = [104, 184, 103, 242, 88, 151, 107, 20];
17pub(crate) const UPDATE_FEE_SHARES_IX: [u8; 8] = [189, 13, 136, 99, 187, 164, 237, 35];
18pub(crate) const UPDATE_FEE_SHARES_V2_IX: [u8; 8] = [111, 251, 49, 6, 78, 78, 106, 18];
19pub(crate) const UPSERT_FEE_TIERS_IX: [u8; 8] = [227, 23, 150, 12, 77, 86, 94, 4];
20
21#[inline(always)]
22fn disc8(data: &[u8]) -> Option<[u8; 8]> {
23    data.get(..8)?.try_into().ok()
24}
25
26#[inline(always)]
27fn metadata(
28    signature: Signature,
29    slot: u64,
30    tx_index: u64,
31    block_time_us: Option<i64>,
32    grpc_recv_us: i64,
33) -> EventMetadata {
34    EventMetadata {
35        signature,
36        slot,
37        tx_index,
38        block_time_us: block_time_us.unwrap_or(0),
39        grpc_recv_us,
40        recent_blockhash: None,
41    }
42}
43
44#[inline(always)]
45pub fn parse_instruction(
46    instruction_data: &[u8],
47    accounts: &[Pubkey],
48    signature: Signature,
49    slot: u64,
50    tx_index: u64,
51    block_time_us: Option<i64>,
52    grpc_recv_us: i64,
53) -> Option<DexEvent> {
54    let md = metadata(signature, slot, tx_index, block_time_us, grpc_recv_us);
55    let disc = disc8(instruction_data)?;
56
57    if disc == CREATE_FEE_SHARING_IX {
58        let payer = *accounts.get(2)?;
59        let mint = *accounts.get(4)?;
60        let sharing_config = accounts.get(5).copied().unwrap_or_default();
61        let bonding_curve = accounts.get(7).copied().unwrap_or_default();
62        let pool = accounts.get(10).copied();
63        return Some(DexEvent::PumpFeesCreateFeeSharingConfig(
64            PumpFeesCreateFeeSharingConfigEvent {
65                metadata: md,
66                timestamp: 0,
67                mint,
68                bonding_curve,
69                pool,
70                sharing_config,
71                admin: payer,
72                initial_shareholders: Vec::new(),
73                status: PumpFeesConfigStatus::Active,
74            },
75        ));
76    }
77
78    if disc == UPDATE_FEE_SHARES_IX || disc == UPDATE_FEE_SHARES_V2_IX {
79        if accounts.len() < 8 || instruction_data.len() < 8 {
80            return None;
81        }
82        let authority = *accounts.get(2)?;
83        let mint = *accounts.get(4)?;
84        let sharing_config = *accounts.get(5)?;
85        let bonding_curve = accounts.get(6).copied().unwrap_or_default();
86        let pump_creator_vault = accounts.get(7).copied().unwrap_or_default();
87        let mut o = 8usize;
88        let new_shareholders = read_shareholders_vec(instruction_data, &mut o)?;
89        if o != instruction_data.len() {
90            return None;
91        }
92        return Some(DexEvent::PumpFeesUpdateFeeShares(PumpFeesUpdateFeeSharesEvent {
93            metadata: md,
94            timestamp: 0,
95            mint,
96            sharing_config,
97            admin: authority,
98            bonding_curve,
99            pump_creator_vault,
100            new_shareholders,
101        }));
102    }
103
104    if disc == INITIALIZE_FEE_CONFIG_IX {
105        let admin = *accounts.first()?;
106        let fee_config = *accounts.get(1)?;
107        return Some(DexEvent::PumpFeesInitializeFeeConfig(PumpFeesInitializeFeeConfigEvent {
108            metadata: md,
109            timestamp: 0,
110            admin,
111            fee_config,
112        }));
113    }
114
115    if disc == RESET_FEE_SHARING_IX || disc == RESET_FEE_SHARING_V2_IX {
116        let new_admin = *accounts.first()?;
117        let old_admin = *accounts.get(3)?;
118        let mint = *accounts.get(5)?;
119        let sharing_config = *accounts.get(6)?;
120        return Some(DexEvent::PumpFeesResetFeeSharingConfig(PumpFeesResetFeeSharingConfigEvent {
121            metadata: md,
122            timestamp: 0,
123            mint,
124            sharing_config,
125            old_admin,
126            old_shareholders: Vec::new(),
127            new_admin,
128            new_shareholders: Vec::new(),
129        }));
130    }
131
132    if disc == REVOKE_FEE_SHARING_IX {
133        let admin = *accounts.first()?;
134        let mint = *accounts.get(2)?;
135        let sharing_config = *accounts.get(3)?;
136        return Some(DexEvent::PumpFeesRevokeFeeSharingAuthority(
137            PumpFeesRevokeFeeSharingAuthorityEvent {
138                metadata: md,
139                timestamp: 0,
140                mint,
141                sharing_config,
142                admin,
143            },
144        ));
145    }
146
147    if disc == TRANSFER_FEE_SHARING_IX {
148        let old_admin = *accounts.first()?;
149        let mint = *accounts.get(2)?;
150        let sharing_config = *accounts.get(3)?;
151        let new_admin = *accounts.get(4)?;
152        return Some(DexEvent::PumpFeesTransferFeeSharingAuthority(
153            PumpFeesTransferFeeSharingAuthorityEvent {
154                metadata: md,
155                timestamp: 0,
156                mint,
157                sharing_config,
158                old_admin,
159                new_admin,
160            },
161        ));
162    }
163
164    if disc == UPDATE_ADMIN_IX {
165        let old_admin = *accounts.first()?;
166        let new_admin = *accounts.get(2)?;
167        return Some(DexEvent::PumpFeesUpdateAdmin(PumpFeesUpdateAdminEvent {
168            metadata: md,
169            timestamp: 0,
170            old_admin,
171            new_admin,
172        }));
173    }
174
175    if disc == UPDATE_FEE_CONFIG_IX {
176        let fee_config = *accounts.first()?;
177        let admin = *accounts.get(1)?;
178        if instruction_data.len() < 8 {
179            return None;
180        }
181        let mut o = 8usize;
182        let fee_tiers = read_fee_tiers_vec(instruction_data, &mut o)?;
183        let flat_fees = read_fees_at(instruction_data, &mut o)?;
184        if o != instruction_data.len() {
185            return None;
186        }
187        return Some(DexEvent::PumpFeesUpdateFeeConfig(PumpFeesUpdateFeeConfigEvent {
188            metadata: md,
189            timestamp: 0,
190            admin,
191            fee_config,
192            fee_tiers,
193            flat_fees,
194        }));
195    }
196
197    if disc == UPSERT_FEE_TIERS_IX {
198        let fee_config = *accounts.first()?;
199        let admin = *accounts.get(1)?;
200        if instruction_data.len() < 8 {
201            return None;
202        }
203        let mut o = 8usize;
204        let fee_tiers = read_fee_tiers_vec(instruction_data, &mut o)?;
205        let offset = *instruction_data.get(o)?;
206        o += 1;
207        if o != instruction_data.len() {
208            return None;
209        }
210        return Some(DexEvent::PumpFeesUpsertFeeTiers(PumpFeesUpsertFeeTiersEvent {
211            metadata: md,
212            timestamp: 0,
213            admin,
214            fee_config,
215            fee_tiers,
216            offset,
217        }));
218    }
219
220    None
221}
222
223#[cfg(test)]
224mod tests {
225    use super::*;
226
227    fn pk(byte: u8) -> Pubkey {
228        Pubkey::new_from_array([byte; 32])
229    }
230
231    fn accounts() -> Vec<Pubkey> {
232        vec![
233            pk(1),  // event_authority
234            pk(2),  // program
235            pk(3),  // authority
236            pk(4),  // global
237            pk(5),  // mint
238            pk(6),  // sharing_config
239            pk(7),  // bonding_curve
240            pk(8),  // pump_creator_vault
241            pk(9),  // pump_creator_vault_ata (v2) / system_program (v1)
242            pk(10), // system_program (v2) / pump_program (v1)
243        ]
244    }
245
246    fn reset_accounts() -> Vec<Pubkey> {
247        vec![
248            pk(10), // new_admin
249            pk(11), // event_authority
250            pk(12), // program
251            pk(13), // authority / old admin
252            pk(14), // global
253            pk(15), // mint
254            pk(16), // sharing_config
255            pk(17), // bonding_curve
256            pk(18), // pump_creator_vault
257            pk(19), // v1 system_program / v2 pump_creator_vault_ata
258        ]
259    }
260
261    fn update_fee_shares_data(disc: [u8; 8]) -> Vec<u8> {
262        let mut data = disc.to_vec();
263        data.extend_from_slice(&1u32.to_le_bytes());
264        data.extend_from_slice(&pk(42).to_bytes());
265        data.extend_from_slice(&2500u16.to_le_bytes());
266        data
267    }
268
269    fn parse_update_fee_shares(disc: [u8; 8]) -> PumpFeesUpdateFeeSharesEvent {
270        let event = parse_instruction(
271            &update_fee_shares_data(disc),
272            &accounts(),
273            Signature::default(),
274            1,
275            0,
276            None,
277            99,
278        )
279        .expect("update_fee_shares event");
280
281        match event {
282            DexEvent::PumpFeesUpdateFeeShares(e) => e,
283            other => panic!("expected PumpFeesUpdateFeeShares, got {other:?}"),
284        }
285    }
286
287    #[test]
288    fn parses_update_fee_shares_v1_ix_vault() {
289        let event = parse_update_fee_shares(UPDATE_FEE_SHARES_IX);
290        assert_eq!(event.mint, pk(5));
291        assert_eq!(event.sharing_config, pk(6));
292        assert_eq!(event.admin, pk(3));
293        assert_eq!(event.bonding_curve, pk(7));
294        assert_eq!(event.pump_creator_vault, pk(8));
295        assert_eq!(event.new_shareholders[0].address, pk(42));
296        assert_eq!(event.new_shareholders[0].share_bps, 2500);
297    }
298
299    #[test]
300    fn parses_update_fee_shares_v2_ix_vault() {
301        let event = parse_update_fee_shares(UPDATE_FEE_SHARES_V2_IX);
302        assert_eq!(event.mint, pk(5));
303        assert_eq!(event.sharing_config, pk(6));
304        assert_eq!(event.admin, pk(3));
305        assert_eq!(event.bonding_curve, pk(7));
306        assert_eq!(event.pump_creator_vault, pk(8));
307        assert_eq!(event.new_shareholders[0].address, pk(42));
308        assert_eq!(event.new_shareholders[0].share_bps, 2500);
309    }
310
311    #[test]
312    fn parses_reset_fee_sharing_accounts_from_idl_order() {
313        for disc in [RESET_FEE_SHARING_IX, RESET_FEE_SHARING_V2_IX] {
314            let event =
315                parse_instruction(&disc, &reset_accounts(), Signature::default(), 1, 0, None, 99)
316                    .expect("reset fee sharing event");
317
318            match event {
319                DexEvent::PumpFeesResetFeeSharingConfig(e) => {
320                    assert_eq!(e.new_admin, pk(10));
321                    assert_eq!(e.old_admin, pk(13));
322                    assert_eq!(e.mint, pk(15));
323                    assert_eq!(e.sharing_config, pk(16));
324                }
325                other => panic!("expected PumpFeesResetFeeSharingConfig, got {other:?}"),
326            }
327        }
328    }
329}