light_token/compressed_token/v2/mint_to_compressed/
account_metas.rs

1use solana_instruction::AccountMeta;
2use solana_pubkey::Pubkey;
3
4use crate::utils::TokenDefaultAccounts;
5
6/// Account metadata configuration for mint_to_compressed instruction
7#[derive(Debug, Copy, Clone)]
8pub struct MintToCompressedMetaConfig {
9    pub mint_authority: Option<Pubkey>,
10    pub payer: Option<Pubkey>,
11    pub state_merkle_tree: Pubkey,
12    pub output_queue: Pubkey,
13    pub state_tree_pubkey: Pubkey,
14    pub compressed_mint_tree: Pubkey,
15    pub compressed_mint_queue: Pubkey,
16    pub spl_mint_initialized: bool,
17    pub mint_pda: Option<Pubkey>,
18    pub spl_interface_pda: Option<Pubkey>,
19    pub token_program: Option<Pubkey>,
20    pub with_lamports: bool,
21}
22
23impl MintToCompressedMetaConfig {
24    /// Create a new MintToCompressedMetaConfig for standard compressed mint operations
25    #[allow(clippy::too_many_arguments)]
26    pub fn new(
27        mint_authority: Pubkey,
28        payer: Pubkey,
29        state_merkle_tree: Pubkey,
30        output_queue: Pubkey,
31        state_tree_pubkey: Pubkey,
32        compressed_mint_tree: Pubkey,
33        compressed_mint_queue: Pubkey,
34        with_lamports: bool,
35    ) -> Self {
36        Self {
37            mint_authority: Some(mint_authority),
38            payer: Some(payer),
39            state_merkle_tree,
40            output_queue,
41            state_tree_pubkey,
42            compressed_mint_tree,
43            compressed_mint_queue,
44            spl_mint_initialized: false,
45            mint_pda: None,
46            spl_interface_pda: None,
47            token_program: None,
48            with_lamports,
49        }
50    }
51
52    /// Create a new MintToCompressedMetaConfig for client use (excludes authority and payer accounts)
53    pub fn new_client(
54        state_merkle_tree: Pubkey,
55        output_queue: Pubkey,
56        state_tree_pubkey: Pubkey,
57        compressed_mint_tree: Pubkey,
58        compressed_mint_queue: Pubkey,
59        with_lamports: bool,
60    ) -> Self {
61        Self {
62            mint_authority: None, // Client mode - account provided by caller
63            payer: None,          // Client mode - account provided by caller
64            state_merkle_tree,
65            output_queue,
66            state_tree_pubkey,
67            compressed_mint_tree,
68            compressed_mint_queue,
69            spl_mint_initialized: false,
70            mint_pda: None,
71            spl_interface_pda: None,
72            token_program: None,
73            with_lamports,
74        }
75    }
76
77    /// Create a new MintToCompressedMetaConfig for decompressed mint operations
78    #[allow(clippy::too_many_arguments)]
79    pub fn new_decompressed(
80        mint_authority: Pubkey,
81        payer: Pubkey,
82        state_merkle_tree: Pubkey,
83        output_queue: Pubkey,
84        state_tree_pubkey: Pubkey,
85        compressed_mint_tree: Pubkey,
86        compressed_mint_queue: Pubkey,
87        mint_pda: Pubkey,
88        spl_interface_pda: Pubkey,
89        token_program: Pubkey,
90        with_lamports: bool,
91    ) -> Self {
92        Self {
93            mint_authority: Some(mint_authority),
94            payer: Some(payer),
95            state_merkle_tree,
96            output_queue,
97            state_tree_pubkey,
98            compressed_mint_tree,
99            compressed_mint_queue,
100            spl_mint_initialized: true,
101            mint_pda: Some(mint_pda),
102            spl_interface_pda: Some(spl_interface_pda),
103            token_program: Some(token_program),
104            with_lamports,
105        }
106    }
107}
108
109#[derive(Debug, Copy, Clone)]
110pub struct MintToCompressedMetaConfigCpiWrite {
111    pub fee_payer: Pubkey,
112    pub mint_authority: Pubkey,
113    pub cpi_context: Pubkey,
114}
115
116pub fn get_mint_to_compressed_instruction_account_metas_cpi_write(
117    config: MintToCompressedMetaConfigCpiWrite,
118) -> [AccountMeta; 5] {
119    let default_pubkeys = TokenDefaultAccounts::default();
120    [
121        AccountMeta::new_readonly(default_pubkeys.light_system_program, false),
122        AccountMeta::new_readonly(config.mint_authority, true),
123        AccountMeta::new(config.fee_payer, true),
124        AccountMeta::new_readonly(default_pubkeys.cpi_authority_pda, false),
125        AccountMeta::new(config.cpi_context, false),
126    ]
127}
128
129/// Get the standard account metas for a mint_to_compressed instruction
130pub fn get_mint_to_compressed_instruction_account_metas(
131    config: MintToCompressedMetaConfig,
132) -> Vec<AccountMeta> {
133    let default_pubkeys = TokenDefaultAccounts::default();
134
135    // Calculate capacity based on configuration
136    // Optional accounts: authority + payer + optional decompressed accounts (3) + light_system_program +
137    //                   cpi accounts (6 without fee_payer) + optional SOL pool + system_program + merkle tree accounts (5)
138    let base_capacity = 14; // light_system_program + 6 cpi accounts + system_program + 5 tree accounts
139    let authority_capacity = if config.mint_authority.is_some() {
140        1
141    } else {
142        0
143    };
144    let payer_capacity = if config.payer.is_some() { 1 } else { 0 };
145    let decompressed_capacity = if config.spl_mint_initialized { 3 } else { 0 };
146    let sol_pool_capacity = if config.with_lamports { 1 } else { 0 };
147    let total_capacity = base_capacity
148        + authority_capacity
149        + payer_capacity
150        + decompressed_capacity
151        + sol_pool_capacity;
152
153    let mut metas = Vec::with_capacity(total_capacity);
154
155    // light_system_program (always first)
156    metas.push(AccountMeta::new_readonly(
157        default_pubkeys.light_system_program,
158        false,
159    ));
160
161    // authority (signer) - always required by program, even in CPI mode
162    // In CPI mode, caller provides authority account at runtime
163    if let Some(mint_authority) = config.mint_authority {
164        metas.push(AccountMeta::new_readonly(mint_authority, true));
165    }
166
167    // Optional decompressed mint accounts
168    if config.spl_mint_initialized {
169        metas.push(AccountMeta::new(config.mint_pda.unwrap(), false)); // mint
170        metas.push(AccountMeta::new(config.spl_interface_pda.unwrap(), false)); // spl_interface_pda
171        metas.push(AccountMeta::new_readonly(
172            config.token_program.unwrap(),
173            false,
174        )); // token_program
175    }
176
177    // CPI accounts in exact order expected by InvokeCpiWithReadOnly
178    if let Some(payer) = config.payer {
179        metas.push(AccountMeta::new(payer, true)); // fee_payer (signer, mutable)
180    }
181    metas.push(AccountMeta::new_readonly(
182        default_pubkeys.cpi_authority_pda,
183        false,
184    )); // cpi_authority_pda
185    metas.push(AccountMeta::new_readonly(
186        default_pubkeys.registered_program_pda,
187        false,
188    )); // registered_program_pda
189    metas.push(AccountMeta::new_readonly(
190        default_pubkeys.account_compression_authority,
191        false,
192    )); // account_compression_authority
193    metas.push(AccountMeta::new_readonly(
194        default_pubkeys.account_compression_program,
195        false,
196    )); // account_compression_program
197
198    // system_program
199    metas.push(AccountMeta::new_readonly(
200        default_pubkeys.system_program,
201        false,
202    ));
203
204    // Optional SOL pool
205    if config.with_lamports {
206        metas.push(AccountMeta::new(
207            Pubkey::from(light_sdk::constants::SOL_POOL_PDA),
208            false,
209        )); // sol_pool_pda (mutable)
210    }
211
212    // Merkle tree accounts - UpdateOneCompressedAccountTreeAccounts (3 accounts)
213    metas.push(AccountMeta::new(config.state_merkle_tree, false)); // in_merkle_tree (mutable)
214    metas.push(AccountMeta::new(config.compressed_mint_queue, false)); // in_output_queue (mutable)
215    metas.push(AccountMeta::new(config.compressed_mint_queue, false)); // out_output_queue (mutable) - same as in_output_queue
216
217    // Additional tokens_out_queue (separate from UpdateOneCompressedAccountTreeAccounts)
218    metas.push(AccountMeta::new(config.output_queue, false)); // tokens_out_queue (mutable)
219
220    metas
221}