spl_token_2022_interface/extension/confidential_mint_burn/
instruction.rs

1#[cfg(feature = "serde")]
2use {
3    crate::serialization::{
4        aeciphertext_fromstr, elgamalciphertext_fromstr, elgamalpubkey_fromstr,
5    },
6    serde::{Deserialize, Serialize},
7};
8use {
9    crate::{
10        check_program_account,
11        extension::confidential_transfer::DecryptableBalance,
12        instruction::{encode_instruction, TokenInstruction},
13    },
14    bytemuck::{Pod, Zeroable},
15    num_enum::{IntoPrimitive, TryFromPrimitive},
16    solana_instruction::{AccountMeta, Instruction},
17    solana_program_error::ProgramError,
18    solana_pubkey::Pubkey,
19    solana_zk_sdk::encryption::pod::{
20        auth_encryption::PodAeCiphertext,
21        elgamal::{PodElGamalCiphertext, PodElGamalPubkey},
22    },
23};
24#[cfg(not(target_os = "solana"))]
25use {
26    solana_zk_sdk::zk_elgamal_proof_program::{
27        instruction::ProofInstruction,
28        proof_data::{
29            BatchedGroupedCiphertext3HandlesValidityProofData, BatchedRangeProofU128Data,
30            CiphertextCiphertextEqualityProofData, CiphertextCommitmentEqualityProofData,
31        },
32    },
33    spl_token_confidential_transfer_proof_extraction::instruction::{
34        process_proof_location, ProofLocation,
35    },
36};
37
38/// Confidential Transfer extension instructions
39#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
40#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
41#[derive(Clone, Copy, Debug, TryFromPrimitive, IntoPrimitive)]
42#[repr(u8)]
43pub enum ConfidentialMintBurnInstruction {
44    /// Initializes confidential mints and burns for a mint.
45    ///
46    /// The `ConfidentialMintBurnInstruction::InitializeMint` instruction
47    /// requires no signers and MUST be included within the same Transaction
48    /// as `TokenInstruction::InitializeMint`. Otherwise another party can
49    /// initialize the configuration.
50    ///
51    /// The instruction fails if the `TokenInstruction::InitializeMint`
52    /// instruction has already executed for the mint.
53    ///
54    /// Accounts expected by this instruction:
55    ///
56    ///   0. `[writable]` The SPL Token mint.
57    ///
58    /// Data expected by this instruction:
59    ///   `InitializeMintData`
60    InitializeMint,
61    /// Rotates the ElGamal pubkey used to encrypt confidential supply
62    ///
63    /// The pending burn amount must be zero in order for this instruction
64    /// to be processed successfully.
65    ///
66    /// Accounts expected by this instruction:
67    ///
68    ///   * Single authority
69    ///   0. `[writable]` The SPL Token mint.
70    ///   1. `[]` Instructions sysvar if `CiphertextCiphertextEquality` is
71    ///      included in the same transaction or context state account if
72    ///      `CiphertextCiphertextEquality` is pre-verified into a context state
73    ///      account.
74    ///   2. `[signer]` Confidential mint authority.
75    ///
76    ///   * Multisignature authority
77    ///   0. `[writable]` The SPL Token mint.
78    ///   1. `[]` Instructions sysvar if `CiphertextCiphertextEquality` is
79    ///      included in the same transaction or context state account if
80    ///      `CiphertextCiphertextEquality` is pre-verified into a context state
81    ///      account.
82    ///   2. `[]` The multisig authority account owner.
83    ///   3. ..`[signer]` Required M signer accounts for the SPL Token Multisig
84    ///
85    /// Data expected by this instruction:
86    ///   `RotateSupplyElGamalPubkeyData`
87    RotateSupplyElGamalPubkey,
88    /// Updates the decryptable supply of the mint
89    ///
90    /// Accounts expected by this instruction:
91    ///
92    ///   * Single authority
93    ///   0. `[writable]` The SPL Token mint.
94    ///   1. `[signer]` Confidential mint authority.
95    ///
96    ///   * Multisignature authority
97    ///   0. `[writable]` The SPL Token mint.
98    ///   1. `[]` The multisig authority account owner.
99    ///   2. ..`[signer]` Required M signer accounts for the SPL Token Multisig
100    ///
101    /// Data expected by this instruction:
102    ///   `UpdateDecryptableSupplyData`
103    UpdateDecryptableSupply,
104    /// Mints tokens to confidential balance
105    ///
106    /// Fails if the destination account is frozen.
107    ///
108    /// Accounts expected by this instruction:
109    ///
110    ///   * Single authority
111    ///   0. `[writable]` The SPL Token account.
112    ///   1. `[writable]` The SPL Token mint.
113    ///   2. `[]` (Optional) Instructions sysvar if at least one of the
114    ///      `zk_elgamal_proof` instructions are included in the same
115    ///      transaction.
116    ///   3. `[]` (Optional) The context state account containing the
117    ///      pre-verified `VerifyCiphertextCommitmentEquality` proof
118    ///   4. `[]` (Optional) The context state account containing the
119    ///      pre-verified `VerifyBatchedGroupedCiphertext3HandlesValidity` proof
120    ///   5. `[]` (Optional) The context state account containing the
121    ///      pre-verified `VerifyBatchedRangeProofU128`
122    ///   6. `[signer]` The single account owner.
123    ///
124    ///   * Multisignature authority
125    ///   0. `[writable]` The SPL Token mint.
126    ///   1. `[]` The SPL Token mint. `[writable]` if the mint has a non-zero
127    ///      supply elgamal-pubkey
128    ///   2. `[]` (Optional) Instructions sysvar if at least one of the
129    ///      `zk_elgamal_proof` instructions are included in the same
130    ///      transaction.
131    ///   3. `[]` (Optional) The context state account containing the
132    ///      pre-verified `VerifyCiphertextCommitmentEquality` proof
133    ///   4. `[]` (Optional) The context state account containing the
134    ///      pre-verified `VerifyBatchedGroupedCiphertext3HandlesValidity` proof
135    ///   5. `[]` (Optional) The context state account containing the
136    ///      pre-verified `VerifyBatchedRangeProofU128`
137    ///   6. `[]` The multisig account owner.
138    ///   7. ..`[signer]` Required M signer accounts for the SPL Token Multisig
139    ///
140    /// Data expected by this instruction:
141    ///   `MintInstructionData`
142    Mint,
143    /// Burn tokens from confidential balance
144    ///
145    /// Fails if the destination account is frozen.
146    ///
147    /// Accounts expected by this instruction:
148    ///
149    ///   * Single authority
150    ///   0. `[writable]` The SPL Token account.
151    ///   1. `[writable]` The SPL Token mint.
152    ///   2. `[]` (Optional) Instructions sysvar if at least one of the
153    ///      `zk_elgamal_proof` instructions are included in the same
154    ///      transaction.
155    ///   3. `[]` (Optional) The context state account containing the
156    ///      pre-verified `VerifyCiphertextCommitmentEquality` proof
157    ///   4. `[]` (Optional) The context state account containing the
158    ///      pre-verified `VerifyBatchedGroupedCiphertext3HandlesValidity` proof
159    ///   5. `[]` (Optional) The context state account containing the
160    ///      pre-verified `VerifyBatchedRangeProofU128`
161    ///   6. `[signer]` The single account owner.
162    ///
163    ///   * Multisignature authority
164    ///   0. `[writable]` The SPL Token mint.
165    ///   1. `[]` The SPL Token mint. `[writable]` if the mint has a non-zero
166    ///      supply elgamal-pubkey
167    ///   2. `[]` (Optional) Instructions sysvar if at least one of the
168    ///      `zk_elgamal_proof` instructions are included in the same
169    ///      transaction.
170    ///   3. `[]` (Optional) The context state account containing the
171    ///      pre-verified `VerifyCiphertextCommitmentEquality` proof
172    ///   4. `[]` (Optional) The context state account containing the
173    ///      pre-verified `VerifyBatchedGroupedCiphertext3HandlesValidity` proof
174    ///   5. `[]` (Optional) The context state account containing the
175    ///      pre-verified `VerifyBatchedRangeProofU128`
176    ///   6. `[]` The multisig account owner.
177    ///   7. ..`[signer]` Required M signer accounts for the SPL Token Multisig
178    ///
179    /// Data expected by this instruction:
180    ///   `BurnInstructionData`
181    Burn,
182
183    /// Applies the pending burn amount to the confidential supply
184    ///
185    ///   * Single authority
186    ///   0. `[writable]` The SPL token mint.
187    ///   1. `[signer]` The single mint authority.
188    ///
189    ///   * Multisignature authority
190    ///   0. `[writable]` The SPL token mint.
191    ///   1. `[]` The multisig account owner.
192    ///   2. .. `[signer]` Required M signer accounts for the SPL Token Multisig
193    ///      account.
194    ApplyPendingBurn,
195}
196
197/// Data expected by `ConfidentialMintBurnInstruction::InitializeMint`
198#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
199#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
200#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
201#[repr(C)]
202pub struct InitializeMintData {
203    /// The ElGamal pubkey used to encrypt the confidential supply
204    #[cfg_attr(feature = "serde", serde(with = "elgamalpubkey_fromstr"))]
205    pub supply_elgamal_pubkey: PodElGamalPubkey,
206    /// The initial 0 supply encrypted with the supply aes key
207    #[cfg_attr(feature = "serde", serde(with = "aeciphertext_fromstr"))]
208    pub decryptable_supply: PodAeCiphertext,
209}
210
211/// Data expected by `ConfidentialMintBurnInstruction::RotateSupplyElGamalPubkey`
212#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
213#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
214#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
215#[repr(C)]
216pub struct RotateSupplyElGamalPubkeyData {
217    /// The new ElGamal pubkey for supply encryption
218    #[cfg_attr(feature = "serde", serde(with = "elgamalpubkey_fromstr"))]
219    pub new_supply_elgamal_pubkey: PodElGamalPubkey,
220    /// The location of the
221    /// `ProofInstruction::VerifyCiphertextCiphertextEquality` instruction
222    /// relative to the `RotateSupplyElGamalPubkey` instruction in the transaction
223    pub proof_instruction_offset: i8,
224}
225
226/// Data expected by `ConfidentialMintBurnInstruction::UpdateDecryptableSupply`
227#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
228#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
229#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
230#[repr(C)]
231pub struct UpdateDecryptableSupplyData {
232    /// The new decryptable supply
233    #[cfg_attr(feature = "serde", serde(with = "aeciphertext_fromstr"))]
234    pub new_decryptable_supply: PodAeCiphertext,
235}
236
237/// Data expected by `ConfidentialMintBurnInstruction::ConfidentialMint`
238#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
239#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
240#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
241#[repr(C)]
242pub struct MintInstructionData {
243    /// The new decryptable supply if the mint succeeds
244    #[cfg_attr(feature = "serde", serde(with = "aeciphertext_fromstr"))]
245    pub new_decryptable_supply: PodAeCiphertext,
246    /// The transfer amount encrypted under the auditor ElGamal public key
247    #[cfg_attr(feature = "serde", serde(with = "elgamalciphertext_fromstr"))]
248    pub mint_amount_auditor_ciphertext_lo: PodElGamalCiphertext,
249    /// The transfer amount encrypted under the auditor ElGamal public key
250    #[cfg_attr(feature = "serde", serde(with = "elgamalciphertext_fromstr"))]
251    pub mint_amount_auditor_ciphertext_hi: PodElGamalCiphertext,
252    /// Relative location of the
253    /// `ProofInstruction::VerifyCiphertextCommitmentEquality` instruction
254    /// to the `ConfidentialMint` instruction in the transaction. 0 if the
255    /// proof is in a pre-verified context account
256    pub equality_proof_instruction_offset: i8,
257    /// Relative location of the
258    /// `ProofInstruction::VerifyBatchedGroupedCiphertext3HandlesValidity`
259    /// instruction to the `ConfidentialMint` instruction in the
260    /// transaction. 0 if the proof is in a pre-verified context account
261    pub ciphertext_validity_proof_instruction_offset: i8,
262    /// Relative location of the `ProofInstruction::VerifyBatchedRangeProofU128`
263    /// instruction to the `ConfidentialMint` instruction in the
264    /// transaction. 0 if the proof is in a pre-verified context account
265    pub range_proof_instruction_offset: i8,
266}
267
268/// Data expected by `ConfidentialMintBurnInstruction::ConfidentialBurn`
269#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
270#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
271#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
272#[repr(C)]
273pub struct BurnInstructionData {
274    /// The new decryptable balance of the burner if the burn succeeds
275    #[cfg_attr(feature = "serde", serde(with = "aeciphertext_fromstr"))]
276    pub new_decryptable_available_balance: DecryptableBalance,
277    /// The transfer amount encrypted under the auditor ElGamal public key
278    #[cfg_attr(feature = "serde", serde(with = "elgamalciphertext_fromstr"))]
279    pub burn_amount_auditor_ciphertext_lo: PodElGamalCiphertext,
280    /// The transfer amount encrypted under the auditor ElGamal public key
281    #[cfg_attr(feature = "serde", serde(with = "elgamalciphertext_fromstr"))]
282    pub burn_amount_auditor_ciphertext_hi: PodElGamalCiphertext,
283    /// Relative location of the
284    /// `ProofInstruction::VerifyCiphertextCommitmentEquality` instruction
285    /// to the `ConfidentialMint` instruction in the transaction. 0 if the
286    /// proof is in a pre-verified context account
287    pub equality_proof_instruction_offset: i8,
288    /// Relative location of the
289    /// `ProofInstruction::VerifyBatchedGroupedCiphertext3HandlesValidity`
290    /// instruction to the `ConfidentialMint` instruction in the
291    /// transaction. 0 if the proof is in a pre-verified context account
292    pub ciphertext_validity_proof_instruction_offset: i8,
293    /// Relative location of the `ProofInstruction::VerifyBatchedRangeProofU128`
294    /// instruction to the `ConfidentialMint` instruction in the
295    /// transaction. 0 if the proof is in a pre-verified context account
296    pub range_proof_instruction_offset: i8,
297}
298
299/// Create a `InitializeMint` instruction
300pub fn initialize_mint(
301    token_program_id: &Pubkey,
302    mint: &Pubkey,
303    supply_elgamal_pubkey: &PodElGamalPubkey,
304    decryptable_supply: &DecryptableBalance,
305) -> Result<Instruction, ProgramError> {
306    check_program_account(token_program_id)?;
307    let accounts = vec![AccountMeta::new(*mint, false)];
308
309    Ok(encode_instruction(
310        token_program_id,
311        accounts,
312        TokenInstruction::ConfidentialMintBurnExtension,
313        ConfidentialMintBurnInstruction::InitializeMint,
314        &InitializeMintData {
315            supply_elgamal_pubkey: *supply_elgamal_pubkey,
316            decryptable_supply: *decryptable_supply,
317        },
318    ))
319}
320
321/// Create a `RotateSupplyElGamalPubkey` instruction
322#[allow(clippy::too_many_arguments)]
323#[cfg(not(target_os = "solana"))]
324pub fn rotate_supply_elgamal_pubkey(
325    token_program_id: &Pubkey,
326    mint: &Pubkey,
327    authority: &Pubkey,
328    multisig_signers: &[&Pubkey],
329    new_supply_elgamal_pubkey: &PodElGamalPubkey,
330    ciphertext_equality_proof: ProofLocation<CiphertextCiphertextEqualityProofData>,
331) -> Result<Vec<Instruction>, ProgramError> {
332    check_program_account(token_program_id)?;
333    let mut accounts = vec![AccountMeta::new(*mint, false)];
334
335    let mut expected_instruction_offset = 1;
336    let mut proof_instructions = vec![];
337
338    let proof_instruction_offset = process_proof_location(
339        &mut accounts,
340        &mut expected_instruction_offset,
341        &mut proof_instructions,
342        ciphertext_equality_proof,
343        true,
344        ProofInstruction::VerifyCiphertextCiphertextEquality,
345    )?;
346
347    accounts.push(AccountMeta::new_readonly(
348        *authority,
349        multisig_signers.is_empty(),
350    ));
351    for multisig_signer in multisig_signers.iter() {
352        accounts.push(AccountMeta::new_readonly(**multisig_signer, true));
353    }
354
355    let mut instructions = vec![encode_instruction(
356        token_program_id,
357        accounts,
358        TokenInstruction::ConfidentialMintBurnExtension,
359        ConfidentialMintBurnInstruction::RotateSupplyElGamalPubkey,
360        &RotateSupplyElGamalPubkeyData {
361            new_supply_elgamal_pubkey: *new_supply_elgamal_pubkey,
362            proof_instruction_offset,
363        },
364    )];
365
366    instructions.extend(proof_instructions);
367
368    Ok(instructions)
369}
370
371/// Create a `UpdateDecryptableSupply` instruction
372#[cfg(not(target_os = "solana"))]
373pub fn update_decryptable_supply(
374    token_program_id: &Pubkey,
375    mint: &Pubkey,
376    authority: &Pubkey,
377    multisig_signers: &[&Pubkey],
378    new_decryptable_supply: &DecryptableBalance,
379) -> Result<Instruction, ProgramError> {
380    check_program_account(token_program_id)?;
381    let mut accounts = vec![
382        AccountMeta::new(*mint, false),
383        AccountMeta::new_readonly(*authority, multisig_signers.is_empty()),
384    ];
385    for multisig_signer in multisig_signers.iter() {
386        accounts.push(AccountMeta::new_readonly(**multisig_signer, true));
387    }
388    Ok(encode_instruction(
389        token_program_id,
390        accounts,
391        TokenInstruction::ConfidentialMintBurnExtension,
392        ConfidentialMintBurnInstruction::UpdateDecryptableSupply,
393        &UpdateDecryptableSupplyData {
394            new_decryptable_supply: *new_decryptable_supply,
395        },
396    ))
397}
398
399/// Context state accounts used in confidential mint
400#[derive(Clone, Copy)]
401pub struct MintSplitContextStateAccounts<'a> {
402    /// Location of equality proof
403    pub equality_proof: &'a Pubkey,
404    /// Location of ciphertext validity proof
405    pub ciphertext_validity_proof: &'a Pubkey,
406    /// Location of range proof
407    pub range_proof: &'a Pubkey,
408    /// Authority able to close proof accounts
409    pub authority: &'a Pubkey,
410}
411
412/// Create a `ConfidentialMint` instruction
413#[allow(clippy::too_many_arguments)]
414#[cfg(not(target_os = "solana"))]
415pub fn confidential_mint_with_split_proofs(
416    token_program_id: &Pubkey,
417    token_account: &Pubkey,
418    mint: &Pubkey,
419    mint_amount_auditor_ciphertext_lo: &PodElGamalCiphertext,
420    mint_amount_auditor_ciphertext_hi: &PodElGamalCiphertext,
421    authority: &Pubkey,
422    multisig_signers: &[&Pubkey],
423    equality_proof_location: ProofLocation<CiphertextCommitmentEqualityProofData>,
424    ciphertext_validity_proof_location: ProofLocation<
425        BatchedGroupedCiphertext3HandlesValidityProofData,
426    >,
427    range_proof_location: ProofLocation<BatchedRangeProofU128Data>,
428    new_decryptable_supply: &DecryptableBalance,
429) -> Result<Vec<Instruction>, ProgramError> {
430    check_program_account(token_program_id)?;
431    let mut accounts = vec![
432        AccountMeta::new(*token_account, false),
433        AccountMeta::new(*mint, false),
434    ];
435
436    let mut expected_instruction_offset = 1;
437    let mut proof_instructions = vec![];
438
439    let equality_proof_instruction_offset = process_proof_location(
440        &mut accounts,
441        &mut expected_instruction_offset,
442        &mut proof_instructions,
443        equality_proof_location,
444        true,
445        ProofInstruction::VerifyCiphertextCommitmentEquality,
446    )?;
447
448    let ciphertext_validity_proof_instruction_offset = process_proof_location(
449        &mut accounts,
450        &mut expected_instruction_offset,
451        &mut proof_instructions,
452        ciphertext_validity_proof_location,
453        false,
454        ProofInstruction::VerifyBatchedGroupedCiphertext3HandlesValidity,
455    )?;
456
457    let range_proof_instruction_offset = process_proof_location(
458        &mut accounts,
459        &mut expected_instruction_offset,
460        &mut proof_instructions,
461        range_proof_location,
462        false,
463        ProofInstruction::VerifyBatchedRangeProofU128,
464    )?;
465
466    accounts.push(AccountMeta::new_readonly(
467        *authority,
468        multisig_signers.is_empty(),
469    ));
470    for multisig_signer in multisig_signers.iter() {
471        accounts.push(AccountMeta::new_readonly(**multisig_signer, true));
472    }
473
474    let mut instructions = vec![encode_instruction(
475        token_program_id,
476        accounts,
477        TokenInstruction::ConfidentialMintBurnExtension,
478        ConfidentialMintBurnInstruction::Mint,
479        &MintInstructionData {
480            new_decryptable_supply: *new_decryptable_supply,
481            mint_amount_auditor_ciphertext_lo: *mint_amount_auditor_ciphertext_lo,
482            mint_amount_auditor_ciphertext_hi: *mint_amount_auditor_ciphertext_hi,
483            equality_proof_instruction_offset,
484            ciphertext_validity_proof_instruction_offset,
485            range_proof_instruction_offset,
486        },
487    )];
488
489    instructions.extend(proof_instructions);
490
491    Ok(instructions)
492}
493
494/// Create a inner `ConfidentialBurn` instruction
495#[allow(clippy::too_many_arguments)]
496#[cfg(not(target_os = "solana"))]
497pub fn confidential_burn_with_split_proofs(
498    token_program_id: &Pubkey,
499    token_account: &Pubkey,
500    mint: &Pubkey,
501    new_decryptable_available_balance: &DecryptableBalance,
502    burn_amount_auditor_ciphertext_lo: &PodElGamalCiphertext,
503    burn_amount_auditor_ciphertext_hi: &PodElGamalCiphertext,
504    authority: &Pubkey,
505    multisig_signers: &[&Pubkey],
506    equality_proof_location: ProofLocation<CiphertextCommitmentEqualityProofData>,
507    ciphertext_validity_proof_location: ProofLocation<
508        BatchedGroupedCiphertext3HandlesValidityProofData,
509    >,
510    range_proof_location: ProofLocation<BatchedRangeProofU128Data>,
511) -> Result<Vec<Instruction>, ProgramError> {
512    check_program_account(token_program_id)?;
513    let mut accounts = vec![
514        AccountMeta::new(*token_account, false),
515        AccountMeta::new(*mint, false),
516    ];
517
518    let mut expected_instruction_offset = 1;
519    let mut proof_instructions = vec![];
520
521    let equality_proof_instruction_offset = process_proof_location(
522        &mut accounts,
523        &mut expected_instruction_offset,
524        &mut proof_instructions,
525        equality_proof_location,
526        true,
527        ProofInstruction::VerifyCiphertextCommitmentEquality,
528    )?;
529
530    let ciphertext_validity_proof_instruction_offset = process_proof_location(
531        &mut accounts,
532        &mut expected_instruction_offset,
533        &mut proof_instructions,
534        ciphertext_validity_proof_location,
535        false,
536        ProofInstruction::VerifyBatchedGroupedCiphertext3HandlesValidity,
537    )?;
538
539    let range_proof_instruction_offset = process_proof_location(
540        &mut accounts,
541        &mut expected_instruction_offset,
542        &mut proof_instructions,
543        range_proof_location,
544        false,
545        ProofInstruction::VerifyBatchedRangeProofU128,
546    )?;
547
548    accounts.push(AccountMeta::new_readonly(
549        *authority,
550        multisig_signers.is_empty(),
551    ));
552
553    for multisig_signer in multisig_signers.iter() {
554        accounts.push(AccountMeta::new_readonly(**multisig_signer, true));
555    }
556
557    let mut instructions = vec![encode_instruction(
558        token_program_id,
559        accounts,
560        TokenInstruction::ConfidentialMintBurnExtension,
561        ConfidentialMintBurnInstruction::Burn,
562        &BurnInstructionData {
563            new_decryptable_available_balance: *new_decryptable_available_balance,
564            burn_amount_auditor_ciphertext_lo: *burn_amount_auditor_ciphertext_lo,
565            burn_amount_auditor_ciphertext_hi: *burn_amount_auditor_ciphertext_hi,
566            equality_proof_instruction_offset,
567            ciphertext_validity_proof_instruction_offset,
568            range_proof_instruction_offset,
569        },
570    )];
571
572    instructions.extend(proof_instructions);
573
574    Ok(instructions)
575}
576
577/// Create a `ApplyPendingBurn` instruction
578pub fn apply_pending_burn(
579    token_program_id: &Pubkey,
580    mint: &Pubkey,
581    authority: &Pubkey,
582    multisig_signers: &[&Pubkey],
583) -> Result<Instruction, ProgramError> {
584    check_program_account(token_program_id)?;
585    let mut accounts = vec![
586        AccountMeta::new(*mint, false),
587        AccountMeta::new_readonly(*authority, multisig_signers.is_empty()),
588    ];
589    for multisig_signer in multisig_signers.iter() {
590        accounts.push(AccountMeta::new_readonly(**multisig_signer, true));
591    }
592    Ok(encode_instruction(
593        token_program_id,
594        accounts,
595        TokenInstruction::ConfidentialMintBurnExtension,
596        ConfidentialMintBurnInstruction::ApplyPendingBurn,
597        &(),
598    ))
599}