Transferir Tokens

Como transferir tokens confidencialmente de uma conta de token para outra

Para transferir tokens confidencialmente de uma conta de token para outra, tanto o remetente quanto o destinatário devem ter contas de token configuradas com o estado ConfidentialTransferAccount e aprovadas para transferências confidenciais. A conta de token do remetente também deve ter um saldo confidencial disponível para transferir.

Para transferir tokens confidencialmente:

  1. Crie três provas do lado do cliente:

    Prova de Igualdade (CiphertextCommitmentEqualityProofData): Verifica se o novo texto cifrado do saldo disponível após a transferência corresponde ao seu compromisso Pedersen correspondente, garantindo que o novo saldo disponível da conta de origem seja calculado corretamente como new_balance = current_balance - transfer_amount.

    Prova de Validade do Texto Cifrado (BatchedGroupedCiphertext3HandlesValidityProofData): Verifica se os textos cifrados do valor da transferência são gerados corretamente para todas as três partes (origem, destino e auditor), garantindo que o valor da transferência seja criptografado corretamente sob a chave pública de cada parte.

    Prova de Intervalo (BatchedRangeProofU128Data): Verifica se o novo saldo disponível e o valor da transferência (dividido em bits baixos/altos) são todos não-negativos e dentro de um intervalo especificado.

  2. Para cada prova:

    • Invoque o programa de prova ZK ElGamal para verificar os dados da prova.
    • Armazene os metadados específicos da prova em uma conta de "estado de contexto" de prova para usar em outras instruções.
  3. Invoque a instrução ConfidentialTransferInstruction::Transfer, fornecendo as contas de estado de contexto da prova.

  4. Feche as contas de estado de contexto da prova para recuperar o SOL usado para criá-las.

O diagrama a seguir mostra as etapas envolvidas na transferência de tokens da conta de token de um remetente para a conta de token de um destinatário.

Transfer Tokens

Instruções necessárias

Para transferir tokens confidencialmente de uma conta de token para outra, você deve:

  • Gerar uma prova de igualdade, prova de validade de texto cifrado e prova de intervalo do lado do cliente
  • Invocar o programa de prova Zk ElGamal para verificar as provas e inicializar as contas de "estado de contexto"
  • Invocar a instrução ConfidentialTransferInstruction::Transfer fornecendo as três contas de prova.
  • Fechar as três contas de prova para recuperar o rent.

O crate spl_token_client fornece os seguintes métodos:

  • Método confidential_transfer_create_context_state_account que cria uma conta de prova.
  • Método confidential_transfer_transfer que invoca a instrução Transfer.
  • Método confidential_transfer_close_context_state_account que fecha uma conta de prova.

Código de exemplo

O exemplo a seguir demonstra como transferir tokens confidencialmente entre contas de token.

Para executar o exemplo, inicie um validador local com o Token Extensions Program clonado da mainnet usando o seguinte comando. Você deve ter o Solana CLI instalado para iniciar o validador local.

Terminal
$
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,
};
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::{
account_info::{TransferAccountInfo, WithdrawAccountInfo},
instruction::{configure_account, PubkeyValidityProofData},
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;
#[tokio::main]
async fn main() -> Result<()> {
// Create connection to local test validator
let 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 keypair
let sender = Arc::new(load_keypair()?);
println!("Sender: {}", sender.pubkey());
// Generate a new keypair to use as the address of the token mint
let mint = Keypair::new();
println!("Mint keypair generated: {}", mint.pubkey());
// Set up program client for Token client
let program_client = ProgramRpcClient::new(rpc_client.clone(), ProgramRpcClientSendTransaction);
// Number of decimals for the mint
let decimals = 9;
// Create a token client for the Token-2022 program
// This provides high-level methods for token operations
let 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 mint
Some(decimals), // Number of decimal places
sender.clone(), // Fee payer for transactions
);
// Create extension initialization parameters for the mint
// The ConfidentialTransferMint extension enables confidential (private) transfers of tokens
let extension_initialization_params =
vec![ExtensionInitializationParams::ConfidentialTransferMint {
authority: Some(sender.pubkey()), // Authority that can modify confidential transfer settings
auto_approve_new_accounts: true, // Automatically approve new confidential accounts
auditor_elgamal_pubkey: None, // None if no auditor
}];
// Create and initialize the mint with the ConfidentialTransferMint extension
// This sends a transaction to create the new token mint
let transaction_signature = token
.create_mint(
&sender.pubkey(), // Mint authority - can mint new tokens
Some(&sender.pubkey()), // Freeze authority - can freeze token accounts
extension_initialization_params, // Add the ConfidentialTransferMint extension
&[&mint], // Mint keypair needed as signer
)
.await?;
println!("Mint Address: {}", mint.pubkey());
println!(
"Create Mint Transaction Signature: {}",
transaction_signature
);
// ===== Create and configure token account for confidential transfers =====
println!("\nCreate and Configure Sender Token Account");
// Get the associated token account address for the owner
let sender_token_account_pubkey = get_associated_token_address_with_program_id(
&sender.pubkey(), // Token account owner
&mint.pubkey(), // Mint
&token_2022_program_id(), // Token program ID
);
println!("Sender Token Account Address: {}", sender_token_account_pubkey);
// Step 1: Create the associated token account
let create_associated_token_account_instruction = create_associated_token_account(
&sender.pubkey(), // Funding account
&sender.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 extension
let reallocate_instruction = reallocate(
&token_2022_program_id(), // Token program ID
&sender_token_account_pubkey, // Token account
&sender.pubkey(), // Payer
&sender.pubkey(), // Token account owner
&[&sender.pubkey()], // Signers
&[ExtensionType::ConfidentialTransferAccount], // Extension to reallocate space for
)?;
// Step 3: Generate the ElGamal keypair and AES key for token account
let sender_elgamal_keypair = ElGamalKeypair::new_from_signer(&sender, &sender_token_account_pubkey.to_bytes())
.expect("Failed to create ElGamal keypair");
let sender_aes_key = AeKey::new_from_signer(&sender, &sender_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 executed
let maximum_pending_balance_credit_counter = 65536;
// Initial token balance is 0
let sender_decryptable_balance = sender_aes_key.encrypt(0);
// Generate the proof data client-side
let proof_data = PubkeyValidityProofData::new(&sender_elgamal_keypair)
.map_err(|_| anyhow::anyhow!("Failed to generate proof data"))?;
// Indicate that proof is included in the same transaction
let proof_location =
ProofLocation::InstructionOffset(1.try_into()?, ProofData::InstructionData(&proof_data));
// Step 4: Create instructions to configure the account for confidential transfers
let configure_account_instructions = configure_account(
&token_2022_program_id(), // Program ID
&sender_token_account_pubkey, // Token account
&mint.pubkey(), // Mint
&sender_decryptable_balance.into(), // Initial balance
maximum_pending_balance_credit_counter, // Maximum pending balance credit counter
&sender.pubkey(), // Token Account Owner
&[], // Additional signers
proof_location, // Proof location
)?;
// Combine all instructions
let mut instructions = vec![
create_associated_token_account_instruction,
reallocate_instruction,
];
instructions.extend(configure_account_instructions);
// Create and send the transaction
let recent_blockhash = rpc_client.get_latest_blockhash().await?;
let transaction = Transaction::new_signed_with_payer(
&instructions,
Some(&sender.pubkey()),
&[&sender],
recent_blockhash,
);
let transaction_signature = rpc_client
.send_and_confirm_transaction(&transaction)
.await?;
println!(
"Create Sender Token Account Transaction Signature: {}",
transaction_signature
);
// Mint some tokens to the newly created token account
// This gives the account some tokens to work with
let mint_signature = token
.mint_to(
&sender_token_account_pubkey, // Destination account
&sender.pubkey(), // Mint authority
100 * 10u64.pow(decimals as u32), // Amount (100 tokens)
&[&sender], // Signers
)
.await?;
println!("Mint Tokens to Sender Token Account Transaction Signature: {}", mint_signature);
// Deposit the tokens to confidential state
// This converts public tokens balance to confidential pending balance
println!("Deposit tokens to confidential state pending balance");
let deposit_signature = token
.confidential_transfer_deposit(
&sender_token_account_pubkey, // The token account
&sender.pubkey(), // Authority (owner) of the account
100 * 10u64.pow(decimals as u32), // Amount to deposit (100 tokens)
decimals, // Decimals of the token
&[&sender], // Signers (owner must sign)
)
.await?;
println!(
"Confidential Transfer Deposit Signature: {}",
deposit_signature
);
// Apply the pending balance to available balance
println!("Apply pending balance to available balance");
let apply_signature = token
.confidential_transfer_apply_pending_balance(
&sender_token_account_pubkey, // The token account
&sender.pubkey(), // Authority (owner) of the account
None, // Optional ApplyPendingBalanceAccountInfo, generated if not provided
sender_elgamal_keypair.secret(), // ElGamal secret k ey for decryption
&sender_aes_key, // AES key for encryption of balance and transfer amounts
&[&sender], // Signers (owner must sign)
)
.await?;
println!("Apply Pending Balance Signature: {}", apply_signature);
// ===== Withdraw half of the tokens from confidential available balance =====
println!("\nWithdraw tokens from confidential available balance to public balance");
// Calculate the withdraw amount (half of the deposited amount)
let withdraw_amount = 50 * 10u64.pow(decimals as u32); // Half of the 100 tokens deposited
// Get the token account data to access the confidential transfer extension
let token_account = token.get_account_info(&sender_token_account_pubkey).await?;
// Unpack the ConfidentialTransferAccount extension portion of the token account data
let extension_data = token_account.get_extension::<ConfidentialTransferAccount>()?;
// Confidential Transfer extension information needed to construct a `Withdraw` instruction
let withdraw_account_info = WithdrawAccountInfo::new(extension_data);
// Create keypairs for the proof accounts
let 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
let WithdrawProofData {
equality_proof_data,
range_proof_data,
} = withdraw_account_info.generate_proof_data(
withdraw_amount, // Amount to withdraw from confidential available balance
&sender_elgamal_keypair, // ElGamal keypair for encryption
&sender_aes_key, // AES key for encryption
)?;
// Generate the equality proof account
println!("Creating equality proof context state account...");
let equality_proof_signature = token
.confidential_transfer_create_context_state_account(
&equality_proof_context_state_pubkey, // Public key of the new equality proof context state account
&sender.pubkey(), // Authority that can close the context state account
&equality_proof_data, // Proof data for the equality proof verification
false, // False: combine account creation and proof verification in one transaction
&[&equality_proof_context_state_keypair], // Signer for the new account
)
.await?;
println!(
"Equality Proof Context State Account Signature: {}",
equality_proof_signature
);
// Generate the range proof account
println!("Creating range proof context state account...");
let range_proof_signature = token
.confidential_transfer_create_context_state_account(
&range_proof_context_state_pubkey, // Public key of the new range proof context state account
&sender.pubkey(), // Authority that can close the context state account
&range_proof_data, // Proof data for the range proof verification
true, // True: split account creation and proof verification into separate transactions (for large proofs)
&[&range_proof_context_state_keypair], // Signer for the new account
)
.await?;
println!(
"Range Proof Context State Account Signature: {}",
range_proof_signature
);
// Perform the withdrawal
println!("Executing withdrawal transaction...");
let withdraw_signature = token
.confidential_transfer_withdraw(
&sender_token_account_pubkey, // Token account to withdraw from
&sender.pubkey(), // Owner of the token account
Some(&spl_token_client::token::ProofAccount::ContextAccount(
equality_proof_context_state_pubkey, // Reference to the equality proof account
)),
Some(&spl_token_client::token::ProofAccount::ContextAccount(
range_proof_context_state_pubkey, // Reference to the range proof account
)),
withdraw_amount, // Amount to withdraw from confidential available balance
decimals, // Decimal precision of the token
Some(withdraw_account_info), // Data from confidential transfer extension for proof verification
&sender_elgamal_keypair, // ElGamal keypair for encryption
&sender_aes_key, // AES key for encryption
&[&sender], // Owner must sign the transaction
)
.await?;
println!("Withdraw Transaction Signature: {}", withdraw_signature);
// Close the context state accounts to recover rent
println!("Closing equality proof context state account...");
let close_equality_signature = token
.confidential_transfer_close_context_state_account(
&equality_proof_context_state_pubkey, // Equality proof context state account to close
&sender_token_account_pubkey, // Account that will receive the lamports
&sender.pubkey(), // Authority allowed to close the account
&[&sender], // Authority must sign
)
.await?;
println!(
"Close Equality Proof Account Signature: {}",
close_equality_signature
);
println!("Closing range proof context state account...");
let close_range_signature = token
.confidential_transfer_close_context_state_account(
&range_proof_context_state_pubkey, // Range proof context state account to close
&sender_token_account_pubkey, // Account that will receive the lamports
&sender.pubkey(), // Authority allowed to close the account
&[&sender], // Authority must sign
)
.await?;
println!(
"Close Range Proof Account Signature: {}",
close_range_signature
);
// ===== Create a recipient token account for confidential transfers =====
println!("\nCreate Recipient Token Account");
// Create a new keypair to use as the recipient account owner
let recipient = Keypair::new();
println!("Recipient: {}", recipient.pubkey());
// Fund the recipient account with SOL for transaction fees
let fund_signature = rpc_client
.send_and_confirm_transaction(&Transaction::new_signed_with_payer(
&[solana_sdk::system_instruction::transfer(
&sender.pubkey(),
&recipient.pubkey(),
1_000_000_000, // 1 SOL
)],
Some(&sender.pubkey()),
&[&sender],
rpc_client.get_latest_blockhash().await?,
))
.await?;
println!("Fund Recipient Signature: {}", fund_signature);
// Get the associated token account address for the recipient owner
let recipient_token_account_pubkey = get_associated_token_address_with_program_id(
&recipient.pubkey(), // Token account owner
&mint.pubkey(), // Same mint as before
&token_2022_program_id(), // Token program ID
);
println!(
"Recipient Token Account Address: {}",
recipient_token_account_pubkey
);
// Step 1: Create the associated token account
let create_associated_token_account_instruction = create_associated_token_account(
&recipient.pubkey(), // Funding account
&recipient.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 extension
let reallocate_instruction = reallocate(
&token_2022_program_id(), // Token program ID
&recipient_token_account_pubkey, // Token account
&recipient.pubkey(), // Payer
&recipient.pubkey(), // Token account owner
&[&recipient.pubkey()], // Signers
&[ExtensionType::ConfidentialTransferAccount], // Extension to reallocate space for
)?;
// Step 3: Generate the ElGamal keypair and AES key for recipient token account
let recipient_elgamal_keypair =
ElGamalKeypair::new_from_signer(&recipient, &recipient_token_account_pubkey.to_bytes())
.expect("Failed to create ElGamal keypair");
let recipient_aes_key =
AeKey::new_from_signer(&recipient, &recipient_token_account_pubkey.to_bytes())
.expect("Failed to create AES key");
// Maximum pending balance credit counter, same as before
let maximum_pending_balance_credit_counter = 65536;
// Initial token balance is 0
let decryptable_balance = recipient_aes_key.encrypt(0);
// Generate the proof data client-side
let proof_data = PubkeyValidityProofData::new(&recipient_elgamal_keypair)
.map_err(|_| anyhow::anyhow!("Failed to generate proof data"))?;
// Indicate that proof is included in the same transaction
let proof_location =
ProofLocation::InstructionOffset(1.try_into()?, ProofData::InstructionData(&proof_data));
// Step 4: Create instructions to configure the account for confidential transfers
let configure_account_instructions = configure_account(
&token_2022_program_id(), // Program ID
&recipient_token_account_pubkey, // Token account
&mint.pubkey(), // Mint
&decryptable_balance.into(), // Initial balance
maximum_pending_balance_credit_counter, // Maximum pending balance credit counter
&recipient.pubkey(), // Token Account Owner
&[], // Additional signers
proof_location, // Proof location
)?;
// Combine all instructions
let mut instructions = vec![
create_associated_token_account_instruction,
reallocate_instruction,
];
instructions.extend(configure_account_instructions);
// Create and send the transaction
let recent_blockhash = rpc_client.get_latest_blockhash().await?;
let transaction = Transaction::new_signed_with_payer(
&instructions,
Some(&recipient.pubkey()),
&[&recipient],
recent_blockhash,
);
let transaction_signature = rpc_client
.send_and_confirm_transaction(&transaction)
.await?;
println!(
"Create Recipient Token Account Transaction Signature: {}",
transaction_signature
);
// ===== Perform a confidential transfer from sender account to recipient account =====
println!("\nPerform Confidential Transfer");
let transfer_amount = 25 * 10u64.pow(decimals as u32); // Transfer 25 tokens
// Get the token account data to access the confidential transfer extension state
let token_account = token.get_account_info(&sender_token_account_pubkey).await?;
let extension_data = token_account.get_extension::<ConfidentialTransferAccount>()?;
// Create TransferAccountInfo from the extension data
let transfer_account_info = TransferAccountInfo::new(extension_data);
// Generate the proof data for the transfer
let transfer_proof_data = transfer_account_info.generate_split_transfer_proof_data(
transfer_amount,
&sender_elgamal_keypair,
&sender_aes_key,
recipient_elgamal_keypair.pubkey(),
None, // auditor ElGamal public key (none if no auditor)
)?;
// Create proof context accounts
let 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();
// Create context state account for equality proof
println!("Creating equality proof context state account for transfer...");
let equality_proof_signature = token
.confidential_transfer_create_context_state_account(
&equality_proof_context_state_pubkey, // Public key of the new equality proof context state account
&sender.pubkey(), // Authority that can close the context state account
&transfer_proof_data.equality_proof_data, // Proof data for the equality proof verification
false, // False: combine account creation and proof verification in one transaction
&[&equality_proof_context_state_keypair], // Signer for the new account
)
.await?;
println!(
"Transfer Equality Proof Account Signature: {}",
equality_proof_signature
);
// Create context state account for ciphertext validity proof
println!("Creating ciphertext validity proof context state account...");
let ciphertext_proof_signature = token
.confidential_transfer_create_context_state_account(
&ciphertext_validity_proof_context_state_pubkey, // Public key of the new ciphertext validity proof context state account
&sender.pubkey(), // Authority that can close the context state account
&transfer_proof_data
.ciphertext_validity_proof_data_with_ciphertext
.proof_data, // Proof data for the ciphertext validity proof verification
false, // False: combine account creation and proof verification in one transaction
&[&ciphertext_validity_proof_context_state_keypair], // Signer for the new account
)
.await?;
println!(
"Ciphertext Validity Proof Account Signature: {}",
ciphertext_proof_signature
);
// Create context state account for range proof
println!("Creating range proof context state account...");
let range_proof_signature = token
.confidential_transfer_create_context_state_account(
&range_proof_context_state_pubkey, // Public key of the new range proof context state account
&sender.pubkey(), // Authority that can close the context state account
&transfer_proof_data.range_proof_data, // Proof data for the range proof verification
true, // True: split account creation and proof verification into separate transactions (for large proofs)
&[&range_proof_context_state_keypair], // Signer for the new account
)
.await?;
println!("Range Proof Account Signature: {}", range_proof_signature);
// Execute the confidential transfer
println!("Executing confidential transfer transaction...");
// Create a ProofAccountWithCiphertext for the ciphertext validity proof
let 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,
),
ciphertext_lo: transfer_proof_data
.ciphertext_validity_proof_data_with_ciphertext
.ciphertext_lo,
ciphertext_hi: transfer_proof_data
.ciphertext_validity_proof_data_with_ciphertext
.ciphertext_hi,
};
let transfer_signature = token
.confidential_transfer_transfer(
&sender_token_account_pubkey, // Source account
&recipient_token_account_pubkey, // Destination account
&sender.pubkey(), // Authority (owner) of source account
Some(&spl_token_client::token::ProofAccount::ContextAccount(
equality_proof_context_state_pubkey, // Reference to the equality proof account
)),
Some(&ciphertext_validity_proof_account_with_ciphertext),
Some(&spl_token_client::token::ProofAccount::ContextAccount(
range_proof_context_state_pubkey, // Reference to the range proof account
)),
transfer_amount, // Amount to transfer
None, // Custom data for verification (optional)
&sender_elgamal_keypair, // ElGamal keypair for source account
&sender_aes_key, // AES key for source account
recipient_elgamal_keypair.pubkey(), // ElGamal public key of destination account
None, // Auditor ElGamal public key (none if no auditor)
&[&sender], // Signers
)
.await?;
println!("Confidential Transfer Signature: {}", transfer_signature);
// Apply pending balance on the recipient account to make transferred funds available
println!("Applying pending balance on recipient account...");
let apply_signature = token
.confidential_transfer_apply_pending_balance(
&recipient_token_account_pubkey, // The token account
&recipient.pubkey(), // Authority (owner) of the account
None, // Optional new decryptable available balance
recipient_elgamal_keypair.secret(), // ElGamal secret key for encryption
&recipient_aes_key, // AES key for encryption
&[&recipient], // Signers (owner must sign)
)
.await?;
println!(
"Recipient Account Apply Pending Balance Signature: {}",
apply_signature
);
// Close the context state accounts to recover rent
println!("Closing equality proof context state account...");
let close_equality_signature = token
.confidential_transfer_close_context_state_account(
&equality_proof_context_state_pubkey, // Equality proof context state account to close
&sender_token_account_pubkey, // Account that will receive the lamports
&sender.pubkey(), // Authority allowed to close the account
&[&sender], // Authority must sign
)
.await?;
println!(
"Close Transfer Equality Proof Account Signature: {}",
close_equality_signature
);
println!("Closing ciphertext validity proof context state account...");
let close_ciphertext_signature = token
.confidential_transfer_close_context_state_account(
&ciphertext_validity_proof_context_state_pubkey, // Ciphertext validity proof context state account to close
&sender_token_account_pubkey, // Account that will receive the lamports
&sender.pubkey(), // Authority allowed to close the account
&[&sender], // Authority must sign
)
.await?;
println!(
"Close Ciphertext Validity Proof Account Signature: {}",
close_ciphertext_signature
);
println!("Closing range proof context state account...");
let close_range_signature = token
.confidential_transfer_close_context_state_account(
&range_proof_context_state_pubkey, // Range proof context state account to close
&sender_token_account_pubkey, // Account that will receive the lamports
&sender.pubkey(), // Authority allowed to close the account
&[&sender], // Authority must sign
)
.await?;
println!(
"Close Range Proof Account Signature: {}",
close_range_signature
);
println!("\nConfidential transfer completed successfully!");
println!("Sender Token Account: {}", sender_token_account_pubkey);
println!(
"Recipient Token Account: {}",
recipient_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 tools
fn load_keypair() -> Result<Keypair> {
// Get the default keypair path
let 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 bytes
let 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 keypair
let keypair = Keypair::from_bytes(&keypair_bytes)?;
Ok(keypair)
}
Click to execute the code.

Is this page helpful?

Índice

Editar Página