1use 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), pk(2), pk(3), pk(4), pk(5), pk(6), pk(7), pk(8), pk(9), pk(10), ]
244 }
245
246 fn reset_accounts() -> Vec<Pubkey> {
247 vec![
248 pk(10), pk(11), pk(12), pk(13), pk(14), pk(15), pk(16), pk(17), pk(18), pk(19), ]
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}