A token transfer usually tells two stories at once: who moved the tokens, and how much they moved. Public amounts make a ledger easy to inspect. They also make a payment history easy to reconstruct.
In Confidential Transfer Solana, I explored what happens when the amount becomes private but the transaction still has to be verifiable. The project is written in Rust: a Token-2022 lifecycle demo, plus a CLI that explores wrapping an existing SPL token into a confidential version.
The interesting part is the accounting underneath. A transfer becomes a sequence of balance changes, encrypted values, and proofs that connect them.
What stays visible
Solana’s Confidential Transfer extension, now documented as Confidential Balances, keeps transfer amounts and confidential balances encrypted. Token account addresses remain public. This gives amount confidentiality; it does not make the sender or recipient anonymous.
The boundary matters. Moving a public balance into the confidential side uses a deposit with a public amount. Withdrawing makes an amount public again. Those entry and exit points can provide clues, so “encrypted” should never be treated as a promise that the entire history is hidden.
Three balances, one account
A configured token account has a public balance, a confidential pending balance, and a confidential available balance. Pending is where incoming value arrives. Available is what the owner can spend. Receiving value and making it available are separate operations.
The demo in main.rs makes this concrete. It creates a mint and two configured accounts, mints 100 tokens, deposits them, and applies the sender’s pending balance. It then transfers 50 to the recipient. The recipient applies that incoming balance and withdraws 25 back to the public side.
Sender
- Public
- 100
- Pending
- sealed
- Available
- sealed
Recipient
- Public
- 0
- Pending
- sealed
- Available
- sealed
1 / 6100 tokens begin in the sender’s public balance. Both accounts are already configured for confidential transfers.
After that sequence, the sender has 50 confidential tokens. The recipient has 25 confidential tokens and 25 public tokens. The total is still 100. What changes is where each amount is held and who can read it.
The separate apply step is easy to overlook. A successful transfer can leave the recipient with value in pending, without increasing their available balance yet. Any interface built around these instructions needs to represent that intermediate state accurately.
Proving the transfer
Encryption hides an amount, but the token program still needs evidence that the update follows its rules. The client generates proofs from the account state, transfer amount, and encryption keys. The chain verifies those proofs without requiring the amount to be published in plaintext.
The transfer demo generates three kinds of proof: equality, ciphertext validity, and range. Together they establish the required relationships between encrypted values, show that the transfer ciphertext is well formed, and constrain the represented values to valid ranges.
The implementation puts the verified proof contexts in separate accounts, references them in the transfer instruction, and closes those accounts afterward to recover rent. The withdrawal path follows a related sequence with equality and range proofs. Proof generation, verification, execution, and cleanup are distinct pieces of work.
Each token account also needs encryption material. The code derives an ElGamal keypair and an authenticated-encryption key from the owner’s signer and the token account address. ElGamal supports the confidential balance operations; the authenticated-encryption key lets the owner read the associated decryptable balance. Both are separate from simply displaying a wallet’s public address.
The demo configures automatic account approval and no auditor key. Token-2022 also supports an optional auditor public key, which can give its holder visibility into transfer amounts. Privacy depends on the mint’s actual configuration as well as the cryptography.
Wrapping an existing token
The lifecycle demo starts with a new Token-2022 mint. The wrapper asks another question: how could an existing SPL token move through a similar flow?
In wrapper.rs, the original tokens move into a vault account. A separate Token-2022 mint issues the matching amount of wrapper tokens, using the original mint’s decimals. The wrapper tokens enter the user’s public balance first, then move through deposit and apply-pending into the confidential available balance.
Unwrapping reverses the sequence. The user withdraws from confidential to public balance, burns wrapper tokens, and receives original tokens from the vault. The wrapper is coordinating two token programs and several transactions; it is not changing the original mint into a confidential mint.
The CLI exposes init, wrap, unwrap, and info, with utilities for funding an account and checking its SOL balance. The sender-to-recipient confidential transfer is demonstrated separately in main.rs; there is no transfer subcommand in this wrapper CLI.
Where this leaves the project
This repository is an exploration of the mechanics, not a production custody system. The wrapper currently derives its vault-controlling keypair from public inputs. That is reproducible, but it cannot serve as a secret authority. A production design needs a different authority model, such as a program-controlled vault with explicit rules for minting and redemption.
The wrap and unwrap operations also run as successive transactions. A failure halfway through can leave only part of the sequence completed. Safe recovery, enforced backing, and authority design need to be addressed together before this wrapper could hold real funds. These diagrams explain the code’s intended flow; they are not evidence of a production deployment.
What this project makes tangible is that confidential transfers are still bookkeeping. The amounts are hidden, but ownership, balance transitions, proof verification, and failure handling all need to be explicit. That is the part of building privacy infrastructure I find most interesting.