Ausstehenden Saldo anwenden

So wird das ausstehende Guthaben auf das verfügbare Guthaben angewendet

Bevor Token vertraulich übertragen werden können, muss das öffentliche Token-Guthaben in ein vertrauliches Guthaben umgewandelt werden. Diese Umwandlung erfolgt in zwei Schritten:

  1. Vertrauliches ausstehendes Guthaben: Zunächst werden Token vom öffentlichen Guthaben auf ein „ausstehendes“ vertrauliches Guthaben „eingezahlt“.
  2. Vertrauliches verfügbares Guthaben: Das ausstehende Guthaben wird anschließend auf das verfügbare Guthaben „angewendet“, wodurch die Token für vertrauliche Übertragungen verfügbar werden.

Dieser Abschnitt erläutert den zweiten Schritt: die Anwendung des ausstehenden Guthabens auf das verfügbare Guthaben.

Wenn Token vom öffentlichen Guthaben „eingezahlt“ werden oder wenn Token vertraulich von einem token account auf einen anderen token account übertragen werden, werden die Token zunächst dem vertraulichen ausstehenden Guthaben hinzugefügt. Bevor Token für vertrauliche Übertragungen verwendet werden können, muss das ausstehende Guthaben auf das verfügbare Guthaben „angewendet“ werden.

Apply Pending Balance

Das folgende Diagramm zeigt die Schritte, die beim Anwenden des ausstehenden Guthabens auf das verfügbare Guthaben beteiligt sind:

Apply Pending Balance

Erforderliche Anweisungen

Um ein ausstehendes Guthaben in ein verfügbares Guthaben umzuwandeln, rufen Sie die ConfidentialTransferInstruction::ApplyPendingBalance Anweisungen auf.

Das spl_token_client-Crate stellt eine confidential_transfer_apply_pending_balance-Methode bereit, die eine Transaktion mit der ApplyPendingBalance- Anweisungen erstellt und sendet, wie im nachfolgenden Beispiel gezeigt.

Beispielcode

Das folgende Beispiel zeigt, wie das vertrauliche ausstehende Guthaben auf das vertrauliche verfügbare Guthaben angewendet wird.

Vertrauliche Übertragungen sind auf das ZK-ElGamal-Proof-Programm angewiesen, das auf Mainnet und Devnet aktiviert ist. Ein standardmäßiger solana-test-validator aktiviert es nicht, aber ein Mainnet-forkender lokaler Validatoren wie Surfpool schon. Führen Sie das Beispiel gegen eines davon aus (der Code verwendet Devnet) mit einem finanzierten Zahler, und ersetzen Sie die Platzhalter-Mint- und Konten-Adressen durch Ihre eigenen.

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?

Inhaltsverzeichnis

Seite bearbeiten
© 2026 Solana Foundation. Alle Rechte vorbehalten.