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