Introduction to Rings
Every transaction on Solana is publicly readable: addresses, balances, and transfer history are visible to anyone. Rings encrypt onchain balances and are always non-custodial by default:- In confidential Rings, the asset and amount are private, while the sender and recipient stay visible.
- In anonymous Rings the asset, amount, sender, and recipient are private.
- The Default Ring is permissionless and confidential. It encrypts asset and amount, can be used by anyone, and does not have custom policy controls.
- Custom Rings are Solana programs that make privacy on Solana programmable, similar to Token-2022. A Custom Ring can be confidential or anonymous with custom policy, reporting, and visibility rules.
For most applications we recommend the permissionless, confidential Default Ring.
For custom policy and compliance controls, contact us to get started with a custom Ring.
Custom Rings Are Programmable
Custom Rings are simple Solana programs that make privacy programmable, with features similar to Token-2022. A Custom Ring can define fully custom rules for transfers and visibility/auditing.Example Custom Ring policies
Example Custom Ring policies
User Flows and Privacy Guarantees
Transactions with Helius Privacy execute natively onchain and compose with other Solana programs in a single transaction. Users can enter, transfer privately within, or exit from a Ring: 1. Deposit or on ramp. Users send tokens from a public Solana balance, or a fiat balance, directly to a private balance. 2. Private transfer. Users send tokens between private balances in the same Ring, or to a different one. 3. Withdraw or off ramp. Users send tokens from a private to a public Solana balance, or directly to a fiat balance.Deposit to a Private Balance: What Is Private
Users can deposit to a private balance in two ways:- On-ramp from a fiat balance to a private crypto balance
- Deposit from a public to a private crypto balance
- On-ramp from fiat balance
- Deposit from public crypto balance
Private Transfer: What Is Private
Users can send a private transfer in two ways:- Transfer in the same Ring
- Transfer to a different Ring
For most applications we recommend the permissionless, confidential Default Ring.
For custom policy and compliance controls, contact us to get started with a custom Ring.
- Permissionless Confidential Ring
- Custom Rings
Withdraw from a Private Balance: What Is Private
Users can withdraw from a private balance in two ways:- Off-ramp from private crypto to a fiat balance
- Withdraw from private crypto to a public crypto balance
- Off-ramp to fiat balance
- Withdraw to public crypto balance
High-Level Transaction Flow
A private transfer behaves similarly to public transfers and is executed in a single Solana transaction.- Private transfer
- Solana transfer
- The user’s SOL or SPL balance is encrypted onchain.
- Fetch encrypted state and decrypt locally, or a delegated provider decrypts and serves decrypted state.
- The wallet sets amount and recipient, then requests a ZK proof.
- The RPC provider generates the ZK proof by default and returns it. A Custom Ring also requires a policy proof.
- The wallet builds the Solana transaction. ZK proofs are verified without revealing the encrypted state. The invoked programs and who signs and submits depend on the Ring:
- The app tracks status via the Solana transaction hash.
- Permissionless Confidential Ring
- Custom Confidential Ring
- Custom Anonymous Ring
Private Wallet
Users hold their encrypted balance in a Private Wallet. The Private Wallet receives transfers at the user’s public Solana address, serving as theinbox.
This way, the sender always enters the recipient’s Solana address, as they do with public transfers.
Creation of a Private Wallet
Upon creation of a private wallet, the Solana address is registered in an onchain registry. The registry maps one Solana address to one . Transfers are encrypted under the hood to the Shielded Address. Multiple Solana addresses cannot share one Shielded Address. Each registry record is a PDA owned by the , which anyone can look up to see whether a Solana address can receive private transfers.In your wallet app, you can let your users create private wallets only for a dedicated “Private Wallet” public key,
or for any public key.
Transfers to a Private Wallet
On every transfer, the Privacy API looks up the wallet address in the onchain registry to check if the recipient has a private wallet. On a hit, it uses the Shielded Address under the hood to encrypt the transfer.When the recipient of a private transfer does not have a Private Wallet, the Privacy API resolves the transfer to a private-to-public withdrawal.We recommend requesting a separate user signature in the UI to ensure the user does not withdraw to a public balance by accident.
Key Management
A Private Wallet adds a shielded keypair to a Solana wallet from your Solana keypair.
The shielded keypair is a bundle of the signing, nullifier, and viewing keys. It is not a Solana keypair.
The Shielded Address is the shielded keypair’s public keys. Transfers encrypt to it.
Creating a Shielded Keypair
A wallet can create the shielded keypair in two ways.-
Derive keys from the Seed phrase.
- The recommended approach for seed based wallets.
- Confidential Rings: the signing key is the same Ed25519 key a Solana wallet derives from that seed. The nullifier and viewing keys are additional keys derived from the same seed.
- Custom Anonymous Rings: the signing key is an additional P-256 key derived from the wallet seed.
- You can always recreate the shielded keypair the same way you recreate the Solana wallet.
-
Derive keys from a fixed message with the existing Solana key.
- Solana signatures are deterministic, hence the same Solana keypair always produces the same shielded keypair. The app never receives the private key.
- The recommended approach for tests and wallets that do not use a seed phrase. The integrating wallet must ensure that the signature is not exposed to third parties.
Integrating a Shielded Keypair
A wallet can hold the shielded keypair itself via the native Wallet Integration. With the Embedded Privacy Wallet, a Wallet Provider holds the shielded keypair and runs its key operations instead.Decryption Modes and Wallet Sync
Only the owner can decrypt a Private Wallet Balance with a Viewing Key. For selective disclosure, an owner can share a viewing key with an auditor, so the auditor can read activity without being able to spend. In custom rings, the policy can declare an auditor that can decrypt all balances from that custom ring. Confidential rings (the Default Ring and confidential Custom Rings) support local and delegated decryption. Anonymous rings (Custom Rings only) support only delegated decryption.A wallet should decrypt and update balances wherever it runs backfills: on wallet unlock,
private wallet open, app resume, network reconnect, stream gap, or wallet restore.
Local Decryption
In local decryption mode, the wallet decrypts and syncs balances and history locally. Helius servers do not receive the mnemonic, seed, signing private key, viewing private keys, or nullifier secret.Delegated Decryption
In delegated decryption mode, the wallet and selected provider share a viewing key. This lets the provider decrypt balances and history, but it does not grant spending authority. A wallet may also share prior viewing keys when it authorizes the provider to sync historical activity. Auditor access is separate from delegated decryption. An auditor receives policy-defined Ring visibility, while a delegated provider receives wallet-scoped sync access.Indexer Serves Encrypted State
Just as public wallets rely on a Solana RPC, Private Wallets rely on an indexer. The indexer serves the encrypted state a wallet needs to read balances and build private transactions. By default, the indexer is accessible via the ; anyone can also run their own indexer permissionlessly as a fallback.Private Wallet Balance
The Private Wallet Balance is held onchain in Private Solana Token Accounts as UTXOs (unspent transaction outputs), not a Solana token account. The Default Ring and all Custom Rings store private balances in one state Merkle tree. The Private Wallet Balance is the sum of all UTXOs owned by a Private Wallet. Solana programs can own UTXOs similar to regular Solana accounts. This documentation uses Private Solana Token Accounts and UTXOs interchangeably.Balances in UTXOs vs Solana accounts
Balances in UTXOs vs Solana accounts
For the user experience, it does not matter whether the balance is held in UTXOs or Solana token accounts.At a technical level, spending a public balance reduces the 
amount in a Solana token account.
Spending a Private Balance does not overwrite the amount field of a UTXO.
Instead, private transactions take existing UTXOs as inputs (automatically chosen by the SDK),
consume the inputs and create new outputs for the recipient and for the sender’s remaining balance.For example, Alice has 50 USDC and sends Bob 35.- In a Solana token account, her
amountfield goes from 50 to 15, and Bob’s goes up by 35. The number changes in place. - With UTXOs, Alice holds one 50 USDC note. The transaction spends it and creates two new notes: a 35 USDC note for Bob, and a 15 USDC change note for Alice. Her old 50 note is now spent.
- One input note
- Multiple input notes
At deposit to a private balance of SOL and SPL assets, an interface PDA owned by the Solana Privacy Program escrows tokens and creates UTXOs with the user as owner.At withdrawal to a public balance, existing UTXOs are marked as spent, and tokens are released to the Solana token accounts.
policy_program_id field.
Private Solana Token Accounts
A Private Solana Token Account is a UTXO that behaves similarly to a Solana token account with two core differences:- a Private Solana Token Account does not require rent-exemption upon creation, and
- its balance is encrypted onchain.
- Asset: the mint the UTXO holds units of. SOL is the default mint address. The asset can be SOL or any SPL or Token-2022 asset.
- Amount: the number of units of
assetthe note holds, in the asset’s smallest unit. - Program and policy data: optional data for the configured ring policy to add Token-2022-like functionality
- Diagram
- Code
View Solana token account
View Solana token account
- Diagram
- Code