1use anchor_lang::{InstructionData, ToAccountMetas};
2use solana_program::{
3 instruction::{AccountMeta, Instruction},
4 pubkey::Pubkey,
5 system_program,
6};
7
8use crate::pump_amm::{
9 client as amm_client, types::OptionBool as AmmOptionBool, ID as PUMP_AMM_PROGRAM_ID,
10};
11use crate::state::pump_amm::{GlobalConfig, Pool};
12use crate::token::create_associated_token_account_idempotent;
13use crate::{constants, pda};
14
15use super::PumpSdk;
16
17struct AmmTradeAccounts {
21 coin_creator_vault_authority: Pubkey,
22 user_volume_accumulator: Pubkey,
23 user_base_token_account: Pubkey,
24 user_quote_token_account: Pubkey,
25 pool_base_token_account: Pubkey,
26 pool_quote_token_account: Pubkey,
27 protocol_fee_recipient_token_account: Pubkey,
28 coin_creator_vault_ata: Pubkey,
29}
30
31impl AmmTradeAccounts {
32 fn derive(
33 pool: Pubkey,
34 base_mint: Pubkey,
35 quote_mint: Pubkey,
36 base_token_program: Pubkey,
37 quote_token_program: Pubkey,
38 user: Pubkey,
39 coin_creator: Pubkey,
40 protocol_fee_recipient: Pubkey,
41 ) -> Self {
42 let coin_creator_vault_authority =
43 pda::pump_amm::coin_creator_vault_authority(&coin_creator).0;
44 let user_volume_accumulator = pda::pump_amm::user_volume_accumulator(&user).0;
45 let ata = |owner: &Pubkey, token_program: &Pubkey, mint: &Pubkey| {
46 pda::associated_token(owner, token_program, mint).0
47 };
48 Self {
49 coin_creator_vault_authority,
50 user_volume_accumulator,
51 user_base_token_account: ata(&user, &base_token_program, &base_mint),
52 user_quote_token_account: ata(&user, "e_token_program, "e_mint),
53 pool_base_token_account: ata(&pool, &base_token_program, &base_mint),
54 pool_quote_token_account: ata(&pool, "e_token_program, "e_mint),
55 protocol_fee_recipient_token_account: ata(
56 &protocol_fee_recipient,
57 "e_token_program,
58 "e_mint,
59 ),
60 coin_creator_vault_ata: ata(
61 &coin_creator_vault_authority,
62 "e_token_program,
63 "e_mint,
64 ),
65 }
66 }
67}
68
69impl PumpSdk {
70 pub fn buy_amm_instruction(
72 &self,
73 pool: Pubkey,
74 amm_global: &GlobalConfig,
75 pool_state: &Pool,
76 base_token_program: Pubkey,
77 quote_token_program: Pubkey,
78 user: Pubkey,
79 base_amount_out: u64,
80 max_quote_amount_in: u64,
81 ) -> Option<Instruction> {
82 let (protocol_fee_recipient, buyback_fee_recipient) =
83 Self::amm_fee_recipients_pair(amm_global)?;
84 Some(self.buy_amm_instruction_with_recipients(
85 pool,
86 pool_state.base_mint,
87 pool_state.quote_mint,
88 base_token_program,
89 quote_token_program,
90 user,
91 pool_state.coin_creator,
92 protocol_fee_recipient,
93 buyback_fee_recipient,
94 pool_state.is_cashback_coin,
95 base_amount_out,
96 max_quote_amount_in,
97 ))
98 }
99
100 pub(crate) fn buy_amm_instruction_with_recipients(
101 &self,
102 pool: Pubkey,
103 base_mint: Pubkey,
104 quote_mint: Pubkey,
105 base_token_program: Pubkey,
106 quote_token_program: Pubkey,
107 user: Pubkey,
108 coin_creator: Pubkey,
109 protocol_fee_recipient: Pubkey,
110 buyback_fee_recipient: Pubkey,
111 is_cashback_coin: bool,
112 base_amount_out: u64,
113 max_quote_amount_in: u64,
114 ) -> Instruction {
115 let a = AmmTradeAccounts::derive(
116 pool,
117 base_mint,
118 quote_mint,
119 base_token_program,
120 quote_token_program,
121 user,
122 coin_creator,
123 protocol_fee_recipient,
124 );
125 let accounts = amm_client::accounts::Buy {
126 pool,
127 user,
128 global_config: pda::pump_amm::global_config().0,
129 base_mint,
130 quote_mint,
131 user_base_token_account: a.user_base_token_account,
132 user_quote_token_account: a.user_quote_token_account,
133 pool_base_token_account: a.pool_base_token_account,
134 pool_quote_token_account: a.pool_quote_token_account,
135 protocol_fee_recipient,
136 protocol_fee_recipient_token_account: a.protocol_fee_recipient_token_account,
137 base_token_program,
138 quote_token_program,
139 system_program: system_program::ID,
140 associated_token_program: constants::SPL_ATA_PROGRAM_ID,
141 event_authority: pda::pump_amm::event_authority().0,
142 program: PUMP_AMM_PROGRAM_ID,
143 coin_creator_vault_ata: a.coin_creator_vault_ata,
144 coin_creator_vault_authority: a.coin_creator_vault_authority,
145 global_volume_accumulator: pda::pump_amm::global_volume_accumulator().0,
146 user_volume_accumulator: a.user_volume_accumulator,
147 fee_config: pda::pump_amm::fee_config().0,
148 fee_program: constants::FEE_PROGRAM_ID,
149 };
150 let args = amm_client::args::Buy {
151 base_amount_out,
152 max_quote_amount_in,
153 track_volume: AmmOptionBool(true),
154 };
155 let mut metas = accounts.to_account_metas(None);
156 if is_cashback_coin {
157 metas.push(AccountMeta::new(
158 pda::associated_token(
159 &a.user_volume_accumulator,
160 "e_token_program,
161 &constants::NATIVE_MINT,
162 )
163 .0,
164 false,
165 ));
166 }
167 if coin_creator != Pubkey::default() {
168 metas.push(AccountMeta::new_readonly(
169 pda::pump_amm::pool_v2(&base_mint).0,
170 false,
171 ));
172 }
173 metas.push(AccountMeta::new(buyback_fee_recipient, false));
174 metas.push(AccountMeta::new(
175 pda::associated_token(&buyback_fee_recipient, "e_token_program, "e_mint).0,
176 false,
177 ));
178 Instruction {
179 program_id: PUMP_AMM_PROGRAM_ID,
180 accounts: metas,
181 data: args.data(),
182 }
183 }
184
185 pub fn buy_amm_instructions(
187 &self,
188 pool: Pubkey,
189 amm_global: &GlobalConfig,
190 pool_state: &Pool,
191 base_token_program: Pubkey,
192 quote_token_program: Pubkey,
193 user: Pubkey,
194 base_amount_out: u64,
195 max_quote_amount_in: u64,
196 ) -> Option<Vec<Instruction>> {
197 let buy = self.buy_amm_instruction(
198 pool,
199 amm_global,
200 pool_state,
201 base_token_program,
202 quote_token_program,
203 user,
204 base_amount_out,
205 max_quote_amount_in,
206 )?;
207 Some(vec![
208 create_associated_token_account_idempotent(
209 &user,
210 &user,
211 &pool_state.base_mint,
212 &base_token_program,
213 ),
214 buy,
215 ])
216 }
217
218 pub fn sell_amm_instruction(
220 &self,
221 pool: Pubkey,
222 amm_global: &GlobalConfig,
223 pool_state: &Pool,
224 base_token_program: Pubkey,
225 quote_token_program: Pubkey,
226 user: Pubkey,
227 base_amount_in: u64,
228 min_quote_amount_out: u64,
229 ) -> Option<Instruction> {
230 let (protocol_fee_recipient, buyback_fee_recipient) =
231 Self::amm_fee_recipients_pair(amm_global)?;
232 Some(self.sell_amm_instruction_with_recipients(
233 pool,
234 pool_state.base_mint,
235 pool_state.quote_mint,
236 base_token_program,
237 quote_token_program,
238 user,
239 pool_state.coin_creator,
240 protocol_fee_recipient,
241 buyback_fee_recipient,
242 pool_state.is_cashback_coin,
243 base_amount_in,
244 min_quote_amount_out,
245 ))
246 }
247
248 pub(crate) fn sell_amm_instruction_with_recipients(
249 &self,
250 pool: Pubkey,
251 base_mint: Pubkey,
252 quote_mint: Pubkey,
253 base_token_program: Pubkey,
254 quote_token_program: Pubkey,
255 user: Pubkey,
256 coin_creator: Pubkey,
257 protocol_fee_recipient: Pubkey,
258 buyback_fee_recipient: Pubkey,
259 is_cashback_coin: bool,
260 base_amount_in: u64,
261 min_quote_amount_out: u64,
262 ) -> Instruction {
263 let a = AmmTradeAccounts::derive(
264 pool,
265 base_mint,
266 quote_mint,
267 base_token_program,
268 quote_token_program,
269 user,
270 coin_creator,
271 protocol_fee_recipient,
272 );
273 let accounts = amm_client::accounts::Sell {
274 pool,
275 user,
276 global_config: pda::pump_amm::global_config().0,
277 base_mint,
278 quote_mint,
279 user_base_token_account: a.user_base_token_account,
280 user_quote_token_account: a.user_quote_token_account,
281 pool_base_token_account: a.pool_base_token_account,
282 pool_quote_token_account: a.pool_quote_token_account,
283 protocol_fee_recipient,
284 protocol_fee_recipient_token_account: a.protocol_fee_recipient_token_account,
285 base_token_program,
286 quote_token_program,
287 system_program: system_program::ID,
288 associated_token_program: constants::SPL_ATA_PROGRAM_ID,
289 event_authority: pda::pump_amm::event_authority().0,
290 program: PUMP_AMM_PROGRAM_ID,
291 coin_creator_vault_ata: a.coin_creator_vault_ata,
292 coin_creator_vault_authority: a.coin_creator_vault_authority,
293 fee_config: pda::pump_amm::fee_config().0,
294 fee_program: constants::FEE_PROGRAM_ID,
295 };
296 let args = amm_client::args::Sell {
297 base_amount_in,
298 min_quote_amount_out,
299 };
300 let mut metas = accounts.to_account_metas(None);
301 if is_cashback_coin {
302 metas.push(AccountMeta::new(
303 pda::associated_token(
304 &a.user_volume_accumulator,
305 "e_token_program,
306 "e_mint,
307 )
308 .0,
309 false,
310 ));
311 metas.push(AccountMeta::new(a.user_volume_accumulator, false));
312 }
313 if coin_creator != Pubkey::default() {
314 metas.push(AccountMeta::new_readonly(
315 pda::pump_amm::pool_v2(&base_mint).0,
316 false,
317 ));
318 }
319 metas.push(AccountMeta::new_readonly(buyback_fee_recipient, false));
320 metas.push(AccountMeta::new(
321 pda::associated_token(&buyback_fee_recipient, "e_token_program, "e_mint).0,
322 false,
323 ));
324 Instruction {
325 program_id: PUMP_AMM_PROGRAM_ID,
326 accounts: metas,
327 data: args.data(),
328 }
329 }
330
331 pub fn sell_amm_instructions(
333 &self,
334 pool: Pubkey,
335 amm_global: &GlobalConfig,
336 pool_state: &Pool,
337 base_token_program: Pubkey,
338 quote_token_program: Pubkey,
339 user: Pubkey,
340 base_amount_in: u64,
341 min_quote_amount_out: u64,
342 ) -> Option<Vec<Instruction>> {
343 let sell = self.sell_amm_instruction(
344 pool,
345 amm_global,
346 pool_state,
347 base_token_program,
348 quote_token_program,
349 user,
350 base_amount_in,
351 min_quote_amount_out,
352 )?;
353 Some(vec![
354 create_associated_token_account_idempotent(
355 &user,
356 &user,
357 &pool_state.quote_mint,
358 "e_token_program,
359 ),
360 sell,
361 ])
362 }
363
364 pub fn collect_coin_creator_fee_instruction(
368 &self,
369 coin_creator: Pubkey,
370 quote_mint: Pubkey,
371 quote_token_program: Pubkey,
372 ) -> Instruction {
373 let coin_creator_vault_authority =
374 pda::pump_amm::coin_creator_vault_authority(&coin_creator).0;
375 let coin_creator_vault_ata = pda::associated_token(
376 &coin_creator_vault_authority,
377 "e_token_program,
378 "e_mint,
379 )
380 .0;
381 let coin_creator_token_account =
382 pda::associated_token(&coin_creator, "e_token_program, "e_mint).0;
383 let accounts = amm_client::accounts::CollectCoinCreatorFee {
384 quote_mint,
385 quote_token_program,
386 coin_creator,
387 coin_creator_vault_authority,
388 coin_creator_vault_ata,
389 coin_creator_token_account,
390 event_authority: pda::pump_amm::event_authority().0,
391 program: PUMP_AMM_PROGRAM_ID,
392 };
393 Instruction {
394 program_id: PUMP_AMM_PROGRAM_ID,
395 accounts: accounts.to_account_metas(None),
396 data: amm_client::args::CollectCoinCreatorFee.data(),
397 }
398 }
399
400 pub fn collect_coin_creator_fee_instructions(
404 &self,
405 payer: Pubkey,
406 coin_creator: Pubkey,
407 quote_mint: Pubkey,
408 quote_token_program: Pubkey,
409 create_coin_creator_ata: bool,
410 ) -> Vec<Instruction> {
411 let mut ixs = Vec::with_capacity(2);
412 if create_coin_creator_ata {
413 ixs.push(create_associated_token_account_idempotent(
414 &payer,
415 &coin_creator,
416 "e_mint,
417 "e_token_program,
418 ));
419 }
420 ixs.push(self.collect_coin_creator_fee_instruction(
421 coin_creator,
422 quote_mint,
423 quote_token_program,
424 ));
425 ixs
426 }
427
428 pub fn transfer_creator_fees_to_pump_v2_instruction(
429 &self,
430 payer: Pubkey,
431 coin_creator: Pubkey,
432 quote_mint: Pubkey,
433 token_program: Pubkey,
434 ) -> Instruction {
435 let coin_creator_vault_authority =
436 pda::pump_amm::coin_creator_vault_authority(&coin_creator).0;
437 let coin_creator_vault_ata =
438 pda::associated_token(&coin_creator_vault_authority, &token_program, "e_mint).0;
439 let pump_creator_vault = pda::pump::creator_vault(&coin_creator).0;
440 let pump_creator_vault_ata =
441 pda::associated_token(&pump_creator_vault, &token_program, "e_mint).0;
442 let accounts = amm_client::accounts::TransferCreatorFeesToPumpV2 {
443 payer,
444 quote_mint,
445 token_program,
446 system_program: system_program::ID,
447 associated_token_program: constants::SPL_ATA_PROGRAM_ID,
448 coin_creator,
449 coin_creator_vault_authority,
450 coin_creator_vault_ata,
451 pump_creator_vault,
452 pump_creator_vault_ata,
453 event_authority: pda::pump_amm::event_authority().0,
454 program: PUMP_AMM_PROGRAM_ID,
455 };
456 Instruction {
457 program_id: PUMP_AMM_PROGRAM_ID,
458 accounts: accounts.to_account_metas(None),
459 data: amm_client::args::TransferCreatorFeesToPumpV2.data(),
460 }
461 }
462}