spl_token_2022/extension/confidential_mint_burn/
instruction.rs

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