#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(not(target_os = "solana"))]
use {
crate::extension::{
confidential_mint_burn::instruction::BurnInstructionData as ConfidentialBurnInstructionData,
confidential_transfer::DecryptableBalance,
},
solana_zk_elgamal_proof_interface::{
instruction::ProofInstruction,
proof_data::{
BatchedGroupedCiphertext3HandlesValidityProofData, BatchedRangeProofU128Data,
CiphertextCommitmentEqualityProofData,
},
},
solana_zk_sdk_pod::encryption::elgamal::PodElGamalCiphertext,
spl_token_confidential_transfer_proof_extraction::instruction::{
process_proof_location, ProofLocation,
},
};
use {
crate::{
check_program_account,
instruction::{encode_instruction, TokenInstruction},
},
alloc::{vec, vec::Vec},
bytemuck::{Pod, Zeroable},
num_enum::{IntoPrimitive, TryFromPrimitive},
solana_address::Address,
solana_instruction::{AccountMeta, Instruction},
solana_program_error::ProgramError,
solana_zero_copy::unaligned::U64,
};
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
#[derive(Clone, Copy, Debug, PartialEq, IntoPrimitive, TryFromPrimitive)]
#[repr(u8)]
pub enum PermissionedBurnInstruction {
Initialize,
Burn,
BurnChecked,
ConfidentialBurn,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C)]
pub struct InitializeInstructionData {
pub authority: Address,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C)]
pub struct BurnInstructionData {
pub amount: U64,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C)]
pub struct BurnCheckedInstructionData {
pub amount: U64,
pub decimals: u8,
}
pub fn initialize(
token_program_id: &Address,
mint: &Address,
authority: &Address,
) -> Result<Instruction, ProgramError> {
check_program_account(token_program_id)?;
let accounts = vec![AccountMeta::new(*mint, false)];
Ok(encode_instruction(
token_program_id,
accounts,
TokenInstruction::PermissionedBurnExtension,
PermissionedBurnInstruction::Initialize,
&InitializeInstructionData {
authority: *authority,
},
))
}
pub fn burn(
token_program_id: &Address,
account: &Address,
mint: &Address,
permissioned_burn_authority: &Address,
authority: &Address,
signer_pubkeys: &[&Address],
amount: u64,
) -> Result<Instruction, ProgramError> {
check_program_account(token_program_id)?;
let data = BurnInstructionData {
amount: amount.into(),
};
let mut accounts = Vec::with_capacity(4 + signer_pubkeys.len());
accounts.push(AccountMeta::new(*account, false));
accounts.push(AccountMeta::new(*mint, false));
accounts.push(AccountMeta::new_readonly(
*permissioned_burn_authority,
true,
));
accounts.push(AccountMeta::new_readonly(
*authority,
signer_pubkeys.is_empty(),
));
for signer_pubkey in signer_pubkeys.iter() {
accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
}
Ok(encode_instruction(
token_program_id,
accounts,
TokenInstruction::PermissionedBurnExtension,
PermissionedBurnInstruction::Burn,
&data,
))
}
#[allow(clippy::too_many_arguments)]
pub fn burn_checked(
token_program_id: &Address,
account: &Address,
mint: &Address,
permissioned_burn_authority: &Address,
authority: &Address,
signer_pubkeys: &[&Address],
amount: u64,
decimals: u8,
) -> Result<Instruction, ProgramError> {
check_program_account(token_program_id)?;
let data = BurnCheckedInstructionData {
amount: amount.into(),
decimals,
};
let mut accounts = Vec::with_capacity(4 + signer_pubkeys.len());
accounts.push(AccountMeta::new(*account, false));
accounts.push(AccountMeta::new(*mint, false));
accounts.push(AccountMeta::new_readonly(
*permissioned_burn_authority,
true,
));
accounts.push(AccountMeta::new_readonly(
*authority,
signer_pubkeys.is_empty(),
));
for signer_pubkey in signer_pubkeys.iter() {
accounts.push(AccountMeta::new_readonly(**signer_pubkey, true));
}
Ok(encode_instruction(
token_program_id,
accounts,
TokenInstruction::PermissionedBurnExtension,
PermissionedBurnInstruction::BurnChecked,
&data,
))
}
#[allow(clippy::too_many_arguments)]
#[cfg(not(target_os = "solana"))]
pub fn confidential_burn_with_split_proofs(
token_program_id: &Address,
token_account: &Address,
mint: &Address,
permissioned_burn_authority: &Address,
new_decryptable_available_balance: &DecryptableBalance,
burn_amount_auditor_ciphertext_lo: &PodElGamalCiphertext,
burn_amount_auditor_ciphertext_hi: &PodElGamalCiphertext,
authority: &Address,
multisig_signers: &[&Address],
equality_proof_location: ProofLocation<CiphertextCommitmentEqualityProofData>,
ciphertext_validity_proof_location: ProofLocation<
BatchedGroupedCiphertext3HandlesValidityProofData,
>,
range_proof_location: ProofLocation<BatchedRangeProofU128Data>,
) -> Result<Vec<Instruction>, ProgramError> {
check_program_account(token_program_id)?;
let mut accounts = vec![
AccountMeta::new(*token_account, false),
AccountMeta::new(*mint, false),
];
let mut expected_instruction_offset = 1;
let mut proof_instructions = vec![];
let equality_proof_instruction_offset = process_proof_location(
&mut accounts,
&mut expected_instruction_offset,
&mut proof_instructions,
equality_proof_location,
true,
ProofInstruction::VerifyCiphertextCommitmentEquality,
)?;
let ciphertext_validity_proof_instruction_offset = process_proof_location(
&mut accounts,
&mut expected_instruction_offset,
&mut proof_instructions,
ciphertext_validity_proof_location,
false,
ProofInstruction::VerifyBatchedGroupedCiphertext3HandlesValidity,
)?;
let range_proof_instruction_offset = process_proof_location(
&mut accounts,
&mut expected_instruction_offset,
&mut proof_instructions,
range_proof_location,
false,
ProofInstruction::VerifyBatchedRangeProofU128,
)?;
accounts.push(AccountMeta::new_readonly(
*permissioned_burn_authority,
true,
));
accounts.push(AccountMeta::new_readonly(
*authority,
multisig_signers.is_empty(),
));
for multisig_signer in multisig_signers.iter() {
accounts.push(AccountMeta::new_readonly(**multisig_signer, true));
}
let mut instructions = vec![encode_instruction(
token_program_id,
accounts,
TokenInstruction::PermissionedBurnExtension,
PermissionedBurnInstruction::ConfidentialBurn,
&ConfidentialBurnInstructionData {
new_decryptable_available_balance: *new_decryptable_available_balance,
burn_amount_auditor_ciphertext_lo: *burn_amount_auditor_ciphertext_lo,
burn_amount_auditor_ciphertext_hi: *burn_amount_auditor_ciphertext_hi,
equality_proof_instruction_offset,
ciphertext_validity_proof_instruction_offset,
range_proof_instruction_offset,
},
)];
instructions.extend(proof_instructions);
Ok(instructions)
}