应用待处理余额
如何将待处理余额应用到可用余额
在代币可以进行保密转账之前,公共代币余额必须转换为保密余额。此转换分为两个阶段:
- 保密待处理余额:首先,代币从公共余额“存入”到“待处理”的保密余额中。
- 保密可用余额:然后将待处理余额“应用”到可用余额中,使代币可用于保密转账。
本节将解释第二阶段:将待处理余额应用到可用余额。
当代币从公共余额“存入”或从一个代币账户保密转账到另一个代币账户时,这些代币最初会被添加到保密待处理余额中。在代币可以用于保密转账之前,必须将待处理余额“应用”到可用余额中。
Apply Pending Balance
下图显示了将待处理余额应用到可用余额的步骤:
Apply Pending Balance
所需指令
要将待处理余额转换为可用余额,请调用 ConfidentialTransferInstruction::ApplyPendingBalance 指令。
spl_token_client
crate 提供了一个
confidential_transfer_apply_pending_balance
方法,该方法构建并发送包含
ApplyPendingBalance
指令的交易,如以下示例所示。
示例代码
以下示例演示了如何将保密待处理余额应用到保密可用余额。
要运行此示例,请使用以下命令启动一个从主网克隆的带有 Token Extension Program 的本地验证器。您必须安装 Solana CLI 才能启动本地验证器。
Terminal
$solana-test-validator --clone-upgradeable-program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb --url https://api.mainnet-beta.solana.com -r
在撰写本文时,机密转账功能尚未在默认的本地 validator 上启用。您必须克隆主网的 Token Extension Program 才能运行示例代码。
use anyhow::{Context, Result};use solana_client::nonblocking::rpc_client::RpcClient;use solana_sdk::{commitment_config::CommitmentConfig,signature::{Keypair, Signer},transaction::Transaction,};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},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 std::sync::Arc;#[tokio::main]async fn main() -> Result<()> {// Create connection to local test validatorlet 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 payer// This will be the wallet paying for the transaction fees// Use Arc to prevent multiple clones of the keypairlet payer = Arc::new(load_keypair()?);println!("Using payer: {}", payer.pubkey());// Generate a new keypair to use as the address of the token mintlet mint = Keypair::new();println!("Mint keypair generated: {}", mint.pubkey());// Set up program client for Token clientlet program_client = ProgramRpcClient::new(rpc_client.clone(), ProgramRpcClientSendTransaction);// Number of decimals for the mintlet decimals = 9;// Create a token client for the Token-2022 program// This provides high-level methods for token operationslet token = Token::new(Arc::new(program_client),&token_2022_program_id(), // Use the Token-2022 program (newer version with extensions)&mint.pubkey(), // Address of the new token mintSome(decimals), // Number of decimal placespayer.clone(), // Fee payer for transactions);// Create extension initialization parameters for the mint// The ConfidentialTransferMint extension enables confidential (private) transfers of tokenslet extension_initialization_params =vec![ExtensionInitializationParams::ConfidentialTransferMint {authority: Some(payer.pubkey()), // Authority that can modify confidential transfer settingsauto_approve_new_accounts: true, // Automatically approve new confidential accountsauditor_elgamal_pubkey: None, // Optional auditor ElGamal public key}];// Create and initialize the mint with the ConfidentialTransferMint extension// This sends a transaction to create the new token mintlet transaction_signature = token.create_mint(&payer.pubkey(), // Mint authority - can mint new tokensSome(&payer.pubkey()), // Freeze authority - can freeze token accountsextension_initialization_params, // Add the ConfidentialTransferMint extension&[&mint], // Mint keypair needed as signer).await?;// Print results for user verificationprintln!("Mint Address: {}", mint.pubkey());println!("Mint Creation Transaction Signature: {}",transaction_signature);// ===== Create and configure token account for confidential transfers =====println!("\nCreate and configure token account for confidential transfers");// Get the associated token account address for the ownerlet token_account_pubkey = get_associated_token_address_with_program_id(&payer.pubkey(), // Token account owner&mint.pubkey(), // Mint&token_2022_program_id(), // Token program ID);println!("Token Account Address: {}", token_account_pubkey);// Step 1: Create the associated token accountlet create_associated_token_account_instruction = create_associated_token_account(&payer.pubkey(), // Funding account&payer.pubkey(), // Token account owner&mint.pubkey(), // Mint&token_2022_program_id(), // Token program ID);// Step 2: Reallocate the token account to include space for the ConfidentialTransferAccount extensionlet reallocate_instruction = reallocate(&token_2022_program_id(), // Token program ID&token_account_pubkey, // Token account&payer.pubkey(), // Payer&payer.pubkey(), // Token account owner&[&payer.pubkey()], // Signers&[ExtensionType::ConfidentialTransferAccount], // Extension to reallocate space for)?;// Step 3: Generate the ElGamal keypair and AES key for token accountlet elgamal_keypair = ElGamalKeypair::new_from_signer(&payer, &token_account_pubkey.to_bytes()).expect("Failed to create ElGamal keypair");let aes_key = AeKey::new_from_signer(&payer, &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 is executedlet maximum_pending_balance_credit_counter = 65536;// Initial token balance is 0let decryptable_balance = aes_key.encrypt(0);// Generate the proof data client-sidelet proof_data = PubkeyValidityProofData::new(&elgamal_keypair).map_err(|_| anyhow::anyhow!("Failed to generate proof data"))?;// Indicate that proof is included in the same transactionlet proof_location =ProofLocation::InstructionOffset(1.try_into()?, ProofData::InstructionData(&proof_data));// Step 4: Create instructions to configure the account for confidential transferslet configure_account_instructions = configure_account(&token_2022_program_id(), // Program ID&token_account_pubkey, // Token account&mint.pubkey(), // Mint&decryptable_balance.into(), // Initial balancemaximum_pending_balance_credit_counter, // Maximum pending balance credit counter&payer.pubkey(), // Token Account Owner&[], // Additional signersproof_location, // Proof location)?;// 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,Some(&payer.pubkey()),&[&payer],recent_blockhash,);let transaction_signature = rpc_client.send_and_confirm_transaction(&transaction).await?;println!("Create Token Account Transaction Signature: {}",transaction_signature);// Mint some tokens to the newly created token account// This gives the account some tokens to work withlet mint_signature = token.mint_to(&token_account_pubkey, // Destination account&payer.pubkey(), // Mint authority100 * 10u64.pow(decimals as u32), // Amount (100 tokens with decimal precision)&[&payer], // Signers).await?;println!("Mint Tokens Transaction Signature: {}", mint_signature);// Deposit the tokens to confidential state// This converts regular tokens to confidential tokensprintln!("Deposit tokens to confidential state pending balance");let deposit_signature = token.confidential_transfer_deposit(&token_account_pubkey, // The token account&payer.pubkey(), // Authority (owner) of the account100 * 10u64.pow(decimals as u32), // Amount to deposit (100 tokens)decimals, // Decimals of the token&[&payer], // Signers (owner must sign)).await?;println!("Confidential Transfer Deposit Signature: {}",deposit_signature);// Apply the pending balance to make funds availableprintln!("Apply pending balance to available balance");let apply_signature = token.confidential_transfer_apply_pending_balance(&token_account_pubkey, // The token account&payer.pubkey(), // Authority (owner) of the accountNone, // Optional new decryptable available balanceelgamal_keypair.secret(), // ElGamal keypair for public-key cryptography (decryption and ZK proofs)&aes_key, // AES key for encryption of balance and transfer amounts&[&payer], // Signers (owner must sign)).await?;println!("Apply Pending Balance Signature: {}", apply_signature);println!("Confidential transfer setup complete. Tokens are now in available balance.");println!("Associated Token Account with confidential transfers: {}",token_account_pubkey);Ok(())}// Load the keypair from the default Solana CLI keypair path (~/.config/solana/id.json)// This enables using the same wallet as the Solana CLI toolsfn load_keypair() -> Result<Keypair> {// Get the default keypair pathlet keypair_path = dirs::home_dir().context("Could not find home directory")?.join(".config/solana/id.json");// Read the keypair file directly into bytes using serde_json// The keypair file is a JSON array of byteslet file = std::fs::File::open(&keypair_path)?;let keypair_bytes: Vec<u8> = serde_json::from_reader(file)?;// Create keypair from the loaded bytes// This converts the byte array into a keypairlet keypair = Keypair::from_bytes(&keypair_bytes)?;Ok(keypair)}
ConsolePowered by Mirror
Click to execute the code.
Is this page helpful?