Skip to main content
Confidential transfers require dynamic-wallet-sdk version 0.7.0 or later.

Overview

Solana’s Token-2022 Confidential Balances extension, also called the confidential transfer extension, keeps token balances and transfer amounts encrypted on chain. Wallet and token account addresses stay public. A token account with the extension enabled holds three balances: Incoming funds always land in the pending balance. Before you can spend them, you apply the pending balance, which moves it to the available balance. Solana keeps the two apart so that anyone sending you tokens cannot interfere with a transaction you are preparing. DynamicSvmWalletClient builds, signs, and optionally broadcasts each operation for a Dynamic wallet. The operation names match the Token-2022 instructions, and each one is a single Solana transaction. amount is a string in the token’s base units, so "1000000" is one token for a mint with 6 decimals. decimals must match the mint.

How it works

Each call to sign_confidential_transfer runs the same steps:
  1. Read. The SDK reads the token account and the latest blockhash from your Solana RPC endpoint, because the secure enclave that builds the transaction cannot reach the network.
  2. Build. The enclave builds one unsigned transaction. It holds the Token-2022 instruction and the zero-knowledge proofs that the ZK ElGamal Proof program verifies on chain, and it uses the wallet’s confidential seed to derive the ElGamal keypair and AES key.
  3. Check. The SDK verifies 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.
  4. Sign. The SDK signs the transaction with the wallet’s key share in a single signing session.
  5. Broadcast. If you set broadcast=True, the SDK submits the transaction and waits for the confirmed commitment level.
The result is a dictionary with serializedTransactions (the signed transaction as base64), signatures (its base58 signature), and transactionIds when you broadcast.

Prerequisites

  • The SDK and solders, which derives token account addresses:
  • A Solana wallet funded with SOL. The wallet pays the transaction fee, and configureAccount also pays the rent to create and grow the token account. See create a Solana wallet.
  • A Token-2022 mint with the Confidential Transfer extension enabled. See Solana’s issuer guide. If the mint does not auto-approve new accounts, its confidential transfer authority must approve each token account after configureAccount. The SDK does not send that instruction.
  • A public balance of the token in the wallet’s associated token account before you deposit.
  • A recipient for transfer whose token account for the mint has already run configureAccount. The transfer fails if the destination token account does not have confidential transfers enabled.

Quick Start

This script creates a wallet and its confidential seed, then runs every operation and reads the decrypted balances. It pauses so that you can fund the new wallet.
The snippets in the sections below run inside main(), in the async with block after the steps above. That is where client, wallet_id, address, token_account, and run are defined.
chain_id defaults to Solana mainnet (solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp). The cluster in chain_id must match the cluster behind rpc_url. Devnet is solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1.
rpc_url falls back to the default_rpc_url you pass to the client constructor. It must use https. The SDK accepts http only for localhost, 127.0.0.1, and ::1.

Operations

Configure a token account

configureAccount enables confidential transfers on the wallet’s associated token account, and creates the account first if it does not exist. Run it for every token account that will send or receive confidential funds, including the recipient’s.

Deposit, apply, transfer, and withdraw

All five operations use the same sign_confidential_transfer call with a different operation name, as in the script above.
  • The recipient of a transfer receives it as a pending balance. The recipient runs applyPendingBalance before they can transfer or withdraw those funds.
  • Each operation reads the current token account state, so wait for one to confirm before you start the next. With broadcast=True the call returns only after the transaction reaches confirmed.
  • Pass the 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.

Read decrypted balances

The pending and available balances are ciphertexts that only the owner’s keys can read. get_confidential_balance decrypts both 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.
The public balance is not encrypted. Read it with the getTokenAccountBalance JSON-RPC method.

Sign without broadcasting

Leave out broadcast 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 from rpc_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 raise DynamicSDKError.
Two common causes are a missing RPC URL, which you fix by passing 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.

Next steps

Last modified on November 5, 2026