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#[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 InitializeMint,
61 RotateSupplyElGamalPubkey,
88 UpdateDecryptableSupply,
104 Mint,
143 Burn,
182
183 ApplyPendingBurn,
195}
196
197#[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 #[cfg_attr(feature = "serde", serde(with = "elgamalpubkey_fromstr"))]
205 pub supply_elgamal_pubkey: PodElGamalPubkey,
206 #[cfg_attr(feature = "serde", serde(with = "aeciphertext_fromstr"))]
208 pub decryptable_supply: PodAeCiphertext,
209}
210
211#[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 #[cfg_attr(feature = "serde", serde(with = "elgamalpubkey_fromstr"))]
219 pub new_supply_elgamal_pubkey: PodElGamalPubkey,
220 pub proof_instruction_offset: i8,
224}
225
226#[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 #[cfg_attr(feature = "serde", serde(with = "aeciphertext_fromstr"))]
234 pub new_decryptable_supply: PodAeCiphertext,
235}
236
237#[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 #[cfg_attr(feature = "serde", serde(with = "aeciphertext_fromstr"))]
245 pub new_decryptable_supply: PodAeCiphertext,
246 #[cfg_attr(feature = "serde", serde(with = "elgamalciphertext_fromstr"))]
248 pub mint_amount_auditor_ciphertext_lo: PodElGamalCiphertext,
249 #[cfg_attr(feature = "serde", serde(with = "elgamalciphertext_fromstr"))]
251 pub mint_amount_auditor_ciphertext_hi: PodElGamalCiphertext,
252 pub equality_proof_instruction_offset: i8,
257 pub ciphertext_validity_proof_instruction_offset: i8,
262 pub range_proof_instruction_offset: i8,
266}
267
268#[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 #[cfg_attr(feature = "serde", serde(with = "aeciphertext_fromstr"))]
276 pub new_decryptable_available_balance: DecryptableBalance,
277 #[cfg_attr(feature = "serde", serde(with = "elgamalciphertext_fromstr"))]
279 pub burn_amount_auditor_ciphertext_lo: PodElGamalCiphertext,
280 #[cfg_attr(feature = "serde", serde(with = "elgamalciphertext_fromstr"))]
282 pub burn_amount_auditor_ciphertext_hi: PodElGamalCiphertext,
283 pub equality_proof_instruction_offset: i8,
288 pub ciphertext_validity_proof_instruction_offset: i8,
293 pub range_proof_instruction_offset: i8,
297}
298
299pub 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#[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#[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#[derive(Clone, Copy)]
401pub struct MintSplitContextStateAccounts<'a> {
402 pub equality_proof: &'a Pubkey,
404 pub ciphertext_validity_proof: &'a Pubkey,
406 pub range_proof: &'a Pubkey,
408 pub authority: &'a Pubkey,
410}
411
412#[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#[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
577pub 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}