Odottavan saldon soveltaminen

Odottavan saldon soveltaminen käytettävissä olevaan saldoon

Ennen kuin tokeneita voidaan siirtää luottamuksellisesti, julkinen tokenisaldo on muunnettava luottamukselliseksi saldoksi. Tämä muunnos tapahtuu kahdessa vaiheessa:

  1. Luottamuksellinen odottava saldo: Aluksi tokenit "talletetaan" julkisesta saldosta "odottavaan" luottamukselliseen saldoon.
  2. Luottamuksellinen käytettävissä oleva saldo: Odottava saldo "sovelletaan" sitten käytettävissä olevaan saldoon, jolloin tokenit ovat käytettävissä luottamuksellisiin siirtoihin.

Tässä osiossa selitetään toinen vaihe: odottavan saldon soveltaminen käytettävissä olevaan saldoon.

Kun tokenit "talletetaan" julkisesta saldosta tai kun tokenit siirretään luottamuksellisesti yhdeltä token account -tililtä toiselle, tokenit lisätään aluksi luottamukselliseen odottavaan saldoon. Ennen kuin tokeneita voidaan käyttää luottamuksellisiin siirtoihin, odottava saldo on "sovellettava" käytettävissä olevaan saldoon.

Apply Pending Balance

Seuraava kaavio näyttää vaiheet, joita tarvitaan odottavan saldon soveltamiseen käytettävissä olevaan saldoon:

Apply Pending Balance

Vaadittu käsky

Muuntaaksesi odottavan saldon käytettävissä olevaksi saldoksi, kutsu ConfidentialTransferInstruction::ApplyPendingBalance -käskyä.

spl_token_client -paketti tarjoaa confidential_transfer_apply_pending_balance -metodin, joka rakentaa ja lähettää tapahtuman ApplyPendingBalance -käskyllä, kuten alla olevassa esimerkissä on esitetty.

Esimerkkikoodi

Seuraava esimerkki havainnollistaa, kuinka luottamuksellinen odottava saldo sovelletaan luottamukselliseen käytettävissä olevaan saldoon.

Luottamukselliset siirrot riippuvat ZK ElGamal Proof -ohjelmasta, joka on käytössä mainnetissä ja devnetissä. Tavallinen solana-test-validator ei ota sitä käyttöön, mutta mainnet-haarukoiva paikallinen validator, kuten Surfpool, ottaa. Suorita esimerkki jompaakumpaa vasten (koodi käyttää devnetiä) rahoitetulla maksajalla ja korvaa paikkamerkkimintit ja tiliosoitteet omillasi.

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?

Sisällysluettelo

Muokkaa sivua
© 2026 Solana Foundation. Kaikki oikeudet pidätetään.