use light_client::{
indexer::{AddressWithTree, Indexer},
rpc::{Rpc, RpcError},
};
use light_token::instruction::{
derive_mint_compressed_address, find_mint_address, CreateMint as CreateMintInstruction,
CreateMintParams as CreateMintInstructionParams,
};
use light_token_interface::{
instructions::extensions::{ExtensionInstructionData, TokenMetadataInstructionData},
state::AdditionalMetadata,
};
use solana_keypair::Keypair;
use solana_pubkey::Pubkey;
use solana_signature::Signature;
use solana_signer::Signer;
#[derive(Clone, Debug, Default)]
pub struct TokenMetadata {
pub name: String,
pub symbol: String,
pub uri: String,
pub update_authority: Option<Pubkey>,
pub additional_metadata: Option<Vec<(String, String)>>,
}
#[derive(Default, Debug)]
pub struct CreateMint {
pub decimals: u8,
pub freeze_authority: Option<Pubkey>,
pub token_metadata: Option<TokenMetadata>,
pub seed: Option<Keypair>,
}
impl CreateMint {
pub async fn execute<R: Rpc + Indexer>(
self,
rpc: &mut R,
payer: &Keypair,
mint_authority: &Keypair,
) -> Result<(Signature, Pubkey), RpcError> {
let mint_seed = self.seed.unwrap_or_else(Keypair::new);
let address_tree = rpc.get_address_tree_v2();
let output_queue = rpc.get_random_state_tree_info()?.queue;
let compression_address =
derive_mint_compressed_address(&mint_seed.pubkey(), &address_tree.tree);
let (mint, bump) = find_mint_address(&mint_seed.pubkey());
let rpc_result = rpc
.get_validity_proof(
vec![],
vec![AddressWithTree {
address: compression_address,
tree: address_tree.tree,
}],
None,
)
.await
.map_err(|e| RpcError::CustomError(format!("Failed to get validity proof: {}", e)))?
.value;
let extensions = self.token_metadata.map(|metadata| {
let additional_metadata = metadata.additional_metadata.map(|items| {
items
.into_iter()
.map(|(key, value)| AdditionalMetadata {
key: key.into_bytes(),
value: value.into_bytes(),
})
.collect()
});
vec![ExtensionInstructionData::TokenMetadata(
TokenMetadataInstructionData {
update_authority: Some(
metadata
.update_authority
.unwrap_or_else(|| mint_authority.pubkey())
.to_bytes()
.into(),
),
name: metadata.name.into_bytes(),
symbol: metadata.symbol.into_bytes(),
uri: metadata.uri.into_bytes(),
additional_metadata,
},
)]
});
let params = CreateMintInstructionParams {
decimals: self.decimals,
address_merkle_tree_root_index: rpc_result.addresses[0].root_index,
mint_authority: mint_authority.pubkey(),
proof: rpc_result.proof.0.ok_or_else(|| {
RpcError::CustomError("Validity proof is required for create_mint".to_string())
})?,
compression_address,
mint,
bump,
freeze_authority: self.freeze_authority,
extensions,
rent_payment: 16, write_top_up: 766, };
let instruction = CreateMintInstruction::new(
params,
mint_seed.pubkey(),
payer.pubkey(),
address_tree.tree,
output_queue,
)
.instruction()
.map_err(|e| RpcError::CustomError(format!("Failed to create instruction: {}", e)))?;
let mut signers: Vec<&Keypair> = vec![payer, &mint_seed];
if mint_authority.pubkey() != payer.pubkey() {
signers.push(mint_authority);
}
let signature = rpc
.create_and_send_transaction(&[instruction], &payer.pubkey(), &signers)
.await?;
Ok((signature, mint))
}
}