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#[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 InitializeMint,
66 RotateSupplyElGamalPubkey,
90 UpdateDecryptableSupply,
106 Mint,
146 Burn,
186}
187
188#[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 #[cfg_attr(feature = "serde-traits", serde(with = "elgamalpubkey_fromstr"))]
196 pub supply_elgamal_pubkey: PodElGamalPubkey,
197 #[cfg_attr(feature = "serde-traits", serde(with = "aeciphertext_fromstr"))]
199 pub decryptable_supply: PodAeCiphertext,
200}
201
202#[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 #[cfg_attr(feature = "serde-traits", serde(with = "elgamalpubkey_fromstr"))]
210 pub new_supply_elgamal_pubkey: PodElGamalPubkey,
211 pub proof_instruction_offset: i8,
215}
216
217#[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 #[cfg_attr(feature = "serde-traits", serde(with = "aeciphertext_fromstr"))]
225 pub new_decryptable_supply: PodAeCiphertext,
226}
227
228#[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 #[cfg_attr(feature = "serde-traits", serde(with = "aeciphertext_fromstr"))]
236 pub new_decryptable_supply: PodAeCiphertext,
237 #[cfg_attr(feature = "serde-traits", serde(with = "elgamalciphertext_fromstr"))]
239 pub mint_amount_auditor_ciphertext_lo: PodElGamalCiphertext,
240 #[cfg_attr(feature = "serde-traits", serde(with = "elgamalciphertext_fromstr"))]
242 pub mint_amount_auditor_ciphertext_hi: PodElGamalCiphertext,
243 pub equality_proof_instruction_offset: i8,
248 pub ciphertext_validity_proof_instruction_offset: i8,
253 pub range_proof_instruction_offset: i8,
257}
258
259#[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 #[cfg_attr(feature = "serde-traits", serde(with = "aeciphertext_fromstr"))]
267 pub new_decryptable_available_balance: DecryptableBalance,
268 #[cfg_attr(feature = "serde-traits", serde(with = "elgamalciphertext_fromstr"))]
270 pub burn_amount_auditor_ciphertext_lo: PodElGamalCiphertext,
271 #[cfg_attr(feature = "serde-traits", serde(with = "elgamalciphertext_fromstr"))]
273 pub burn_amount_auditor_ciphertext_hi: PodElGamalCiphertext,
274 pub equality_proof_instruction_offset: i8,
279 pub ciphertext_validity_proof_instruction_offset: i8,
284 pub range_proof_instruction_offset: i8,
288}
289
290pub 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#[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#[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#[derive(Clone, Copy)]
392pub struct MintSplitContextStateAccounts<'a> {
393 pub equality_proof: &'a Pubkey,
395 pub ciphertext_validity_proof: &'a Pubkey,
397 pub range_proof: &'a Pubkey,
399 pub authority: &'a Pubkey,
401}
402
403#[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 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#[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}