1use light_compressed_account::instruction_data::{
2 compressed_proof::CompressedProof, traits::LightInstructionData,
3};
4use light_compressed_token_sdk::compressed_token::mint_action::MintActionMetaConfig;
5use light_token_interface::{
6 instructions::{
7 extensions::ExtensionInstructionData,
8 mint_action::{CpiContext, DecompressMintAction, MintInstructionData},
9 },
10 COMPRESSED_MINT_SEED,
11};
12use solana_account_info::AccountInfo;
13use solana_cpi::{invoke, invoke_signed};
14use solana_instruction::Instruction;
15use solana_program_error::ProgramError;
16use solana_pubkey::Pubkey;
17
18use crate::{
19 constants::{config_pda, rent_sponsor_pda},
20 instruction::SystemAccountInfos,
21};
22#[derive(Debug, Clone)]
27pub struct CreateMintParams {
28 pub decimals: u8,
29 pub address_merkle_tree_root_index: u16,
30 pub mint_authority: Pubkey,
31 pub proof: CompressedProof,
32 pub compression_address: [u8; 32],
33 pub mint: Pubkey,
34 pub bump: u8,
35 pub freeze_authority: Option<Pubkey>,
36 pub extensions: Option<Vec<ExtensionInstructionData>>,
37 pub rent_payment: u8,
40 pub write_top_up: u32,
43}
44
45#[derive(Debug, Clone)]
89pub struct CreateMint {
90 pub mint_seed_pubkey: Pubkey,
93 pub payer: Pubkey,
94 pub address_tree_pubkey: Pubkey,
95 pub output_queue: Pubkey,
96 pub cpi_context: Option<CpiContext>,
97 pub cpi_context_pubkey: Option<Pubkey>,
98 pub params: CreateMintParams,
99}
100
101impl CreateMint {
102 pub fn new(
103 params: CreateMintParams,
104 mint_seed_pubkey: Pubkey,
105 payer: Pubkey,
106 address_tree_pubkey: Pubkey,
107 output_queue: Pubkey,
108 ) -> Self {
109 Self {
110 mint_seed_pubkey,
111 payer,
112 address_tree_pubkey,
113 output_queue,
114 cpi_context: None,
115 cpi_context_pubkey: None,
116 params,
117 }
118 }
119
120 pub fn with_cpi_context(mut self, cpi_context: CpiContext, cpi_context_pubkey: Pubkey) -> Self {
121 self.cpi_context = Some(cpi_context);
122 self.cpi_context_pubkey = Some(cpi_context_pubkey);
123 self
124 }
125
126 pub fn instruction(self) -> Result<Instruction, ProgramError> {
127 let compressed_mint_instruction_data = MintInstructionData {
128 supply: 0,
129 decimals: self.params.decimals,
130 metadata: light_token_interface::state::MintMetadata {
131 version: 3,
132 mint: self.params.mint.to_bytes().into(),
133 mint_decompressed: false,
134 mint_signer: self.mint_seed_pubkey.to_bytes(),
135 bump: self.params.bump,
136 },
137 mint_authority: Some(self.params.mint_authority.to_bytes().into()),
138 freeze_authority: self
139 .params
140 .freeze_authority
141 .map(|auth| auth.to_bytes().into()),
142 extensions: self.params.extensions,
143 };
144
145 let mut instruction_data =
146 light_token_interface::instructions::mint_action::MintActionCompressedInstructionData::new_mint(
147 self.params.address_merkle_tree_root_index,
148 self.params.proof,
149 compressed_mint_instruction_data,
150 );
151
152 instruction_data = instruction_data.with_decompress_mint(DecompressMintAction {
154 rent_payment: self.params.rent_payment,
155 write_top_up: self.params.write_top_up,
156 });
157
158 if let Some(ctx) = self.cpi_context {
159 instruction_data = instruction_data.with_cpi_context(ctx);
160 }
161
162 let mut meta_config = MintActionMetaConfig::new_create_mint(
163 self.payer,
164 self.params.mint_authority,
165 self.mint_seed_pubkey,
166 self.address_tree_pubkey,
167 self.output_queue,
168 )
169 .with_compressible_mint(self.params.mint, config_pda(), rent_sponsor_pda());
171
172 if let Some(cpi_context_pubkey) = self.cpi_context_pubkey {
173 meta_config.cpi_context = Some(cpi_context_pubkey);
174 }
175
176 let account_metas = meta_config.to_account_metas();
177
178 let data = instruction_data
179 .data()
180 .map_err(|e| ProgramError::BorshIoError(e.to_string()))?;
181
182 Ok(Instruction {
183 program_id: Pubkey::new_from_array(light_token_interface::LIGHT_TOKEN_PROGRAM_ID),
184 accounts: account_metas,
185 data,
186 })
187 }
188}
189
190pub struct CreateMintCpi<'info> {
226 pub mint_seed: AccountInfo<'info>,
227 pub authority: AccountInfo<'info>,
229 pub payer: AccountInfo<'info>,
231 pub address_tree: AccountInfo<'info>,
232 pub output_queue: AccountInfo<'info>,
233 pub compressible_config: AccountInfo<'info>,
235 pub mint: AccountInfo<'info>,
237 pub rent_sponsor: AccountInfo<'info>,
239 pub system_accounts: SystemAccountInfos<'info>,
240 pub cpi_context: Option<CpiContext>,
241 pub cpi_context_account: Option<AccountInfo<'info>>,
242 pub params: CreateMintParams,
243}
244
245impl<'info> CreateMintCpi<'info> {
246 #[allow(clippy::too_many_arguments)]
247 pub fn new(
248 mint_seed: AccountInfo<'info>,
249 authority: AccountInfo<'info>,
250 payer: AccountInfo<'info>,
251 address_tree: AccountInfo<'info>,
252 output_queue: AccountInfo<'info>,
253 compressible_config: AccountInfo<'info>,
254 mint: AccountInfo<'info>,
255 rent_sponsor: AccountInfo<'info>,
256 system_accounts: SystemAccountInfos<'info>,
257 params: CreateMintParams,
258 ) -> Self {
259 Self {
260 mint_seed,
261 authority,
262 payer,
263 address_tree,
264 output_queue,
265 compressible_config,
266 mint,
267 rent_sponsor,
268 system_accounts,
269 cpi_context: None,
270 cpi_context_account: None,
271 params,
272 }
273 }
274
275 pub fn instruction(&self) -> Result<Instruction, ProgramError> {
276 CreateMint::try_from(self)?.instruction()
277 }
278
279 pub fn invoke(self) -> Result<(), ProgramError> {
280 let instruction = self.instruction()?;
281
282 let mut account_infos = vec![
284 self.system_accounts.light_system_program,
285 self.mint_seed,
286 self.authority,
287 self.compressible_config,
288 self.mint,
289 self.rent_sponsor,
290 self.payer,
291 self.system_accounts.cpi_authority_pda,
292 self.system_accounts.registered_program_pda,
293 self.system_accounts.account_compression_authority,
294 self.system_accounts.account_compression_program,
295 self.system_accounts.system_program,
296 ];
297
298 if let Some(cpi_context_account) = self.cpi_context_account {
299 account_infos.push(cpi_context_account);
300 }
301
302 account_infos.push(self.output_queue);
303 account_infos.push(self.address_tree);
304
305 invoke(&instruction, &account_infos)
306 }
307
308 pub fn invoke_signed(self, signer_seeds: &[&[&[u8]]]) -> Result<(), ProgramError> {
309 let instruction = self.instruction()?;
310
311 let mut account_infos = vec![
313 self.system_accounts.light_system_program,
314 self.mint_seed,
315 self.authority,
316 self.compressible_config,
317 self.mint,
318 self.rent_sponsor,
319 self.payer,
320 self.system_accounts.cpi_authority_pda,
321 self.system_accounts.registered_program_pda,
322 self.system_accounts.account_compression_authority,
323 self.system_accounts.account_compression_program,
324 self.system_accounts.system_program,
325 ];
326
327 if let Some(cpi_context_account) = self.cpi_context_account {
328 account_infos.push(cpi_context_account);
329 }
330
331 account_infos.push(self.output_queue);
332 account_infos.push(self.address_tree);
333
334 invoke_signed(&instruction, &account_infos, signer_seeds)
335 }
336}
337
338impl<'info> TryFrom<&CreateMintCpi<'info>> for CreateMint {
339 type Error = ProgramError;
340
341 fn try_from(account_infos: &CreateMintCpi<'info>) -> Result<Self, Self::Error> {
342 if account_infos.params.mint_authority != *account_infos.authority.key {
343 solana_msg::msg!(
344 "CreateMintCpi: params.mint_authority ({}) does not match authority account ({})",
345 account_infos.params.mint_authority,
346 account_infos.authority.key
347 );
348 return Err(ProgramError::InvalidAccountData);
349 }
350 Ok(Self {
351 mint_seed_pubkey: *account_infos.mint_seed.key,
352 payer: *account_infos.payer.key,
353 address_tree_pubkey: *account_infos.address_tree.key,
354 output_queue: *account_infos.output_queue.key,
355 cpi_context: account_infos.cpi_context.clone(),
356 cpi_context_pubkey: account_infos
357 .cpi_context_account
358 .as_ref()
359 .map(|acc| *acc.key),
360 params: account_infos.params.clone(),
361 })
362 }
363}
364
365pub fn derive_mint_compressed_address(
371 mint_seed: &Pubkey,
372 address_tree_pubkey: &Pubkey,
373) -> [u8; 32] {
374 light_compressed_account::address::derive_address(
375 &find_mint_address(mint_seed).0.to_bytes(),
376 &address_tree_pubkey.to_bytes(),
377 &light_token_interface::LIGHT_TOKEN_PROGRAM_ID,
378 )
379}
380
381pub fn derive_mint_from_spl_mint(mint: &Pubkey, address_tree_pubkey: &Pubkey) -> [u8; 32] {
383 light_compressed_account::address::derive_address(
384 &mint.to_bytes(),
385 &address_tree_pubkey.to_bytes(),
386 &light_token_interface::LIGHT_TOKEN_PROGRAM_ID,
387 )
388}
389
390pub fn find_mint_address(mint_seed: &Pubkey) -> (Pubkey, u8) {
392 Pubkey::find_program_address(
393 &[COMPRESSED_MINT_SEED, mint_seed.as_ref()],
394 &Pubkey::new_from_array(light_token_interface::LIGHT_TOKEN_PROGRAM_ID),
395 )
396}