Overview
The Token-2022 confidential transfer extension keeps token balances and transfer amounts encrypted on chain while addresses stay public. A confidential token account holds a public balance and two encrypted balances: a pending balance that receives incoming funds, and an available balance that you can spend.DynamicSvmWalletClient builds, signs, and optionally broadcasts each operation for a Dynamic wallet, and it can decrypt the encrypted balances so your application can display them.
There are five operations, and each one is a single Solana transaction.
Amounts are strings in the token’s base units, so
"1000000" is one token when the mint has 6 decimals.
How signing works
Each call tosign_confidential_transfer runs the same sequence:
- The SDK reads the token account state and the latest blockhash from the RPC endpoint you provide, because the secure enclave that builds the transaction cannot reach the network.
- The enclave builds one unsigned transaction containing the confidential instruction and its zero-knowledge proofs, using the wallet’s confidential seed.
- The SDK checks the transaction before signing. It refuses to sign unless the transaction is a version 1 transaction, uses only the Token-2022, associated token account, System, ZK ElGamal Proof, and Compute Budget programs, has the wallet as its only signer and fee payer, and contains exactly one confidential instruction that matches the operation, mint, token account, destination, amount, and decimals you passed.
- The SDK signs the transaction with the wallet’s key share in a single signing session.
- If you set
broadcast=True, the SDK submits the transaction and waits for theconfirmedcommitment level.
serializedTransactions (the signed transaction as base64), signatures (its base58 signature), and transactionIds when you broadcast.
Prerequisites
- A Solana wallet funded with SOL for network fees.
- A Token-2022 mint with the confidential transfer extension enabled.
- An associated token account for that mint, owned by each wallet that takes part. Your application creates these accounts. The SDK does not.
soldersinstalled to derive token account addresses.
Step 1: Set up the client
rpc_url falls back to the default_rpc_url you pass to the client constructor when you leave it out of the options. It must use https. The SDK accepts http only for localhost, 127.0.0.1, and ::1.
Step 2: Create the confidential seed
Create the confidential seed once for each wallet, before its first confidential operation. The enclave generates the seed and stores it encrypted. The method returns the wallet’s ElGamal public key and the derivation version.async with block and use these values.
Step 3: Configure the token account
configureAccount enables confidential transfers on the wallet’s token account. Run it for every token account that will send or receive confidential funds, including the recipient’s.
password you set when you created the wallet. If you hold the wallet’s key shares yourself, pass them as key_shares instead. For a wallet that requires multi-factor authentication, pass mfa_token.
Step 4: Deposit, apply, transfer, and withdraw
Every operation uses the same call with a different operation name. The wallet must hold a public balance of the token before it can deposit.broadcast=True the call returns only after the transaction reaches the confirmed commitment level.
The recipient receives a transfer as a pending balance. The recipient’s wallet must run applyPendingBalance before it can transfer or withdraw those funds.
Read decrypted balances
get_confidential_balance decrypts both encrypted balances inside the enclave and returns them as decimal strings in base units. It reads the token account at the confirmed commitment level, so the result includes any operation you have already confirmed.
main(). Keep it at the end of the script, outside the function, when you add any of the steps that follow.
The public balance is not encrypted. Read it with the getTokenAccountBalance JSON-RPC method.
Sign without broadcasting
Leave outbroadcast to get the signed transaction back without submitting it. Submit it yourself, and wait for confirmation before the next operation.
Supply account state yourself
By default the SDK fetches account state and the blockhash fromrpc_url. If you already have them, set account_state, destination_account_state, recent_blockhash, and last_valid_block_height on the options and omit rpc_url. A call that broadcasts still needs an RPC URL. account_state is a dictionary with the keys data, lamports, and owner, where data is the base64 account data and lamports and owner are strings.
Error handling
SDK errors raiseDynamicSDKError. Catch it inside the same async with block.
rpc_url or setting default_rpc_url on the client, and a transaction the SDK refuses to sign because it does not match the request. In the second case, check mint, token_account, amount, and decimals.