Hoe een uitstaand saldo op het beschikbare saldo toepassen
Voordat tokens vertrouwelijk kunnen worden overgedragen, moet het publieke tokensaldo worden omgezet naar een vertrouwelijk saldo. Deze omzetting verloopt in twee fasen:
- Vertrouwelijk uitstaand saldo: Eerst worden tokens "gestort" van het publieke saldo naar een "uitstaand" vertrouwelijk saldo.
- Vertrouwelijk beschikbaar saldo: Het uitstaande saldo wordt vervolgens "toegepast" op het beschikbare saldo, waardoor de tokens beschikbaar worden voor vertrouwelijke overdrachten.
Dit gedeelte beschrijft de tweede fase: het toepassen van het uitstaande saldo op het beschikbare saldo.
Wanneer tokens worden "gestort" vanuit het publieke saldo of wanneer tokens vertrouwelijk worden overgedragen van het ene token account naar het andere, worden de tokens aanvankelijk toegevoegd aan het vertrouwelijke uitstaande saldo. Voordat tokens kunnen worden gebruikt voor vertrouwelijke overdrachten, moet het uitstaande saldo worden "toegepast" op het beschikbare saldo.
Het volgende diagram toont de stappen die betrokken zijn bij het toepassen van het uitstaande saldo op het beschikbare saldo:
Vereiste instructie
Om een uitstaand saldo om te zetten naar een beschikbaar saldo, roep de ConfidentialTransferInstruction::ApplyPendingBalance instructie aan.
De spl_token_client crate biedt een
confidential_transfer_apply_pending_balance methode die een transactie opbouwt
en verzendt met de ApplyPendingBalance instructie, zoals gedemonstreerd in het
onderstaande voorbeeld.
Voorbeeldcode
Het volgende voorbeeld laat zien hoe het vertrouwelijke uitstaande saldo op het vertrouwelijke beschikbare saldo wordt toegepast.
Vertrouwelijke overdrachten zijn afhankelijk van het ZK ElGamal Proof-programma,
dat is ingeschakeld op mainnet en devnet. Een standaard solana-test-validator
schakelt het niet in, maar een mainnet-forking lokale validator zoals
Surfpool wel. Voer het voorbeeld uit tegen een van deze
omgevingen (de code gebruikt devnet) met een gefund betalende partij, en vervang
de tijdelijke aanduidingen voor mint- en accountadressen door uw eigen adressen.
Rust
const ZK_PROOF_PROGRAM_ID: Pubkey =solana_pubkey::pubkey!("ZkE1Gama1Proof11111111111111111111111111111");fn main() -> Result<()> {let rpc_client = RpcClient::new_with_commitment(String::from("https://api.devnet.solana.com"),CommitmentConfig::confirmed(),);let owner = load_keypair()?;let amount: u64 = 100;let decimals: u8 = 2;// Setup: create a confidential account with a pending balance.let (_mint, token_account) = setup_pending_balance_account(&rpc_client, &owner, amount, decimals)?;// Derive the owner's keys to decrypt the current balances.let (elgamal_keypair, aes_key) = derive_confidential_keys(&owner, &token_account.to_bytes()).map_err(|e| anyhow::anyhow!("derive confidential keys: {e}"))?;let account_data = rpc_client.get_account(&token_account)?;let account = StateWithExtensions::<TokenAccount>::unpack(&account_data.data)?;let ct_extension = account.get_extension::<ConfidentialTransferAccount>()?;// The pending balance is split into low (16 bits) and high parts, each small// enough to recover with ElGamal's bounded decrypt_u32.let pending_lo: ElGamalCiphertext = ct_extension.pending_balance_lo.try_into().map_err(|e| anyhow::anyhow!("pending_balance_lo: {e:?}"))?;let pending_hi: ElGamalCiphertext = ct_extension.pending_balance_hi.try_into().map_err(|e| anyhow::anyhow!("pending_balance_hi: {e:?}"))?;let pending_lo_amount = pending_lo.decrypt_u32(elgamal_keypair.secret()).context("decrypt pending_balance_lo")? as u64;let pending_hi_amount = pending_hi.decrypt_u32(elgamal_keypair.secret()).context("decrypt pending_balance_hi")? as u64;let pending_total = pending_lo_amount + (pending_hi_amount << 16);// Read the current available balance from the AES-encrypted decryptable// balance. ElGamal's decrypt_u32 only recovers values up to 2^32 raw units,// so it fails for realistic balances; the AES field has no such limit.let decryptable_balance: AeCiphertext = ct_extension.decryptable_available_balance.try_into().map_err(|e| anyhow::anyhow!("decryptable_available_balance: {e:?}"))?;let current_available = decryptable_balance.decrypt(&aes_key).context("decrypt available balance")?;let new_available = current_available + pending_total;// Re-encrypt the new available balance with the AES key for fast reads.let new_decryptable: PodAeCiphertext = aes_key.encrypt(new_available).into();// The expected counter guards against pending credits that arrive between// building and processing this instruction.let expected_counter: u64 = ct_extension.pending_balance_credit_counter.into();let apply_ix = apply_pending_balance(&spl_token_2022::id(),&token_account,expected_counter,&new_decryptable,&owner.pubkey(),&[&owner.pubkey()],)?;let blockhash = rpc_client.get_latest_blockhash()?;let transaction =Transaction::new_signed_with_payer(&[apply_ix], Some(&owner.pubkey()), &[&owner], blockhash);let signature = rpc_client.send_and_confirm_transaction(&transaction)?;println!("Applied pending balance. New available: {new_available}. Tx: {signature}");Ok(())}
Typescript
const client = await createClient().use(signerFromFile(join(homedir(), ".config/solana/id.json"))).use(solanaRpc({rpcUrl: "https://api.devnet.solana.com"}));// The Solana CLI default keypair, used as fee payer, mint authority, and// token account owner.const owner = client.payer;const amount = 100n;const decimals = 2;// Setup: create a confidential account with a pending balance.const mint = await createConfidentialMint(client, owner, decimals);const token = await createConfidentialTokenAccount(client, owner, mint);await mintPublicTokens(client, owner, mint, token, amount);await depositTokens(client, owner, mint, token, amount, decimals);// Derive the owner's keys to decrypt the current balances.const { elgamalSecretKey, aesKey } = await deriveConfidentialKeys(owner, mint);// The helper decrypts the pending and available balances, re-encrypts the new// available balance, and builds the ApplyPendingBalance instruction. No proof// is required.const tokenAccount = await fetchToken(client.rpc, token);const applyInstruction = getApplyConfidentialPendingBalanceInstructionFromToken({token,tokenAccount: tokenAccount.data,authority: owner,elgamalSecretKey,aesKey});const result = await client.sendTransaction([applyInstruction]);console.log(`Applied pending balance. New available: ${amount}. Tx: ${result.context.signature}`);
Is this page helpful?