light_token/
spl_interface.rs

1//! SPL interface PDA derivation utilities.
2//!
3//! Re-exports from `light_token_interface` with convenience wrappers.
4
5use light_token_interface::{
6    discriminator::{ADD_TOKEN_POOL, CREATE_TOKEN_POOL},
7    CPI_AUTHORITY, LIGHT_TOKEN_PROGRAM_ID,
8};
9// Re-export derivation functions from token-interface
10pub use light_token_interface::{
11    find_spl_interface_pda, find_spl_interface_pda_with_index, get_spl_interface_pda,
12    has_restricted_extensions, is_valid_spl_interface_pda, NUM_MAX_POOL_ACCOUNTS,
13};
14use solana_instruction::{AccountMeta, Instruction};
15use solana_pubkey::Pubkey;
16
17use crate::{AnchorDeserialize, AnchorSerialize};
18
19#[derive(Debug, Clone, AnchorDeserialize, AnchorSerialize, PartialEq)]
20pub struct SplInterfacePda {
21    pub pubkey: Pubkey,
22    pub bump: u8,
23    pub index: u8,
24}
25
26/// Derive spl interface pda information for a given mint
27pub fn derive_spl_interface_pda(mint: &Pubkey, index: u8, restricted: bool) -> SplInterfacePda {
28    let (pubkey, bump) = find_spl_interface_pda_with_index(mint, index, restricted);
29    SplInterfacePda {
30        pubkey,
31        bump,
32        index,
33    }
34}
35
36/// # Create SPL interface PDA (token pool) instruction builder
37///
38/// Creates or adds an spl interface pda for an SPL mint.
39/// Spl interface pdas store spl tokens that are wrapped in ctoken or compressed token accounts.
40///
41/// ```rust
42/// # use solana_pubkey::Pubkey;
43/// # use light_token::spl_interface::CreateSplInterfacePda;
44/// # use light_token::constants::SPL_TOKEN_PROGRAM_ID;
45/// # let fee_payer = Pubkey::new_unique();
46/// # let mint = Pubkey::new_unique();
47/// # let token_program = SPL_TOKEN_PROGRAM_ID;
48/// // Create initial pool (index 0)
49/// let instruction = CreateSplInterfacePda::new(fee_payer, mint, token_program, false)
50///     .instruction();
51/// // Add additional pool (index 1)
52/// let instruction = CreateSplInterfacePda::new_with_index(fee_payer, mint, token_program, 1, false)
53///     .instruction();
54/// ```
55pub struct CreateSplInterfacePda {
56    pub fee_payer: Pubkey,
57    pub mint: Pubkey,
58    pub token_program: Pubkey,
59    pub spl_interface_pda: Pubkey,
60    pub existing_spl_interface_pda: Option<Pubkey>,
61    pub index: u8,
62}
63
64impl CreateSplInterfacePda {
65    /// Derives the spl interface pda for an SPL mint with index 0.
66    pub fn new(fee_payer: Pubkey, mint: Pubkey, token_program: Pubkey, restricted: bool) -> Self {
67        Self::new_with_index(fee_payer, mint, token_program, 0, restricted)
68    }
69
70    /// Derives the spl interface pda for an SPL mint with a specific index.
71    /// For index 0, creates the initial pool. For index > 0, adds an additional pool.
72    pub fn new_with_index(
73        fee_payer: Pubkey,
74        mint: Pubkey,
75        token_program: Pubkey,
76        index: u8,
77        restricted: bool,
78    ) -> Self {
79        let (spl_interface_pda, _) = find_spl_interface_pda_with_index(&mint, index, restricted);
80        let existing_spl_interface_pda = if index > 0 {
81            let (existing_pda, _) =
82                find_spl_interface_pda_with_index(&mint, index.saturating_sub(1), restricted);
83            Some(existing_pda)
84        } else {
85            None
86        };
87        Self {
88            fee_payer,
89            mint,
90            token_program,
91            spl_interface_pda,
92            existing_spl_interface_pda,
93            index,
94        }
95    }
96
97    pub fn instruction(self) -> Instruction {
98        let cpi_authority = Pubkey::from(CPI_AUTHORITY);
99
100        if self.index == 0 {
101            // CreateTokenPool instruction
102            Instruction {
103                program_id: Pubkey::from(LIGHT_TOKEN_PROGRAM_ID),
104                accounts: vec![
105                    AccountMeta::new(self.fee_payer, true),
106                    AccountMeta::new(self.spl_interface_pda, false),
107                    AccountMeta::new_readonly(Pubkey::default(), false), // system_program
108                    AccountMeta::new(self.mint, false),
109                    AccountMeta::new_readonly(self.token_program, false),
110                    AccountMeta::new_readonly(cpi_authority, false),
111                ],
112                data: CREATE_TOKEN_POOL.to_vec(),
113            }
114        } else {
115            // AddTokenPool instruction
116            let mut data = ADD_TOKEN_POOL.to_vec();
117            data.push(self.index);
118            Instruction {
119                program_id: Pubkey::from(LIGHT_TOKEN_PROGRAM_ID),
120                accounts: vec![
121                    AccountMeta::new(self.fee_payer, true),
122                    AccountMeta::new(self.spl_interface_pda, false),
123                    AccountMeta::new_readonly(self.existing_spl_interface_pda.unwrap(), false),
124                    AccountMeta::new_readonly(Pubkey::default(), false), // system_program
125                    AccountMeta::new(self.mint, false),
126                    AccountMeta::new_readonly(self.token_program, false),
127                    AccountMeta::new_readonly(cpi_authority, false),
128                ],
129                data,
130            }
131        }
132    }
133}