light_token/compressed_token/v2/mint_action/
account_metas.rs

1use light_program_profiler::profile;
2use solana_instruction::AccountMeta;
3use solana_pubkey::Pubkey;
4
5use crate::utils::TokenDefaultAccounts;
6
7#[derive(Debug, Clone)]
8pub struct MintActionMetaConfig {
9    pub fee_payer: Pubkey,
10    pub authority: Pubkey,
11    pub tree_pubkey: Pubkey, // address tree when create_mint, input state tree when not
12    pub output_queue: Pubkey,
13    pub mint_signer: Option<Pubkey>,
14    pub input_queue: Option<Pubkey>, // Input queue for existing compressed mint operations
15    pub tokens_out_queue: Option<Pubkey>, // Output queue for new token accounts
16    pub cpi_context: Option<Pubkey>,
17    pub token_accounts: Vec<Pubkey>, // For mint_to_ctoken actions
18    pub mint: Option<Pubkey>,        // Mint PDA account for DecompressMint action
19    pub compressible_config: Option<Pubkey>, // CompressibleConfig account (when creating Mint)
20    pub rent_sponsor: Option<Pubkey>, // Rent sponsor PDA (when creating Mint)
21    pub mint_signer_must_sign: bool, // true for create_mint, false for decompress_mint
22}
23
24impl MintActionMetaConfig {
25    /// Create a new MintActionMetaConfig for creating a new compressed mint.
26    pub fn new_create_mint(
27        fee_payer: Pubkey,
28        authority: Pubkey,
29        mint_signer: Pubkey,
30        address_tree: Pubkey,
31        output_queue: Pubkey,
32    ) -> Self {
33        Self {
34            fee_payer,
35            authority,
36            tree_pubkey: address_tree,
37            output_queue,
38            mint_signer: Some(mint_signer),
39            input_queue: None,
40            tokens_out_queue: None,
41            cpi_context: None,
42            token_accounts: Vec::new(),
43            mint: None,
44            compressible_config: None,
45            rent_sponsor: None,
46            mint_signer_must_sign: true,
47        }
48    }
49
50    /// Create a new MintActionMetaConfig for operations on an existing compressed mint.
51    #[inline(never)]
52    pub fn new(
53        fee_payer: Pubkey,
54        authority: Pubkey,
55        state_tree: Pubkey,
56        input_queue: Pubkey,
57        output_queue: Pubkey,
58    ) -> Self {
59        Self {
60            fee_payer,
61            authority,
62            tree_pubkey: state_tree,
63            output_queue,
64            mint_signer: None,
65            input_queue: Some(input_queue),
66            tokens_out_queue: None,
67            cpi_context: None,
68            token_accounts: Vec::new(),
69            mint: None,
70            compressible_config: None,
71            rent_sponsor: None,
72            mint_signer_must_sign: false,
73        }
74    }
75
76    /// Create a new MintActionMetaConfig for CPI context operations.
77    pub fn new_cpi_context(
78        instruction_data: &light_token_interface::instructions::mint_action::MintActionCompressedInstructionData,
79        fee_payer: Pubkey,
80        authority: Pubkey,
81        cpi_context_pubkey: Pubkey,
82    ) -> crate::error::Result<Self> {
83        if instruction_data.cpi_context.is_none() {
84            return Err(crate::error::TokenSdkError::CpiContextRequired);
85        }
86
87        Ok(Self {
88            fee_payer,
89            authority,
90            tree_pubkey: Pubkey::default(),
91            output_queue: Pubkey::default(),
92            mint_signer: None,
93            input_queue: None,
94            tokens_out_queue: None,
95            cpi_context: Some(cpi_context_pubkey),
96            token_accounts: Vec::new(),
97            mint: None,
98            compressible_config: None,
99            rent_sponsor: None,
100            mint_signer_must_sign: false,
101        })
102    }
103
104    pub fn with_mint_compressed_tokens(mut self) -> Self {
105        self.tokens_out_queue = Some(self.output_queue);
106        self
107    }
108
109    pub fn with_token_accounts(mut self, accounts: Vec<Pubkey>) -> Self {
110        self.token_accounts = accounts;
111        self
112    }
113
114    pub fn with_mint(mut self, mint: Pubkey) -> Self {
115        self.mint = Some(mint);
116        self
117    }
118
119    /// Set the mint_signer account with signing required.
120    /// Use for create_mint actions.
121    pub fn with_mint_signer(mut self, mint_signer: Pubkey) -> Self {
122        self.mint_signer = Some(mint_signer);
123        self.mint_signer_must_sign = true;
124        self
125    }
126
127    /// Configure compressible Mint with config and rent sponsor.
128    /// Mint is always compressible - this sets all required accounts.
129    pub fn with_compressible_mint(
130        mut self,
131        mint: Pubkey,
132        compressible_config: Pubkey,
133        rent_sponsor: Pubkey,
134    ) -> Self {
135        self.mint = Some(mint);
136        self.compressible_config = Some(compressible_config);
137        self.rent_sponsor = Some(rent_sponsor);
138        self
139    }
140
141    /// Get the account metas for a mint action instruction
142    #[profile]
143    #[inline(never)]
144    pub fn to_account_metas(self) -> Vec<AccountMeta> {
145        let default_pubkeys = TokenDefaultAccounts::default();
146        let mut metas = Vec::new();
147
148        metas.push(AccountMeta::new_readonly(
149            default_pubkeys.light_system_program,
150            false,
151        ));
152
153        // mint_signer is present when creating a new mint or decompressing
154        if let Some(mint_signer) = self.mint_signer {
155            // mint_signer needs to sign for create_mint, not for decompress_mint
156            metas.push(AccountMeta::new_readonly(
157                mint_signer,
158                self.mint_signer_must_sign,
159            ));
160        }
161
162        metas.push(AccountMeta::new_readonly(self.authority, true));
163
164        // CompressibleConfig account (when creating compressible Mint)
165        if let Some(config) = self.compressible_config {
166            metas.push(AccountMeta::new_readonly(config, false));
167        }
168
169        // Mint account is present when decompressing the mint (DecompressMint action) or syncing
170        if let Some(mint) = self.mint {
171            metas.push(AccountMeta::new(mint, false));
172        }
173
174        // Rent sponsor PDA (when creating compressible Mint)
175        if let Some(rent_sponsor) = self.rent_sponsor {
176            metas.push(AccountMeta::new(rent_sponsor, false));
177        }
178
179        metas.push(AccountMeta::new(self.fee_payer, true));
180
181        metas.push(AccountMeta::new_readonly(
182            default_pubkeys.cpi_authority_pda,
183            false,
184        ));
185
186        metas.push(AccountMeta::new_readonly(
187            default_pubkeys.registered_program_pda,
188            false,
189        ));
190
191        metas.push(AccountMeta::new_readonly(
192            default_pubkeys.account_compression_authority,
193            false,
194        ));
195
196        metas.push(AccountMeta::new_readonly(
197            default_pubkeys.account_compression_program,
198            false,
199        ));
200
201        metas.push(AccountMeta::new_readonly(
202            default_pubkeys.system_program,
203            false,
204        ));
205
206        if let Some(cpi_context) = self.cpi_context {
207            metas.push(AccountMeta::new(cpi_context, false));
208        }
209
210        metas.push(AccountMeta::new(self.output_queue, false));
211
212        metas.push(AccountMeta::new(self.tree_pubkey, false));
213
214        // input_queue is present when operating on an existing compressed mint
215        // (input_queue is set via new() for existing mints, None via new_create_mint() for new mints)
216        if let Some(input_queue) = self.input_queue {
217            metas.push(AccountMeta::new(input_queue, false));
218        }
219
220        // tokens_out_queue is present when there are MintToCompressed actions
221        if let Some(tokens_out_queue) = self.tokens_out_queue {
222            metas.push(AccountMeta::new(tokens_out_queue, false));
223        }
224
225        for token_account in &self.token_accounts {
226            metas.push(AccountMeta::new(*token_account, false));
227        }
228
229        metas
230    }
231}
232/// Account metadata configuration for mint action CPI write instruction
233#[derive(Debug, Clone)]
234pub struct MintActionMetaConfigCpiWrite {
235    pub fee_payer: Pubkey,
236    pub mint_signer: Option<Pubkey>, // Optional - only when creating mint
237    pub authority: Pubkey,
238    pub cpi_context: Pubkey,
239}
240
241/// Get the account metas for a mint action CPI write instruction
242#[profile]
243pub fn get_mint_action_instruction_account_metas_cpi_write(
244    config: MintActionMetaConfigCpiWrite,
245) -> Vec<AccountMeta> {
246    let default_pubkeys = TokenDefaultAccounts::default();
247    let mut metas = Vec::new();
248
249    metas.push(AccountMeta::new_readonly(
250        default_pubkeys.light_system_program,
251        false,
252    ));
253
254    // mint signer always needs to sign when present
255    if let Some(mint_signer) = config.mint_signer {
256        metas.push(AccountMeta::new_readonly(mint_signer, true));
257    }
258
259    metas.push(AccountMeta::new_readonly(config.authority, true));
260
261    metas.push(AccountMeta::new(config.fee_payer, true));
262
263    metas.push(AccountMeta::new_readonly(
264        default_pubkeys.cpi_authority_pda,
265        false,
266    ));
267
268    metas.push(AccountMeta::new(config.cpi_context, false));
269
270    metas
271}