Transferência Confidencial
O que são Transferências Confidenciais?
As transferências confidenciais permitem que você transfira tokens entre contas de token sem revelar o valor da transferência. Isso é útil para transações que preservam a privacidade. Apenas os valores de transferência e saldos de tokens são privados. Os endereços das contas de token permanecem públicos.
- Visão geral do protocolo - Detalhes sobre o protocolo criptográfico subjacente
- Guia de início rápido - Configuração e comandos básicos de CLI
- Cookbook de Saldos Confidenciais - Trechos de código sobre como usar a extensão de Transferência Confidencial
Como funciona?
A extensão de Transferência Confidencial adiciona instruções ao Token Extensions Program que permite transferir tokens entre contas sem revelar o valor da transferência.
O fluxo básico de transferências confidenciais de tokens é o seguinte:
- Criar uma mint account com a extensão de transferência confidencial.
- Criar token accounts com extensão de transferência confidencial para o remetente e destinatário.
- Emitir tokens para a conta do remetente.
- Depositar o saldo público do remetente para o saldo pendente confidencial.
- Aplicar o saldo pendente do remetente para o saldo disponível confidencial.
- Transferir confidencialmente tokens da token account do remetente para a token account do destinatário.
- Aplicar o saldo pendente do destinatário para o saldo disponível confidencial.
- Retirar o saldo disponível confidencial do destinatário para o saldo público.
Para mais detalhes sobre as etapas no fluxo de transferência confidencial, veja as páginas correspondentes:
Criar Mint Account
Como criar uma mint account com a extensão de Transferência Confidencial
Criar Token Account
Como configurar uma token account com a extensão de Transferência Confidencial
Depositar Tokens
Como depositar tokens no saldo pendente confidencial
Aplicar Saldo Pendente
Como aplicar o saldo pendente ao saldo confidencial disponível
Retirar Tokens
Como retirar tokens do saldo confidencial disponível
Transferir Tokens
Como transferir tokens confidencialmente entre token accounts
O diagrama abaixo mostra uma sequência detalhada do fluxo básico para transferências confidenciais de tokens:
Código de exemplo
O exemplo em Rust abaixo mostra os passos para transferências confidenciais de tokens correspondentes ao diagrama anterior. Os passos estão separados em funções auxiliares para maior clareza.
Para executar o exemplo, inicie um validador local com o Token Extensions Program clonado da mainnet usando o seguinte comando. Você deve ter a CLI da Solana instalada para usar o validador local.
$solana-test-validator --clone-upgradeable-program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb --url https://api.mainnet-beta.solana.com -r
No momento da escrita, as Transferências Confidenciais não estão habilitadas no validador local padrão. Você deve clonar o Token Extensions Program da mainnet para executar o código de exemplo.
use anyhow::{Context, Result};use solana_client::nonblocking::rpc_client::RpcClient;use solana_sdk::{commitment_config::CommitmentConfig,signature::{Keypair, Signer},transaction::Transaction,pubkey::Pubkey};use spl_associated_token_account::{get_associated_token_address_with_program_id, instruction::create_associated_token_account,};use spl_token_client::{client::{ProgramRpcClient, ProgramRpcClientSendTransaction},spl_token_2022::{extension::{confidential_transfer::{instruction::{configure_account, PubkeyValidityProofData},account_info::{WithdrawAccountInfo, TransferAccountInfo},ConfidentialTransferAccount,},BaseStateWithExtensions, ExtensionType,},id as token_2022_program_id,instruction::reallocate,solana_zk_sdk::encryption::{auth_encryption::*, elgamal::*},},token::{ExtensionInitializationParams, Token},};use spl_token_confidential_transfer_proof_extraction::instruction::{ProofData, ProofLocation};use spl_token_confidential_transfer_proof_generation::withdraw::WithdrawProofData;use std::sync::Arc;/// Define a type alias for the Token client to simplify type signaturestype TokenClient = Token<ProgramRpcClientSendTransaction>;/// Demonstrates the complete lifecycle of confidential tokens:/// - Creating a mint with confidential transfer extension/// - Creating token accounts configured for confidential transfer/// - Minting tokens/// - Depositing tokens from public balance to confidential pending balance/// - Applying pending balance to available balance/// - Transferring tokens confidentially between token accounts/// - Withdrawing tokens from confidential available balance to public balance#[tokio::main]async fn main() -> Result<()> {// Create connection to local test validator with confirmed commitment level// This ensures transactions are confirmed before proceedinglet rpc_client = Arc::new(RpcClient::new_with_commitment(String::from("http://localhost:8899"),CommitmentConfig::confirmed(),));// Load the default Solana CLI keypair to use as the fee payerlet payer = Arc::new(load_keypair()?);println!("Payer: {}", payer.pubkey());// Generate a new keypair for the mint// This pubkey will be the address of the token mintlet mint = Keypair::new();println!("Mint keypair generated: {}", mint.pubkey());// Number of decimals for the mint// This determines the smallest unit of the token (e.g., 2 decimals means 0.01 is the smallest unit)let decimals = 2;// Create a program clientlet program_client = ProgramRpcClient::new(rpc_client.clone(), ProgramRpcClientSendTransaction);// Create a token client// This client provides high-level methods to interact with the token programlet token = Token::new(Arc::new(program_client),&token_2022_program_id(),&mint.pubkey(),Some(decimals),payer.clone(),);// Create a new mint with confidential transfer extension// This initializes the token mint with confidential transfer enabledcreate_mint(&token, &mint, payer.clone()).await?;// Create first token account (sender)// This account will be used to send tokens confidentiallylet (sender_pubkey, sender_elgamal_keypair, sender_aes_key) = create_token_account(rpc_client.clone(),payer.clone(),&mint,payer.clone(),&token_2022_program_id(),).await?;// Create second token account (recipient) with a new owner// This account will receive tokens confidentiallylet recipient_owner = Arc::new(Keypair::new());println!("Recipient owner: {}", recipient_owner.pubkey());// Fund the recipient owner account with SOL// This ensures the recipient can pay for transaction feesfund_account(rpc_client.clone(),payer.clone(),&recipient_owner.pubkey(),10_000_000, // 0.01 SOL in lamports).await?;// Create the recipient token account// This account will receive tokens from the senderlet (recipient_pubkey, recipient_elgamal_keypair, recipient_aes_key) = create_token_account(rpc_client.clone(),recipient_owner.clone(),&mint,recipient_owner.clone(),&token_2022_program_id(),).await?;// Mint tokens to sender account// This creates new tokens and increases the sender's token account public balancelet amount = 100 * 10u64.pow(decimals as u32); // 100 tokensmint_tokens(&token, &sender_pubkey, &payer, amount).await?;// Deposit tokens to confidential pending balance// This converts public balance to confidential pending balancedeposit_tokens(&token, &sender_pubkey, &payer, amount, decimals).await?;// Apply pending balance to make funds available// This converts confidential pending balance to confidential available balanceapply_pending_balance(&token,&sender_pubkey,&payer,&sender_elgamal_keypair,&sender_aes_key,).await?;// Transfer tokens from sender to recipient// This sends tokens confidentially from sender token account to recipient token accountlet transfer_amount = 50 * 10u64.pow(decimals as u32); // 50 tokenstransfer_tokens(&token,&sender_pubkey,&payer,&sender_elgamal_keypair,&sender_aes_key,&recipient_pubkey,recipient_elgamal_keypair.pubkey(),transfer_amount,payer.clone(),).await?;// Apply pending balance on recipient token account// This converts confidential pending balance to confidential available balanceapply_pending_balance(&token,&recipient_pubkey,&recipient_owner,&recipient_elgamal_keypair,&recipient_aes_key,).await?;// Withdraw half of the tokens from recipient's confidential available balance// This converts confidential available balance to public balancelet withdraw_amount = 25 * 10u64.pow(decimals as u32); // half of the 50 tokens receivedwithdraw_tokens(&token,&recipient_pubkey,&recipient_owner,&recipient_elgamal_keypair,&recipient_aes_key,withdraw_amount,recipient_owner.clone(),decimals,).await?;println!("\nAll operations completed successfully!");println!("Sender Account: {}", sender_pubkey);println!("Recipient Account: {}", recipient_pubkey);Ok(())}/// Load the keypair from the default Solana CLI keypair path////// Returns:/// - Result<Keypair>: The loaded keypair or an error if loading failsfn load_keypair() -> Result<Keypair> {// Get the default keypair path from the home directorylet keypair_path = dirs::home_dir().context("Could not find home directory")?.join(".config/solana/id.json");// Read the keypair filelet file = std::fs::File::open(&keypair_path)?;let keypair_bytes: Vec<u8> = serde_json::from_reader(file)?;// Create keypairlet keypair = Keypair::from_bytes(&keypair_bytes)?;Ok(keypair)}/// Create a new token mint with confidential transfer extension////// Arguments:/// - token: Reference to the token client for interacting with the Token-2022 program/// - mint: Reference to the keypair that will be used as the mint account's address/// - payer: Arc-wrapped keypair that will pay for the transaction fees and serve as the mint authority////// Returns:/// - Result<()>: Success or errorasync fn create_mint(token: &TokenClient, mint: &Keypair, payer: Arc<Keypair>) -> Result<()> {println!("Creating token mint with confidential transfer extension...");// Create extension initialization parameters for the mint// This configures the confidential transfer extension on the mintlet extension_initialization_params =vec![ExtensionInitializationParams::ConfidentialTransferMint {authority: Some(payer.pubkey()), // Authority that can modify confidential transfer settingsauto_approve_new_accounts: true, // Automatically approve new accounts for confidential transfersauditor_elgamal_pubkey: None, // No auditor for this example}];// Create and initialize the mint with the ConfidentialTransferMint extensionlet transaction_signature = token.create_mint(&payer.pubkey(), // Mint authoritySome(&payer.pubkey()), // Freeze authority (optional)extension_initialization_params, // Extension parameters&[mint], // Signers needed for the transaction).await?;println!("Mint Address: {}", mint.pubkey());println!("Mint Creation Transaction Signature: {}",transaction_signature);Ok(())}/// Fund an account with SOL////// Arguments:/// - rpc_client: Arc-wrapped RPC client for communicating with the Solana cluster/// - payer: Arc-wrapped keypair that will send the SOL/// - recipient: Public key of the account that will receive the SOL/// - amount: Amount of lamports (1 SOL = 1,000,000,000 lamports) to send////// Returns:/// - Result<()>: Success or errorasync fn fund_account(rpc_client: Arc<RpcClient>,payer: Arc<Keypair>,recipient: &Pubkey,amount: u64,) -> Result<()> {println!("Funding account {} with {} lamports...", recipient, amount);// Create and send a transaction to transfer SOL from payer to recipientlet fund_signature = rpc_client.send_and_confirm_transaction(&Transaction::new_signed_with_payer(&[solana_sdk::system_instruction::transfer(&payer.pubkey(), // Fromrecipient, // Toamount, // Amount in lamports)],Some(&payer.pubkey()), // Fee payer&[&payer], // Signersrpc_client.get_latest_blockhash().await?, // Recent blockhash)).await?;println!("Fund Transaction Signature: {}", fund_signature);Ok(())}/// Create and configure a token account for confidential transfers////// Arguments:/// - rpc_client: Arc-wrapped RPC client for communicating with the Solana cluster/// - payer: Arc-wrapped keypair that will pay for the transaction fees/// - mint: Reference to the keypair of the token mint/// - owner: Arc-wrapped keypair that will own the token account/// - token_program_id: Public key of the token program (Token-2022)////// Returns:/// - Result<(Pubkey, ElGamalKeypair, AeKey)>: The token account address, ElGamal keypair, and AES key for encryptionasync fn create_token_account(rpc_client: Arc<RpcClient>,payer: Arc<Keypair>,mint: &Keypair,owner: Arc<Keypair>,token_program_id: &Pubkey,) -> Result<(Pubkey, ElGamalKeypair, AeKey)> {println!("Creating token account for owner: {}", owner.pubkey());// Get the associated token account address for the owner// This is a deterministic address based on the owner and mintlet token_account_pubkey = get_associated_token_address_with_program_id(&owner.pubkey(),&mint.pubkey(),token_program_id,);println!("Token Account Address: {}", token_account_pubkey);// Step 1: Create the associated token account// This creates a token account at the deterministic addresslet create_associated_token_account_instruction = create_associated_token_account(&payer.pubkey(), // Funding account&owner.pubkey(), // Wallet address&mint.pubkey(), // Mint addresstoken_program_id, // Token program ID);// Step 2: Reallocate the token account to include space for the ConfidentialTransferAccount extension// This increases the account size to accommodate the confidential transfer datalet reallocate_instruction = reallocate(token_program_id, // Token program ID&token_account_pubkey, // Token account to reallocate&payer.pubkey(), // Payer for the reallocation&owner.pubkey(), // Owner of the token account&[&owner.pubkey()], // Signers required for the reallocation&[ExtensionType::ConfidentialTransferAccount], // Extension to allocate space for)?;// Step 3: Generate the ElGamal keypair and AES key for token account// These are used for encrypting and decrypting confidential token amountslet elgamal_keypair = ElGamalKeypair::new_from_signer(&owner, &token_account_pubkey.to_bytes()).expect("Failed to create ElGamal keypair");let aes_key = AeKey::new_from_signer(&owner, &token_account_pubkey.to_bytes()).expect("Failed to create AES key");// The maximum number of Deposit and Transfer instructions that can// credit pending_balance before the ApplyPendingBalance instruction must be executedlet maximum_pending_balance_credit_counter = 65536;// Initial token balance is 0// This is encrypted using the AES keylet decryptable_balance = aes_key.encrypt(0);// Generate the proof data client-side// This proves that the ElGamal public key is validlet proof_data = PubkeyValidityProofData::new(&elgamal_keypair).map_err(|_| anyhow::anyhow!("Failed to generate proof data"))?;// Indicate that proof is included as instruction data// This specifies where to find the proof data in the transactionlet proof_location =ProofLocation::InstructionOffset(1.try_into()?, ProofData::InstructionData(&proof_data));// Step 4: Create instructions to configure the account for confidential transfers// This initializes the confidential transfer extension on the token accountlet configure_account_instructions = configure_account(token_program_id, // Token program ID&token_account_pubkey, // Token account to configure&mint.pubkey(), // Mint of the token&decryptable_balance.into(), // Initial encrypted balancemaximum_pending_balance_credit_counter, // Maximum pending balance credit counter&owner.pubkey(), // Owner of the token account&[], // Additional signers (none needed)proof_location, // Location of the proof data)?;// Combine all instructionslet mut instructions = vec![create_associated_token_account_instruction,reallocate_instruction,];instructions.extend(configure_account_instructions);// Create and send the transactionlet recent_blockhash = rpc_client.get_latest_blockhash().await?;let transaction = Transaction::new_signed_with_payer(&instructions, // Instructions to executeSome(&payer.pubkey()), // Fee payer&[&payer], // Signersrecent_blockhash, // Recent blockhash);let transaction_signature = rpc_client.send_and_confirm_transaction(&transaction).await?;println!("Create Token Account Transaction Signature: {}",transaction_signature);Ok((token_account_pubkey, elgamal_keypair, aes_key))}/// Mint tokens to a token account////// Arguments:/// - token: Reference to the token client for interacting with the Token-2022 program/// - token_account: Public key of the token account that will receive the tokens/// - mint_authority: Arc-wrapped keypair that has authority to mint tokens/// - amount: Amount of tokens to mint (in the smallest units)////// Returns:/// - Result<()>: Success or errorasync fn mint_tokens(token: &TokenClient,token_account: &Pubkey,mint_authority: &Arc<Keypair>,amount: u64,) -> Result<()> {println!("Minting {} tokens to account: {}", amount, token_account);// Mint tokens to the specified token accountlet mint_signature = token.mint_to(token_account, // Destination token account&mint_authority.pubkey(), // Mint authorityamount, // Amount to mint&[&mint_authority], // Signers).await?;println!("Token Minting Transaction Signature: {}", mint_signature);Ok(())}/// Deposit tokens to confidential state////// Arguments:/// - token: Reference to the token client for interacting with the Token-2022 program/// - token_account: Public key of the token account with public balance/// - owner: Arc-wrapped keypair that owns the token account/// - amount: Amount of tokens to deposit into confidential pending balance/// - decimals: Number of decimal for the mint////// Returns:/// - Result<()>: Success or errorasync fn deposit_tokens(token: &TokenClient,token_account: &Pubkey,owner: &Arc<Keypair>,amount: u64,decimals: u8,) -> Result<()> {println!("Depositing {} tokens to confidential pending balance...", amount);// Deposit tokens to confidential pending balancelet deposit_signature = token.confidential_transfer_deposit(token_account, // Token account&owner.pubkey(), // Owner of the token accountamount, // Amount to depositdecimals, // Decimal for the mint&[&owner], // Signers).await?;println!("Confidential Transfer Deposit Signature: {}",deposit_signature);Ok(())}/// Apply pending balance to make funds available////// Arguments:/// - token: Reference to the token client for interacting with the Token-2022 program/// - token_account: Public key of the token account/// - owner: Arc-wrapped keypair that owns the token account/// - elgamal_keypair: ElGamal keypair for decrypting confidential amounts/// - aes_key: AES key for decrypting confidential amounts////// Returns:/// - Result<()>: Success or errorasync fn apply_pending_balance(token: &TokenClient,token_account: &Pubkey,owner: &Arc<Keypair>,elgamal_keypair: &ElGamalKeypair,aes_key: &AeKey,) -> Result<()> {println!("Applying pending balance for account: {}", token_account);// Apply pending balance to convert confidential pending balance to confidential available balancelet apply_signature = token.confidential_transfer_apply_pending_balance(token_account, // Token account&owner.pubkey(), // Owner of the token accountNone, // Optional auditor public key (none in this example)elgamal_keypair.secret(), // ElGamal secret key for decryptionaes_key, // AES key for decryption&[&owner], // Signers).await?;println!("Apply Pending Balance Signature: {}", apply_signature);Ok(())}/// Withdraw tokens from confidential state////// Arguments:/// - token: Reference to the token client for interacting with the Token-2022 program/// - token_account: Public key of the token account/// - owner: Arc-wrapped keypair that owns the token account/// - elgamal_keypair: ElGamal keypair for decrypting confidential amounts/// - aes_key: AES key for decrypting confidential amounts/// - amount: Amount of tokens to withdraw from confidential available balance/// - payer: Arc-wrapped keypair that will pay for the transaction fees/// - decimals: Number of decimal for the mint////// Returns:/// - Result<()>: Success or error#[allow(clippy::too_many_arguments)]async fn withdraw_tokens(token: &TokenClient,token_account: &Pubkey,owner: &Arc<Keypair>,elgamal_keypair: &ElGamalKeypair,aes_key: &AeKey,amount: u64,payer: Arc<Keypair>,decimals: u8,) -> Result<()> {println!("Withdrawing {} tokens from confidential available balance to public balance...", amount);// Get the token account data to access the confidential transfer extension statelet token_account_data = token.get_account_info(token_account).await?;// Unpack the ConfidentialTransferAccount extension portion of the token account datalet extension_data = token_account_data.get_extension::<ConfidentialTransferAccount>()?;// Confidential Transfer extension state from token account needed to construct a `Withdraw` instructionlet withdraw_account_info =WithdrawAccountInfo::new(extension_data,);// Create keypairs for the proof context state accounts// These accounts will store the context state from the equality and range proofs required for the withdrawallet equality_proof_context_state_keypair = Keypair::new();let equality_proof_context_state_pubkey = equality_proof_context_state_keypair.pubkey();let range_proof_context_state_keypair = Keypair::new();let range_proof_context_state_pubkey = range_proof_context_state_keypair.pubkey();// Create a withdraw proof data// This generates the zero-knowledge proofs needed for the withdrawallet WithdrawProofData {equality_proof_data,range_proof_data,} = withdraw_account_info.generate_proof_data(amount, elgamal_keypair, aes_key)?;// Generate the equality proof context state account// This creates an account to store the equality proof context stateprintln!("Creating equality proof context state account...");let equality_proof_signature = token.confidential_transfer_create_context_state_account(&equality_proof_context_state_pubkey, // Account to create&payer.pubkey(), // Payer for the account creation&equality_proof_data, // Proof data to verifyfalse, // Not a range proof&[&equality_proof_context_state_keypair], // Signers).await?;println!("Create Equality Proof Context State Account: {}",equality_proof_signature);// Generate the range proof context state account// This creates an account to store the range proof context stateprintln!("Creating range proof context state account...");let range_proof_signature = token.confidential_transfer_create_context_state_account(&range_proof_context_state_pubkey, // Account to create&payer.pubkey(), // Payer for the account creation&range_proof_data, // Proof data to verifytrue, // Is a range proof&[&range_proof_context_state_keypair], // Signers).await?;println!("Create Range Proof Context State Account: {}",range_proof_signature);// Perform the withdrawal// This converts confidential available balance back to public balanceprintln!("Executing withdrawal transaction...");let withdraw_signature = token.confidential_transfer_withdraw(token_account, // Token account&owner.pubkey(), // Owner of the token accountSome(&spl_token_client::token::ProofAccount::ContextAccount(equality_proof_context_state_pubkey, // Equality proof account)),Some(&spl_token_client::token::ProofAccount::ContextAccount(range_proof_context_state_pubkey, // Range proof account)),amount, // Amount to withdrawdecimals, // Decimal for the mintSome(withdraw_account_info), // Data from token account required for withdrawalelgamal_keypair, // ElGamal keypair for decryptionaes_key, // AES key for decryption&[&owner], // Signers).await?;println!("Withdraw Transaction Signature: {}", withdraw_signature);// Close the context state accounts to recover rent// This reclaims the SOL used to rent space for the proof accountsprintln!("Closing proof context state accounts...");let close_equality_signature = token.confidential_transfer_close_context_state_account(&equality_proof_context_state_pubkey, // Account to closetoken_account, // Destination for the rent&payer.pubkey(), // Authority to close the account&[&payer], // Signers).await?;println!("Close Equality Proof Context State Account: {}",close_equality_signature);let close_range_signature = token.confidential_transfer_close_context_state_account(&range_proof_context_state_pubkey, // Account to closetoken_account, // Destination for the rent&payer.pubkey(), // Authority to close the account&[&payer], // Signers).await?;println!("Close Range Proof Context State Account: {}",close_range_signature);Ok(())}/// Transfer tokens from one confidential account to another////// Arguments:/// - token: Reference to the token client for interacting with the Token-2022 program/// - sender_token_account: Public key of the sender's token account/// - sender_owner: Arc-wrapped keypair that owns the sender's token account/// - sender_elgamal_keypair: ElGamal keypair for the sender's account/// - sender_aes_key: AES key for encrypting sender's confidential amounts/// - recipient_token_account: Public key of the recipient's token account/// - recipient_elgamal_pubkey: ElGamal public key of the recipient/// - amount: Amount of tokens to transfer/// - payer: Arc-wrapped keypair that will pay for the transaction fees////// Returns:/// - Result<()>: Success or error#[allow(clippy::too_many_arguments)]async fn transfer_tokens(token: &TokenClient,sender_token_account: &Pubkey,sender_owner: &Arc<Keypair>,sender_elgamal_keypair: &ElGamalKeypair,sender_aes_key: &AeKey,recipient_token_account: &Pubkey,recipient_elgamal_pubkey: &ElGamalPubkey,amount: u64,payer: Arc<Keypair>,) -> Result<()> {println!("Performing confidential transfer of {} tokens...", amount);// Get the token account data to access the confidential transfer extensionlet token_account_data = token.get_account_info(sender_token_account).await?;let extension_data = token_account_data.get_extension::<ConfidentialTransferAccount>()?;// Confidential Transfer extension state from token account needed to construct a `Transfer` instructionlet transfer_account_info =TransferAccountInfo::new(extension_data,);// Generate the proof data for the transfer// This creates the zero-knowledge proofs needed for the transferlet transfer_proof_data = transfer_account_info.generate_split_transfer_proof_data(amount, // Amount to transfersender_elgamal_keypair, // Sender's ElGamal keypairsender_aes_key, // Sender's AES keyrecipient_elgamal_pubkey, // Recipient's ElGamal public keyNone, // Auditor ElGamal public key (none in this example))?;// Create proof context state accounts// These accounts will store the context state from the equality, ciphertext validity, and range proofs required for the transferlet equality_proof_context_state_keypair = Keypair::new();let equality_proof_context_state_pubkey = equality_proof_context_state_keypair.pubkey();let ciphertext_validity_proof_context_state_keypair = Keypair::new();let ciphertext_validity_proof_context_state_pubkey =ciphertext_validity_proof_context_state_keypair.pubkey();let range_proof_context_state_keypair = Keypair::new();let range_proof_context_state_pubkey = range_proof_context_state_keypair.pubkey();// Equality proof - proves that two ciphertexts encrypt the same amountprintln!("Creating equality proof context state account...");let equality_proof_signature = token.confidential_transfer_create_context_state_account(&equality_proof_context_state_pubkey, // Account to create&payer.pubkey(), // Payer for the account creation&transfer_proof_data.equality_proof_data, // Proof data to verifyfalse, // Not a range proof&[&equality_proof_context_state_keypair], // Signers).await?;println!("Create Equality Proof Context State Account: {}",equality_proof_signature);// Ciphertext validity proof - proves that the ciphertext is properly formedprintln!("Creating ciphertext validity proof context state account...");let ciphertext_proof_signature = token.confidential_transfer_create_context_state_account(&ciphertext_validity_proof_context_state_pubkey, // Account to create&payer.pubkey(), // Payer for the account creation&transfer_proof_data.ciphertext_validity_proof_data_with_ciphertext.proof_data, // Proof data to verifyfalse, // Not a range proof&[&ciphertext_validity_proof_context_state_keypair], // Signers).await?;println!("Create Ciphertext Validity Proof Context State Account: {}",ciphertext_proof_signature);// Range proof - proves that the encrypted amount is within a valid range and non-negativeprintln!("Creating range proof context state account...");let range_proof_signature = token.confidential_transfer_create_context_state_account(&range_proof_context_state_pubkey, // Account to create&payer.pubkey(), // Payer for the account creation&transfer_proof_data.range_proof_data, // Proof to verifytrue, // Is a range proof&[&range_proof_context_state_keypair], // Signers).await?;println!("Create Range Proof Context State Account: {}", range_proof_signature);// Execute the confidential transfer// This transfers tokens confidentially from sender token account to recipient token accountprintln!("Executing confidential transfer transaction...");// Create a ProofAccountWithCiphertext for the ciphertext validity proof// This combines the proof account with the ciphertext datalet ciphertext_validity_proof_account_with_ciphertext =spl_token_client::token::ProofAccountWithCiphertext {proof_account: spl_token_client::token::ProofAccount::ContextAccount(ciphertext_validity_proof_context_state_pubkey, // Proof account),ciphertext_lo: transfer_proof_data.ciphertext_validity_proof_data_with_ciphertext.ciphertext_lo, // Low 128 bits of ciphertextciphertext_hi: transfer_proof_data.ciphertext_validity_proof_data_with_ciphertext.ciphertext_hi, // High 128 bits of ciphertext};// Perform the confidential transferlet transfer_signature = token.confidential_transfer_transfer(sender_token_account, // Source token accountrecipient_token_account, // Destination token account&sender_owner.pubkey(), // Owner of the source accountSome(&spl_token_client::token::ProofAccount::ContextAccount(equality_proof_context_state_pubkey, // Equality proof context state account)),Some(&ciphertext_validity_proof_account_with_ciphertext), // Ciphertext validity proofSome(&spl_token_client::token::ProofAccount::ContextAccount(range_proof_context_state_pubkey, // Range proof account)),amount, // Amount to transferNone, // Optional auditor info (none in this case)sender_elgamal_keypair, // Sender's ElGamal keypairsender_aes_key, // Sender's AES keyrecipient_elgamal_pubkey, // Recipient's ElGamal public keyNone, // Optional auditor ElGamal public key&[&sender_owner], // Signers).await?;println!("Confidential Transfer Signature: {}", transfer_signature);// Close the context state accounts to recover rent// This reclaims the SOL used to rent space for the proof accountsprintln!("Closing proof context state accounts...");// Close the equality proof accountlet close_equality_signature = token.confidential_transfer_close_context_state_account(&equality_proof_context_state_pubkey, // Account to closesender_token_account, // Destination for the rent&payer.pubkey(), // Authority to close the account&[&payer], // Signers).await?;println!("Close Equality Proof Context State Account: {}",close_equality_signature);// Close the ciphertext validity proof accountlet close_ciphertext_signature = token.confidential_transfer_close_context_state_account(&ciphertext_validity_proof_context_state_pubkey, // Account to closesender_token_account, // Destination for the rent&payer.pubkey(), // Authority to close the account&[&payer], // Signers).await?;println!("Close Ciphertext Validity Proof Context State Account: {}",close_ciphertext_signature);// Close the range proof accountlet close_range_signature = token.confidential_transfer_close_context_state_account(&range_proof_context_state_pubkey, // Account to closesender_token_account, // Destination for the rent&payer.pubkey(), // Authority to close the account&[&payer], // Signers).await?;println!("Close Range Proof Context State Account: {}",close_range_signature);Ok(())}
Instruções de transferência confidencial
A lista completa de instruções da extensão de Transferência Confidencial é a seguinte:
Instrução | Descrição |
---|---|
InitializeMint | Configura a mint account para transferências confidenciais. Esta instrução deve ser incluída na mesma transação que a instrução TokenInstruction::InitializeMint . |
UpdateMint | Atualiza as configurações de transferência confidencial para uma mint. |
ConfigureAccount | Configura uma token account para transferências confidenciais. |
ApproveAccount | Aprova uma token account para transferências confidenciais se a mint exigir aprovação para novas token accounts. |
EmptyAccount | Esvazia os saldos confidenciais pendentes e disponíveis para permitir o fechamento de uma token account. |
Deposit | Converte o saldo público de tokens em saldo confidencial pendente. |
Withdraw | Converte o saldo confidencial disponível de volta para saldo público. |
Transfer | Transfere tokens entre token accounts de forma confidencial. |
ApplyPendingBalance | Converte o saldo pendente em saldo disponível após depósitos ou transferências. |
EnableConfidentialCredits | Permite que uma token account receba transferências confidenciais de tokens. |
DisableConfidentialCredits | Bloqueia transferências confidenciais de entrada, permitindo ainda transferências públicas. |
EnableNonConfidentialCredits | Permite que uma token account receba transferências públicas de tokens. |
DisableNonConfidentialCredits | Bloqueia transferências regulares para fazer com que a conta receba apenas transferências confidenciais. |
TransferWithFee | Transfere tokens entre token accounts de forma confidencial com uma taxa. |
ConfigureAccountWithRegistry | Forma alternativa de configurar token accounts para transferências confidenciais usando uma conta ElGamalRegistry em vez de uma prova VerifyPubkeyValidity . |
Is this page helpful?