Skip to main content

pump_rust_client/sdk/
pump_v2.rs

1use anchor_lang::{InstructionData, ToAccountMetas};
2use solana_program::{
3    instruction::{AccountMeta, Instruction},
4    pubkey::Pubkey,
5    system_program,
6};
7
8use crate::state::{BondingCurve, BondingCurveFromIdl, Global};
9use crate::token::create_associated_token_account_idempotent;
10use crate::{
11    constants, pda, pump::client, pump::types::OptionBool,
12    pump_agent_payments::client as agent_client,
13};
14
15use super::{CreateCoinParams, PumpSdk};
16
17/// Pubkeys derived once and threaded into both `BuyV2` and `SellV2` (the two
18/// instructions share the same account set apart from `BuyV2`'s extra
19/// `global_volume_accumulator`). `quote_mint` is the resolved value
20/// (default → wSOL); `base_token_program` is fixed for v2 (Token-2022).
21struct V2TradeAccounts {
22    quote_mint: Pubkey,
23    base_token_program: Pubkey,
24    bonding_curve: Pubkey,
25    creator_vault: Pubkey,
26    user_volume_accumulator: Pubkey,
27    associated_quote_fee_recipient: Pubkey,
28    associated_quote_buyback_fee_recipient: Pubkey,
29    associated_base_bonding_curve: Pubkey,
30    associated_quote_bonding_curve: Pubkey,
31    associated_base_user: Pubkey,
32    associated_quote_user: Pubkey,
33    associated_creator_vault: Pubkey,
34    associated_user_volume_accumulator: Pubkey,
35}
36
37impl V2TradeAccounts {
38    fn derive(
39        base_mint: Pubkey,
40        quote_mint: Pubkey,
41        quote_token_program: Pubkey,
42        user: Pubkey,
43        creator: Pubkey,
44        fee_recipient: Pubkey,
45        buyback_fee_recipient: Pubkey,
46    ) -> Self {
47        let base_token_program = constants::SPL_TOKEN_2022_PROGRAM_ID;
48        let quote_mint = if quote_mint == Pubkey::default() {
49            constants::NATIVE_MINT
50        } else {
51            quote_mint
52        };
53        let bonding_curve = pda::pump::bonding_curve(&base_mint).0;
54        let creator_vault = pda::pump::creator_vault(&creator).0;
55        let user_volume_accumulator = pda::pump::user_volume_accumulator(&user).0;
56        let ata = |owner: &Pubkey, token_program: &Pubkey, mint: &Pubkey| {
57            pda::associated_token(owner, token_program, mint).0
58        };
59        Self {
60            quote_mint,
61            base_token_program,
62            bonding_curve,
63            creator_vault,
64            user_volume_accumulator,
65            associated_quote_fee_recipient: ata(&fee_recipient, &quote_token_program, &quote_mint),
66            associated_quote_buyback_fee_recipient: ata(
67                &buyback_fee_recipient,
68                &quote_token_program,
69                &quote_mint,
70            ),
71            associated_base_bonding_curve: ata(&bonding_curve, &base_token_program, &base_mint),
72            associated_quote_bonding_curve: ata(&bonding_curve, &quote_token_program, &quote_mint),
73            associated_base_user: ata(&user, &base_token_program, &base_mint),
74            associated_quote_user: ata(&user, &quote_token_program, &quote_mint),
75            associated_creator_vault: ata(&creator_vault, &quote_token_program, &quote_mint),
76            associated_user_volume_accumulator: ata(
77                &user_volume_accumulator,
78                &quote_token_program,
79                &quote_mint,
80            ),
81        }
82    }
83}
84
85impl PumpSdk {
86    /// `create_v2` (Token-2022, Mayhem PDAs).
87    ///
88    /// `quote_mint` selects the curve's quote asset. Pass `Pubkey::default()`
89    /// or [`constants::NATIVE_MINT`] for the standard wSOL curve. Any other
90    /// mint (e.g. USDC) is treated as a non-SOL quote and three remaining
91    /// accounts are appended (quote mint, the legacy-SPL ATA owned by the
92    /// bonding curve, and the legacy SPL Token program).
93    pub fn create_v2_instruction(
94        &self,
95        mint: Pubkey,
96        user: Pubkey,
97        name: impl Into<String>,
98        symbol: impl Into<String>,
99        uri: impl Into<String>,
100        creator: Pubkey,
101        quote_mint: Pubkey,
102        mayhem_mode: bool,
103        cashback: bool,
104    ) -> Instruction {
105        let token_program = constants::SPL_TOKEN_2022_PROGRAM_ID;
106        let bonding_curve = pda::pump::bonding_curve(&mint).0;
107        let accounts = client::accounts::CreateV2 {
108            mint,
109            mint_authority: pda::pump::mint_authority().0,
110            bonding_curve,
111            associated_bonding_curve: pda::associated_token(&bonding_curve, &token_program, &mint)
112                .0,
113            global: pda::pump::global().0,
114            user,
115            system_program: system_program::ID,
116            token_program,
117            associated_token_program: constants::SPL_ATA_PROGRAM_ID,
118            mayhem_program_id: constants::MAYHEM_PROGRAM_ID,
119            global_params: pda::mayhem::global_params().0,
120            sol_vault: pda::mayhem::sol_vault().0,
121            mayhem_state: pda::mayhem::mayhem_state(&mint).0,
122            mayhem_token_vault: pda::mayhem::mayhem_token_vault(&mint).0,
123            event_authority: pda::pump::event_authority().0,
124            program: crate::pump::ID,
125        };
126        let args = client::args::CreateV2 {
127            name: name.into(),
128            symbol: symbol.into(),
129            uri: uri.into(),
130            creator,
131            is_mayhem_mode: mayhem_mode,
132            is_cashback_enabled: OptionBool(cashback),
133        };
134        let mut metas = accounts.to_account_metas(None);
135        let resolved_quote_mint = if quote_mint == Pubkey::default() {
136            constants::NATIVE_MINT
137        } else {
138            quote_mint
139        };
140        if resolved_quote_mint != constants::NATIVE_MINT {
141            let quote_token_program = constants::SPL_TOKEN_PROGRAM_ID;
142            let associated_quote_bonding_curve =
143                pda::associated_token(&bonding_curve, &quote_token_program, &resolved_quote_mint).0;
144            metas.push(AccountMeta::new_readonly(resolved_quote_mint, false));
145            metas.push(AccountMeta::new(associated_quote_bonding_curve, false));
146            metas.push(AccountMeta::new_readonly(quote_token_program, false));
147        }
148        Instruction {
149            program_id: crate::pump::ID,
150            accounts: metas,
151            data: args.data(),
152        }
153    }
154
155    /// `buy_v2` with fee recipients from [`Global`] and quote layout from [`BondingCurve`].
156    pub fn buy_v2_instruction(
157        &self,
158        global: &Global,
159        bonding_curve: &BondingCurve,
160        base_mint: Pubkey,
161        quote_token_program: Pubkey,
162        user: Pubkey,
163        amount: u64,
164        max_quote_tokens: u64,
165    ) -> Option<Instruction> {
166        let (fee_recipient, buyback_fee_recipient) =
167            Self::pump_fee_recipients_pair(global, bonding_curve.is_mayhem_mode)?;
168        Some(self.buy_v2_instruction_with_recipients(
169            base_mint,
170            bonding_curve.quote_mint,
171            quote_token_program,
172            user,
173            bonding_curve.creator,
174            fee_recipient,
175            buyback_fee_recipient,
176            amount,
177            max_quote_tokens,
178        ))
179    }
180
181    pub(crate) fn buy_v2_instruction_with_recipients(
182        &self,
183        base_mint: Pubkey,
184        quote_mint: Pubkey,
185        quote_token_program: Pubkey,
186        user: Pubkey,
187        creator: Pubkey,
188        fee_recipient: Pubkey,
189        buyback_fee_recipient: Pubkey,
190        amount: u64,
191        max_sol_cost: u64,
192    ) -> Instruction {
193        let a = V2TradeAccounts::derive(
194            base_mint,
195            quote_mint,
196            quote_token_program,
197            user,
198            creator,
199            fee_recipient,
200            buyback_fee_recipient,
201        );
202        let accounts = client::accounts::BuyV2 {
203            global: pda::pump::global().0,
204            base_mint,
205            quote_mint: a.quote_mint,
206            base_token_program: a.base_token_program,
207            quote_token_program,
208            associated_token_program: constants::SPL_ATA_PROGRAM_ID,
209            fee_recipient,
210            associated_quote_fee_recipient: a.associated_quote_fee_recipient,
211            buyback_fee_recipient,
212            associated_quote_buyback_fee_recipient: a.associated_quote_buyback_fee_recipient,
213            bonding_curve: a.bonding_curve,
214            associated_base_bonding_curve: a.associated_base_bonding_curve,
215            associated_quote_bonding_curve: a.associated_quote_bonding_curve,
216            user,
217            associated_base_user: a.associated_base_user,
218            associated_quote_user: a.associated_quote_user,
219            creator_vault: a.creator_vault,
220            associated_creator_vault: a.associated_creator_vault,
221            sharing_config: pda::pump::sharing_config(&base_mint).0,
222            global_volume_accumulator: pda::pump::global_volume_accumulator().0,
223            user_volume_accumulator: a.user_volume_accumulator,
224            associated_user_volume_accumulator: a.associated_user_volume_accumulator,
225            fee_config: pda::pump::fee_config().0,
226            fee_program: constants::FEE_PROGRAM_ID,
227            system_program: system_program::ID,
228            event_authority: pda::pump::event_authority().0,
229            program: crate::pump::ID,
230        };
231        let args = client::args::BuyV2 {
232            amount,
233            max_sol_cost,
234        };
235        Instruction {
236            program_id: crate::pump::ID,
237            accounts: accounts.to_account_metas(None),
238            data: args.data(),
239        }
240    }
241
242    /// `buy_v2` with idempotent ATA creates for base/quote user accounts (skipped when `quote_mint` is the default).
243    pub fn buy_v2_instructions(
244        &self,
245        global: &Global,
246        bonding_curve: &BondingCurve,
247        base_mint: Pubkey,
248        quote_token_program: Pubkey,
249        user: Pubkey,
250        amount: u64,
251        max_quote_tokens: u64,
252    ) -> Option<Vec<Instruction>> {
253        let base_token_program = constants::SPL_TOKEN_2022_PROGRAM_ID;
254        let (fee_recipient, buyback_fee_recipient) =
255            Self::pump_fee_recipients_pair(global, bonding_curve.is_mayhem_mode)?;
256        let quote_mint = bonding_curve.quote_mint;
257        let creator = bonding_curve.creator;
258        let mut instructions = Self::user_trade_atas(
259            user,
260            base_mint,
261            bonding_curve,
262            base_token_program,
263            quote_token_program,
264            true,
265        );
266        instructions.push(self.buy_v2_instruction_with_recipients(
267            base_mint,
268            quote_mint,
269            quote_token_program,
270            user,
271            creator,
272            fee_recipient,
273            buyback_fee_recipient,
274            amount,
275            max_quote_tokens,
276        ));
277        Some(instructions)
278    }
279
280    /// `buy_exact_quote_in_v2`: spend exactly `spendable_quote_in`, requiring at least `min_tokens_out` base tokens out.
281    pub fn buy_exact_quote_in_v2_instruction(
282        &self,
283        global: &Global,
284        bonding_curve: &BondingCurve,
285        base_mint: Pubkey,
286        quote_token_program: Pubkey,
287        user: Pubkey,
288        spendable_quote_in: u64,
289        min_tokens_out: u64,
290    ) -> Option<Instruction> {
291        let (fee_recipient, buyback_fee_recipient) =
292            Self::pump_fee_recipients_pair(global, bonding_curve.is_mayhem_mode)?;
293        Some(self.buy_exact_quote_in_v2_instruction_with_recipients(
294            base_mint,
295            bonding_curve.quote_mint,
296            quote_token_program,
297            user,
298            bonding_curve.creator,
299            fee_recipient,
300            buyback_fee_recipient,
301            spendable_quote_in,
302            min_tokens_out,
303        ))
304    }
305
306    pub(crate) fn buy_exact_quote_in_v2_instruction_with_recipients(
307        &self,
308        base_mint: Pubkey,
309        quote_mint: Pubkey,
310        quote_token_program: Pubkey,
311        user: Pubkey,
312        creator: Pubkey,
313        fee_recipient: Pubkey,
314        buyback_fee_recipient: Pubkey,
315        spendable_quote_in: u64,
316        min_tokens_out: u64,
317    ) -> Instruction {
318        let a = V2TradeAccounts::derive(
319            base_mint,
320            quote_mint,
321            quote_token_program,
322            user,
323            creator,
324            fee_recipient,
325            buyback_fee_recipient,
326        );
327        let accounts = client::accounts::BuyExactQuoteInV2 {
328            global: pda::pump::global().0,
329            base_mint,
330            quote_mint: a.quote_mint,
331            base_token_program: a.base_token_program,
332            quote_token_program,
333            associated_token_program: constants::SPL_ATA_PROGRAM_ID,
334            fee_recipient,
335            associated_quote_fee_recipient: a.associated_quote_fee_recipient,
336            buyback_fee_recipient,
337            associated_quote_buyback_fee_recipient: a.associated_quote_buyback_fee_recipient,
338            bonding_curve: a.bonding_curve,
339            associated_base_bonding_curve: a.associated_base_bonding_curve,
340            associated_quote_bonding_curve: a.associated_quote_bonding_curve,
341            user,
342            associated_base_user: a.associated_base_user,
343            associated_quote_user: a.associated_quote_user,
344            creator_vault: a.creator_vault,
345            associated_creator_vault: a.associated_creator_vault,
346            sharing_config: pda::pump::sharing_config(&base_mint).0,
347            global_volume_accumulator: pda::pump::global_volume_accumulator().0,
348            user_volume_accumulator: a.user_volume_accumulator,
349            associated_user_volume_accumulator: a.associated_user_volume_accumulator,
350            fee_config: pda::pump::fee_config().0,
351            fee_program: constants::FEE_PROGRAM_ID,
352            system_program: system_program::ID,
353            event_authority: pda::pump::event_authority().0,
354            program: crate::pump::ID,
355        };
356        let args = client::args::BuyExactQuoteInV2 {
357            spendable_quote_in,
358            min_tokens_out,
359        };
360        Instruction {
361            program_id: crate::pump::ID,
362            accounts: accounts.to_account_metas(None),
363            data: args.data(),
364        }
365    }
366
367    /// `buy_exact_quote_in_v2` with idempotent ATA creates for base/quote user accounts (skipped when `quote_mint` is the default).
368    pub fn buy_exact_quote_in_v2_instructions(
369        &self,
370        global: &Global,
371        bonding_curve: &BondingCurve,
372        base_mint: Pubkey,
373        quote_token_program: Pubkey,
374        user: Pubkey,
375        spendable_quote_in: u64,
376        min_tokens_out: u64,
377    ) -> Option<Vec<Instruction>> {
378        let base_token_program = constants::SPL_TOKEN_2022_PROGRAM_ID;
379        let (fee_recipient, buyback_fee_recipient) =
380            Self::pump_fee_recipients_pair(global, bonding_curve.is_mayhem_mode)?;
381        let quote_mint = bonding_curve.quote_mint;
382        let creator = bonding_curve.creator;
383        let mut instructions = Self::user_trade_atas(
384            user,
385            base_mint,
386            bonding_curve,
387            base_token_program,
388            quote_token_program,
389            true,
390        );
391        instructions.push(self.buy_exact_quote_in_v2_instruction_with_recipients(
392            base_mint,
393            quote_mint,
394            quote_token_program,
395            user,
396            creator,
397            fee_recipient,
398            buyback_fee_recipient,
399            spendable_quote_in,
400            min_tokens_out,
401        ));
402        Some(instructions)
403    }
404
405    /// `sell_v2`; fee recipients from [`Global`], quote accounts from [`BondingCurve`].
406    pub fn sell_v2_instruction(
407        &self,
408        global: &Global,
409        bonding_curve: &BondingCurve,
410        base_mint: Pubkey,
411        quote_token_program: Pubkey,
412        user: Pubkey,
413        amount: u64,
414        min_sol_output: u64,
415    ) -> Option<Instruction> {
416        let (fee_recipient, buyback_fee_recipient) =
417            Self::pump_fee_recipients_pair(global, bonding_curve.is_mayhem_mode)?;
418        Some(self.sell_v2_instruction_with_recipients(
419            base_mint,
420            bonding_curve.quote_mint,
421            quote_token_program,
422            user,
423            bonding_curve.creator,
424            fee_recipient,
425            buyback_fee_recipient,
426            amount,
427            min_sol_output,
428        ))
429    }
430
431    pub(crate) fn sell_v2_instruction_with_recipients(
432        &self,
433        base_mint: Pubkey,
434        quote_mint: Pubkey,
435        quote_token_program: Pubkey,
436        user: Pubkey,
437        creator: Pubkey,
438        fee_recipient: Pubkey,
439        buyback_fee_recipient: Pubkey,
440        amount: u64,
441        min_sol_output: u64,
442    ) -> Instruction {
443        let a = V2TradeAccounts::derive(
444            base_mint,
445            quote_mint,
446            quote_token_program,
447            user,
448            creator,
449            fee_recipient,
450            buyback_fee_recipient,
451        );
452        let accounts = client::accounts::SellV2 {
453            global: pda::pump::global().0,
454            base_mint,
455            quote_mint: a.quote_mint,
456            base_token_program: a.base_token_program,
457            quote_token_program,
458            associated_token_program: constants::SPL_ATA_PROGRAM_ID,
459            fee_recipient,
460            associated_quote_fee_recipient: a.associated_quote_fee_recipient,
461            buyback_fee_recipient,
462            associated_quote_buyback_fee_recipient: a.associated_quote_buyback_fee_recipient,
463            bonding_curve: a.bonding_curve,
464            associated_base_bonding_curve: a.associated_base_bonding_curve,
465            associated_quote_bonding_curve: a.associated_quote_bonding_curve,
466            user,
467            associated_base_user: a.associated_base_user,
468            associated_quote_user: a.associated_quote_user,
469            creator_vault: a.creator_vault,
470            associated_creator_vault: a.associated_creator_vault,
471            sharing_config: pda::pump::sharing_config(&base_mint).0,
472            user_volume_accumulator: a.user_volume_accumulator,
473            associated_user_volume_accumulator: a.associated_user_volume_accumulator,
474            fee_config: pda::pump::fee_config().0,
475            fee_program: constants::FEE_PROGRAM_ID,
476            system_program: system_program::ID,
477            event_authority: pda::pump::event_authority().0,
478            program: crate::pump::ID,
479        };
480        let args = client::args::SellV2 {
481            amount,
482            min_sol_output,
483        };
484        Instruction {
485            program_id: crate::pump::ID,
486            accounts: accounts.to_account_metas(None),
487            data: args.data(),
488        }
489    }
490
491    /// `sell_v2` with idempotent ATA creates for base/quote user accounts (skipped when `quote_mint` is the default).
492    pub fn sell_v2_instructions(
493        &self,
494        global: &Global,
495        bonding_curve: &BondingCurve,
496        base_mint: Pubkey,
497        quote_token_program: Pubkey,
498        user: Pubkey,
499        amount: u64,
500        min_sol_output: u64,
501    ) -> Option<Vec<Instruction>> {
502        let (fee_recipient, buyback_fee_recipient) =
503            Self::pump_fee_recipients_pair(global, bonding_curve.is_mayhem_mode)?;
504        let quote_mint = bonding_curve.quote_mint;
505        let creator = bonding_curve.creator;
506        let mut instructions = Self::user_trade_atas(
507            user,
508            base_mint,
509            bonding_curve,
510            constants::SPL_TOKEN_2022_PROGRAM_ID,
511            quote_token_program,
512            false,
513        );
514        instructions.push(self.sell_v2_instruction_with_recipients(
515            base_mint,
516            quote_mint,
517            quote_token_program,
518            user,
519            creator,
520            fee_recipient,
521            buyback_fee_recipient,
522            amount,
523            min_sol_output,
524        ));
525        Some(instructions)
526    }
527
528    /// `create_v2` then [`Self::buy_v2_instructions`]. Pass
529    /// `quote_mint = Pubkey::default()` for a wSOL-quoted coin, or a
530    /// supported quote mint (e.g. USDC) for a non-native quote.
531    /// Pass `tokenized_agent_buyback_bps = Some(bps)` to also append
532    /// [`Self::agent_initialize_instruction`] in the same tx (mirrors the
533    /// frontend's tokenized-agent flow).
534    pub fn create_v2_and_buy_instruction(
535        &self,
536        mint: Pubkey,
537        user: Pubkey,
538        name: impl Into<String>,
539        symbol: impl Into<String>,
540        uri: impl Into<String>,
541        creator: Pubkey,
542        quote_mint: Pubkey,
543        mayhem_mode: bool,
544        cashback: bool,
545        tokenized_agent_buyback_bps: Option<u16>,
546        global: &Global,
547        amount: u64,
548        max_quote_tokens: u64,
549    ) -> Option<Vec<Instruction>> {
550        let quote_token_program = constants::SPL_TOKEN_PROGRAM_ID;
551        let resolved_quote_mint = if quote_mint == Pubkey::default() {
552            constants::NATIVE_MINT
553        } else {
554            quote_mint
555        };
556        let bonding_curve_preview = BondingCurve::new(BondingCurveFromIdl {
557            creator,
558            is_cashback_coin: cashback,
559            is_mayhem_mode: mayhem_mode,
560            quote_mint: resolved_quote_mint,
561            ..Default::default()
562        });
563        let buy_v2_instructions = self.buy_v2_instructions(
564            global,
565            &bonding_curve_preview,
566            mint,
567            quote_token_program,
568            user,
569            amount,
570            max_quote_tokens,
571        )?;
572        let mut instructions = vec![self.create_v2_instruction(
573            mint,
574            user,
575            name,
576            symbol,
577            uri,
578            creator,
579            quote_mint,
580            mayhem_mode,
581            cashback,
582        )];
583
584        instructions.extend(buy_v2_instructions);
585
586        if let Some(buyback_bps) = tokenized_agent_buyback_bps {
587            instructions.push(self.agent_initialize_instruction(mint, user, creator, buyback_bps));
588        }
589
590        Some(instructions)
591    }
592
593    /// `agent_initialize` (pump_agent_payments). Initializes the
594    /// `TokenAgentPayments` PDA for `mint` with `creator` as the agent
595    /// payment authority. `user` signs and pays for the new account.
596    pub fn agent_initialize_instruction(
597        &self,
598        mint: Pubkey,
599        user: Pubkey,
600        creator: Pubkey,
601        buyback_bps: u16,
602    ) -> Instruction {
603        let accounts = agent_client::accounts::AgentInitialize {
604            authority: user,
605            bonding_curve: pda::pump::bonding_curve(&mint).0,
606            global_config: pda::pump_agent_payments::global_config().0,
607            mint,
608            token_agent_payments: pda::pump_agent_payments::token_agent_payments(&mint).0,
609            system_program: system_program::ID,
610            event_authority: pda::pump_agent_payments::event_authority().0,
611            program: crate::pump_agent_payments::ID,
612            sharing_fee_config: pda::pump::sharing_config(&mint).0,
613        };
614        let args = agent_client::args::AgentInitialize {
615            authority: creator,
616            buyback_bps,
617        };
618        Instruction {
619            program_id: crate::pump_agent_payments::ID,
620            accounts: accounts.to_account_metas(None),
621            data: args.data(),
622        }
623    }
624
625    pub fn extend_account_ix(&self, mint: Pubkey, user: Pubkey) -> Instruction {
626        let accounts = client::accounts::ExtendAccount {
627            account: pda::pump::bonding_curve(&mint).0,
628            user,
629            system_program: system_program::ID,
630            event_authority: pda::pump::event_authority().0,
631            program: crate::pump::ID,
632        };
633        Instruction {
634            program_id: crate::pump::ID,
635            accounts: accounts.to_account_metas(None),
636            data: client::args::ExtendAccount.data(),
637        }
638    }
639
640    pub fn claim_cashback_v2_instruction(
641        &self,
642        user: Pubkey,
643        quote_mint: Pubkey,
644        quote_token_program: Pubkey,
645    ) -> Instruction {
646        let resolved_quote_mint = if quote_mint == Pubkey::default() {
647            constants::NATIVE_MINT
648        } else {
649            quote_mint
650        };
651        let user_volume_accumulator = pda::pump::user_volume_accumulator(&user).0;
652        let associated_user_volume_accumulator = pda::associated_token(
653            &user_volume_accumulator,
654            &quote_token_program,
655            &resolved_quote_mint,
656        )
657        .0;
658        let associated_quote_user =
659            pda::associated_token(&user, &quote_token_program, &resolved_quote_mint).0;
660        let accounts = client::accounts::ClaimCashbackV2 {
661            user,
662            user_volume_accumulator,
663            quote_mint: resolved_quote_mint,
664            quote_token_program,
665            associated_token_program: constants::SPL_ATA_PROGRAM_ID,
666            associated_user_volume_accumulator,
667            associated_quote_user,
668            system_program: system_program::ID,
669            event_authority: pda::pump::event_authority().0,
670            program: crate::pump::ID,
671        };
672        Instruction {
673            program_id: crate::pump::ID,
674            accounts: accounts.to_account_metas(None),
675            data: client::args::ClaimCashbackV2.data(),
676        }
677    }
678
679    pub fn collect_creator_fee_v2_instructions(
680        &self,
681        payer: Pubkey,
682        creator: Pubkey,
683        quote_mint: Pubkey,
684        quote_token_program: Pubkey,
685        create_creator_ata: bool,
686    ) -> Vec<Instruction> {
687        let resolved_quote_mint = if quote_mint == Pubkey::default() {
688            constants::NATIVE_MINT
689        } else {
690            quote_mint
691        };
692        let mut ixs = Vec::with_capacity(2);
693        if create_creator_ata {
694            ixs.push(create_associated_token_account_idempotent(
695                &payer,
696                &creator,
697                &resolved_quote_mint,
698                &quote_token_program,
699            ));
700        }
701        ixs.push(self.collect_creator_fee_v2_instruction(
702            creator,
703            resolved_quote_mint,
704            quote_token_program,
705        ));
706        ixs
707    }
708
709    pub fn collect_creator_fee_v2_instruction(
710        &self,
711        creator: Pubkey,
712        quote_mint: Pubkey,
713        quote_token_program: Pubkey,
714    ) -> Instruction {
715        let resolved_quote_mint = if quote_mint == Pubkey::default() {
716            constants::NATIVE_MINT
717        } else {
718            quote_mint
719        };
720        let creator_vault = pda::pump::creator_vault(&creator).0;
721        let creator_token_account =
722            pda::associated_token(&creator, &quote_token_program, &resolved_quote_mint).0;
723        let creator_vault_token_account =
724            pda::associated_token(&creator_vault, &quote_token_program, &resolved_quote_mint).0;
725        let accounts = client::accounts::CollectCreatorFeeV2 {
726            creator,
727            creator_token_account,
728            creator_vault,
729            creator_vault_token_account,
730            quote_mint: resolved_quote_mint,
731            quote_token_program,
732            associated_token_program: constants::SPL_ATA_PROGRAM_ID,
733            system_program: system_program::ID,
734            event_authority: pda::pump::event_authority().0,
735            program: crate::pump::ID,
736        };
737        Instruction {
738            program_id: crate::pump::ID,
739            accounts: accounts.to_account_metas(None),
740            data: client::args::CollectCreatorFeeV2.data(),
741        }
742    }
743
744    pub fn distribute_creator_fees_v2_instructions(
745        &self,
746        payer: Pubkey,
747        mint: Pubkey,
748        creator: Pubkey,
749        quote_mint: Pubkey,
750        quote_token_program: Pubkey,
751        initialize_ata: bool,
752        create_creator_vault_ata: bool,
753        shareholders: &[Pubkey],
754    ) -> Vec<Instruction> {
755        let resolved_quote_mint = if quote_mint == Pubkey::default() {
756            constants::NATIVE_MINT
757        } else {
758            quote_mint
759        };
760        let mut ixs = Vec::with_capacity(2 + shareholders.len());
761        if create_creator_vault_ata {
762            let creator_vault = pda::pump::creator_vault(&creator).0;
763            ixs.push(create_associated_token_account_idempotent(
764                &payer,
765                &creator_vault,
766                &resolved_quote_mint,
767                &quote_token_program,
768            ));
769        }
770        for sh in shareholders {
771            ixs.push(create_associated_token_account_idempotent(
772                &payer,
773                sh,
774                &resolved_quote_mint,
775                &quote_token_program,
776            ));
777        }
778        ixs.push(self.distribute_creator_fees_v2_instruction(
779            payer,
780            mint,
781            creator,
782            resolved_quote_mint,
783            quote_token_program,
784            initialize_ata,
785            shareholders,
786        ));
787        ixs
788    }
789
790    pub fn distribute_creator_fees_v2_instruction(
791        &self,
792        payer: Pubkey,
793        mint: Pubkey,
794        creator: Pubkey,
795        quote_mint: Pubkey,
796        quote_token_program: Pubkey,
797        initialize_ata: bool,
798        shareholders: &[Pubkey],
799    ) -> Instruction {
800        let resolved_quote_mint = if quote_mint == Pubkey::default() {
801            constants::NATIVE_MINT
802        } else {
803            quote_mint
804        };
805        let bonding_curve = pda::pump::bonding_curve(&mint).0;
806        let sharing_config = pda::pump::sharing_config(&mint).0;
807        let creator_vault = pda::pump::creator_vault(&creator).0;
808        let creator_vault_quote_token_account =
809            pda::associated_token(&creator_vault, &quote_token_program, &resolved_quote_mint).0;
810        let accounts = client::accounts::DistributeCreatorFeesV2 {
811            payer,
812            mint,
813            bonding_curve,
814            sharing_config,
815            creator_vault,
816            system_program: system_program::ID,
817            event_authority: pda::pump::event_authority().0,
818            program: crate::pump::ID,
819            creator_vault_quote_token_account,
820            quote_mint: resolved_quote_mint,
821            quote_token_program,
822            associated_token_program: constants::SPL_ATA_PROGRAM_ID,
823        };
824        let mut metas = accounts.to_account_metas(None);
825        for sh in shareholders {
826            metas.push(AccountMeta::new(*sh, false));
827        }
828        if resolved_quote_mint != constants::NATIVE_MINT {
829            for sh in shareholders {
830                let sh_ata =
831                    pda::associated_token(sh, &quote_token_program, &resolved_quote_mint).0;
832                metas.push(AccountMeta::new(sh_ata, false));
833            }
834        }
835        Instruction {
836            program_id: crate::pump::ID,
837            accounts: metas,
838            data: client::args::DistributeCreatorFeesV2 { initialize_ata }.data(),
839        }
840    }
841
842    /// `create_v2` then [`Self::buy_v2_instructions`]. Set
843    /// `params.quote_mint = Pubkey::default()` for a wSOL-quoted coin, or a
844    /// supported quote mint (e.g. USDC) for a non-native quote.
845    /// Setting `params.tokenized_agent_buyback_bps = Some(bps)` also appends
846    /// [`Self::agent_initialize_instruction`].
847    pub fn create_coin_instructions(
848        &self,
849        params: CreateCoinParams<'_>,
850    ) -> Option<Vec<Instruction>> {
851        let CreateCoinParams {
852            mint,
853            user,
854            creator,
855            name,
856            symbol,
857            uri,
858            mayhem_mode,
859            cashback,
860            quote_mint,
861            global,
862            token_amount,
863            max_quote_tokens,
864            tokenized_agent_buyback_bps,
865        } = params;
866        let quote_token_program = constants::SPL_TOKEN_PROGRAM_ID;
867        let resolved_quote_mint = if quote_mint == Pubkey::default() {
868            constants::NATIVE_MINT
869        } else {
870            quote_mint
871        };
872        let bonding_curve_preview = BondingCurve::new(BondingCurveFromIdl {
873            creator,
874            is_cashback_coin: cashback,
875            is_mayhem_mode: mayhem_mode,
876            quote_mint: resolved_quote_mint,
877            ..Default::default()
878        });
879
880        let mut ixs: Vec<Instruction> = vec![self.create_v2_instruction(
881            mint,
882            user,
883            name,
884            symbol,
885            uri,
886            creator,
887            quote_mint,
888            mayhem_mode,
889            cashback,
890        )];
891        ixs.extend(self.buy_v2_instructions(
892            global,
893            &bonding_curve_preview,
894            mint,
895            quote_token_program,
896            user,
897            token_amount,
898            max_quote_tokens,
899        )?);
900        if let Some(buyback_bps) = tokenized_agent_buyback_bps {
901            ixs.push(self.agent_initialize_instruction(mint, user, creator, buyback_bps));
902        }
903        Some(ixs)
904    }
905
906    /// Idempotent ATA creates for the user's base + quote token accounts plus
907    /// the two PDA-owned quote ATAs (`associated_creator_vault` and
908    /// `associated_user_volume_accumulator`) that v2 buy/sell reference. The
909    /// base ATA is skipped when `include_base` is `false` (sell-side flows
910    /// where the user already holds the base balance to spend). A default
911    /// `bonding_curve.quote_mint` resolves to wSOL to match
912    /// [`V2TradeAccounts::derive`].
913    fn user_trade_atas(
914        user: Pubkey,
915        base_mint: Pubkey,
916        bonding_curve: &BondingCurve,
917        base_token_program: Pubkey,
918        quote_token_program: Pubkey,
919        include_base: bool,
920    ) -> Vec<Instruction> {
921        let mut ixs = vec![];
922        if include_base {
923            ixs.push(create_associated_token_account_idempotent(
924                &user,
925                &user,
926                &base_mint,
927                &base_token_program,
928            ));
929        }
930        let bonding_curve_has_non_sol_quote = bonding_curve.quote_mint != Pubkey::default()
931            && bonding_curve.quote_mint != constants::NATIVE_MINT;
932        // associated_quote_fee_recipient: Has alraedy been initialized, we will initalize this during initial testing.
933        // associated_quote_buyback_fee_recipient: Has alraedy been initialized, we will initalize this during initial testing.
934        if bonding_curve_has_non_sol_quote {
935            ixs.push(create_associated_token_account_idempotent(
936                &user,
937                &user,
938                &bonding_curve.quote_mint,
939                &quote_token_program,
940            ));
941            ixs.push(create_associated_token_account_idempotent(
942                &user,
943                &bonding_curve.creator,
944                &bonding_curve.quote_mint,
945                &quote_token_program,
946            ));
947            // associated_quote_bonding_curve
948            let bonding_curve_pda = pda::pump::bonding_curve(&base_mint).0;
949            ixs.push(create_associated_token_account_idempotent(
950                &user,
951                &bonding_curve_pda,
952                &bonding_curve.quote_mint,
953                &quote_token_program,
954            ));
955            if bonding_curve.is_cashback_coin {
956                let user_volume_accumulator = pda::pump::user_volume_accumulator(&user).0;
957                ixs.push(create_associated_token_account_idempotent(
958                    &user,
959                    &user_volume_accumulator,
960                    &bonding_curve.quote_mint,
961                    &quote_token_program,
962                ));
963            }
964        }
965        ixs
966    }
967}