Wpłata tokenów

Jak wpłacać tokeny do poufnego salda oczekującego

Zanim tokeny będą mogły być przesyłane w sposób poufny, publiczne saldo tokenów musi zostać przekształcone w saldo poufne. Proces ten odbywa się w dwóch etapach:

  1. Poufne saldo oczekujące: Na początku tokeny są "wpłacane" z publicznego salda na "oczekujące" poufne saldo.
  2. Poufne saldo dostępne: Następnie saldo oczekujące jest "zastosowane" do salda dostępnego, co umożliwia poufne przesyłanie tokenów.

Ta sekcja wyjaśnia pierwszy etap: wpłacanie publicznego salda tokenów na poufne saldo oczekujące.

Poniższy diagram przedstawia kroki związane z wpłacaniem tokenów z publicznego salda na poufne saldo oczekujące:

Deposit Tokens

Wymagana instrukcja

Aby przekształcić publiczne saldo w poufne saldo oczekujące, wywołaj instrukcję ConfidentialTransferInstruction::Deposit. Maksymalna kwota na jedną instrukcję wpłaty jest ograniczona do 2^48.

Crate spl_token_client udostępnia metodę confidential_transfer_deposit, która buduje i wysyła transakcję z instrukcją Deposit, jak pokazano w poniższym przykładzie.

Przykładowy kod

Poniższy przykład pokazuje, jak wpłacać publiczne saldo tokenów na poufne saldo oczekujące.

Aby uruchomić przykład, uruchom lokalny walidator z Token Extension Program sklonowanym z mainnetu za pomocą poniższego polecenia. Aby uruchomić lokalny walidator, musisz mieć zainstalowane Solana CLI.

Terminal
$
solana-test-validator --clone-upgradeable-program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb --url https://api.mainnet-beta.solana.com -r

W momencie pisania tego tekstu poufne transfery nie są włączone w domyślnym lokalnym walidatorze. Aby uruchomić przykładowy kod, musisz sklonować Token Extension Program z mainnetu.

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 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 payer = Arc::new(load_keypair()?);
println!("Using payer: {}", payer.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
payer.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(payer.pubkey()), // Authority that can modify confidential transfer settings
auto_approve_new_accounts: true, // Automatically approve new confidential accounts
auditor_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 mint
let transaction_signature = token
.create_mint(
&payer.pubkey(), // Mint authority - can mint new tokens
Some(&payer.pubkey()), // Freeze authority - can freeze token accounts
extension_initialization_params, // Add the ConfidentialTransferMint extension
&[&mint], // Mint keypair needed as signer
)
.await?;
// Print results for user verification
println!("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 owner
let 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 account
let 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 extension
let 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 account
let 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 executed
let maximum_pending_balance_credit_counter = 65536;
// Initial token balance is 0
let decryptable_balance = aes_key.encrypt(0);
// Generate the proof data client-side
let proof_data = PubkeyValidityProofData::new(&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
&token_account_pubkey, // Token account
&mint.pubkey(), // Mint
&decryptable_balance.into(), // Initial balance
maximum_pending_balance_credit_counter, // Maximum pending balance credit counter
&payer.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(&payer.pubkey()),
&[&*payer],
recent_blockhash,
);
let setup_signature = rpc_client
.send_and_confirm_transaction(&transaction)
.await?;
println!(
"Create Token Account Transaction Signature: {}",
setup_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(
&token_account_pubkey, // Destination account
&payer.pubkey(), // Mint authority
100 * 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 tokens
println!("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 account
100 * 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
);
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?

Spis treści

Edytuj stronę