use light_compressed_account::instruction_data::{
compressed_proof::CompressedProof, traits::LightInstructionData,
};
use light_token_interface::{
self,
instructions::{
extensions::ExtensionInstructionData,
mint_action::{CpiContext, MintInstructionData},
},
COMPRESSED_MINT_SEED,
};
use solana_instruction::Instruction;
use solana_msg::msg;
use solana_pubkey::Pubkey;
use crate::{
compressed_token::mint_action::{
get_mint_action_instruction_account_metas_cpi_write, MintActionMetaConfig,
MintActionMetaConfigCpiWrite,
},
error::{Result, TokenSdkError},
AnchorDeserialize, AnchorSerialize,
};
#[derive(Debug, Clone, AnchorDeserialize, AnchorSerialize)]
pub struct CreateMintInputs {
pub decimals: u8,
pub mint_authority: Pubkey,
pub freeze_authority: Option<Pubkey>,
pub proof: CompressedProof,
pub address_merkle_tree_root_index: u16,
pub mint_signer: Pubkey,
pub payer: Pubkey,
pub address_tree_pubkey: Pubkey,
pub output_queue: Pubkey,
pub extensions: Option<Vec<ExtensionInstructionData>>,
pub version: u8,
}
pub fn create_compressed_mint_cpi(
input: CreateMintInputs,
cpi_context: Option<CpiContext>,
cpi_context_pubkey: Option<Pubkey>,
) -> Result<Instruction> {
let (mint_pda, bump) = find_mint_address(&input.mint_signer);
let compressed_mint_instruction_data = MintInstructionData {
supply: 0,
decimals: input.decimals,
metadata: light_token_interface::state::MintMetadata {
version: input.version,
mint: mint_pda.to_bytes().into(),
mint_decompressed: false,
mint_signer: input.mint_signer.to_bytes(),
bump,
},
mint_authority: Some(input.mint_authority.to_bytes().into()),
freeze_authority: input.freeze_authority.map(|auth| auth.to_bytes().into()),
extensions: input.extensions,
};
let mut instruction_data = light_token_interface::instructions::mint_action::MintActionCompressedInstructionData::new_mint(
input.address_merkle_tree_root_index,
input.proof,
compressed_mint_instruction_data,
);
if let Some(ctx) = cpi_context {
instruction_data = instruction_data.with_cpi_context(ctx);
}
let meta_config = if cpi_context_pubkey.is_some() {
MintActionMetaConfig::new_cpi_context(
&instruction_data,
input.payer,
input.mint_authority,
cpi_context_pubkey.unwrap(),
)?
} else {
MintActionMetaConfig::new_create_mint(
input.payer,
input.mint_authority,
input.mint_signer,
input.address_tree_pubkey,
input.output_queue,
)
};
let account_metas = meta_config.to_account_metas();
let data = instruction_data
.data()
.map_err(|_| TokenSdkError::SerializationError)?;
Ok(Instruction {
program_id: solana_pubkey::Pubkey::new_from_array(
light_token_interface::LIGHT_TOKEN_PROGRAM_ID,
),
accounts: account_metas,
data,
})
}
#[derive(Debug, Clone, AnchorDeserialize, AnchorSerialize)]
pub struct CreateMintInputsCpiWrite {
pub decimals: u8,
pub mint_authority: Pubkey,
pub freeze_authority: Option<Pubkey>,
pub address_merkle_tree_root_index: u16,
pub mint_signer: Pubkey,
pub payer: Pubkey,
pub mint_address: [u8; 32],
pub cpi_context: CpiContext,
pub cpi_context_pubkey: Pubkey,
pub extensions: Option<Vec<ExtensionInstructionData>>,
pub version: u8,
}
pub fn create_compressed_mint_cpi_write(input: CreateMintInputsCpiWrite) -> Result<Instruction> {
if !input.cpi_context.first_set_context && !input.cpi_context.set_context {
msg!(
"Invalid CPI context first cpi set or set context must be true {:?}",
input.cpi_context
);
return Err(TokenSdkError::InvalidAccountData);
}
let (mint_pda, bump) = find_mint_address(&input.mint_signer);
let compressed_mint_instruction_data = MintInstructionData {
supply: 0,
decimals: input.decimals,
metadata: light_token_interface::state::MintMetadata {
version: input.version,
mint: mint_pda.to_bytes().into(),
mint_decompressed: false,
mint_signer: input.mint_signer.to_bytes(),
bump,
},
mint_authority: Some(input.mint_authority.to_bytes().into()),
freeze_authority: input.freeze_authority.map(|auth| auth.to_bytes().into()),
extensions: input.extensions,
};
let instruction_data = light_token_interface::instructions::mint_action::MintActionCompressedInstructionData::new_mint_write_to_cpi_context(
input.address_merkle_tree_root_index,
compressed_mint_instruction_data,
input.cpi_context,
);
let meta_config = MintActionMetaConfigCpiWrite {
fee_payer: input.payer,
mint_signer: Some(input.mint_signer),
authority: input.mint_authority,
cpi_context: input.cpi_context_pubkey,
};
let account_metas = get_mint_action_instruction_account_metas_cpi_write(meta_config);
let data = instruction_data
.data()
.map_err(|_| TokenSdkError::SerializationError)?;
Ok(Instruction {
program_id: solana_pubkey::Pubkey::new_from_array(
light_token_interface::LIGHT_TOKEN_PROGRAM_ID,
),
accounts: account_metas,
data,
})
}
pub fn create_compressed_mint(input: CreateMintInputs) -> Result<Instruction> {
create_compressed_mint_cpi(input, None, None)
}
pub fn derive_mint_compressed_address(
mint_seed: &Pubkey,
address_tree_pubkey: &Pubkey,
) -> [u8; 32] {
light_compressed_account::address::derive_address(
&find_mint_address(mint_seed).0.to_bytes(),
&address_tree_pubkey.to_bytes(),
&light_token_interface::LIGHT_TOKEN_PROGRAM_ID,
)
}
pub fn derive_mint_from_spl_mint(mint: &Pubkey, address_tree_pubkey: &Pubkey) -> [u8; 32] {
light_compressed_account::address::derive_address(
&mint.to_bytes(),
&address_tree_pubkey.to_bytes(),
&light_token_interface::LIGHT_TOKEN_PROGRAM_ID,
)
}
pub fn find_mint_address(mint_seed: &Pubkey) -> (Pubkey, u8) {
Pubkey::find_program_address(
&[COMPRESSED_MINT_SEED, mint_seed.as_ref()],
&Pubkey::new_from_array(light_token_interface::LIGHT_TOKEN_PROGRAM_ID),
)
}