Skip to main content
  1. A private transfer moves tokens in a ring between private wallets.
  2. Private transfers are sent in a single Solana transaction to a Solana wallet address.

Transfer: What Is Private

The permissionless Ring is confidential with encrypted amount and asset. A custom Ring can be configured as confidential or anonymous (encrypted sender, recipient, asset, and amount).

How a Transfer Works

A private transfer behaves similarly to a public Solana transfer:
  1. The user’s SOL or SPL balance is encrypted onchain.
  2. The user decrypts private state, the wallet builds a transfer, and the owner signs.
    • Fetch encrypted state with dedicated RPC methods. Only the user can decrypt balances locally.
    • The wallet sets amount and recipient, then requests a ZK proof. The RPC provider generates the ZK proof by default and returns it.
  3. The Solana runtime verifies the signatures and invokes the Solana Privacy Program, which verifies the ZK proof without revealing the encrypted state.
  4. The app tracks status via the Solana transaction hash.
  1. The user’s SOL or SPL balance is public onchain.
  2. The wallet reads public state, builds a transfer, and the owner signs.
  3. The Solana runtime verifies the signatures and invokes the System Program or Token Program, which updates the public balance.
  4. The app tracks status via the Solana transaction hash.
This is the high-level transaction flow for the permissionless confidential Ring. Compare to custom Rings in concepts.

Get Started

1

Prerequisites

The TypeScript examples require Node.js 24 or later, pnpm 11.18.0, and the Solana CLI.
Source: sdk-libs/ts
Add a Helius API key:
.env
The examples use the Solana CLI wallet as the payer by default. The payer must hold devnet SOL. See How to Get Devnet SOL.
2

Transfer to a Private Balance

  • The SDK returns instructions. The app signs and sends them.
  • sendAndConfirmFactory builds a Kit transaction, submits it, and returns the signature plus the landed slot.
  • The example spends the Private Solana Token Account created by the preceding deposit. A transfer can spend multiple UTXOs.
  • transferUtxo selects one Private Solana Token Account from that balance.
  • ProofInputUtxo.fromKeypair prepares the selected UTXO as a proof input with the sender’s private wallet keypair.
  • The keypair derives the nullifier that marks the input UTXO as spent while the asset and amount remain encrypted.
  • senderAddress is the sender’s . The transfer spends from this wallet.
  • [transferInput] lists the sender’s selected UTXOs. A transfer can spend multiple UTXOs.
  • senderSigner.address is the fee payer’s Solana address. A gas sponsor can fill this role for a confidential transfer.
  • recipient is the recipient’s Shielded Address. The transferred output is encrypted to the recipient’s viewing key.
  • SOL_MINT selects SOL. An SPL or Token 2022 transfer passes the token mint.
  • TRANSFER_AMOUNT is denominated in the asset’s base units. SOL uses lamports. SPL and Token 2022 assets use the token’s base units.
  • transfer.sign authorizes the state transition, encrypts the asset and amount, and produces the inputs for the zero-knowledge prover.
  • assets is the asset registry used to resolve supported private assets.
  • client.proveTransact generates the zero-knowledge proof from the signed transfer and returns serialized instruction data.
  • The proof demonstrates that the sender owns and can spend the inputs without revealing the encrypted asset or amount.
  • payer signs and pays for the Solana transaction. A gas sponsor can fill this role for a confidential transfer.
  • inputTree and outputTree are client.tree, the state Merkle tree that contains the spent UTXOs and receives the recipient output and sender change.
  • data contains the zero-knowledge proof and encrypted outputs produced in the previous step.
  • A private transfer moves the asset only between private balances. It does not pass public Solana accounts or token accounts.
  • sendAndConfirm signs and submits transferInstruction as a Solana transaction.
  • Confirmation yields the landed slot used to gate the indexer fetch.

Full Code Example

Clone and run the example:
The examples use a confidential Ring on local/devnet here.
deposit_transfer_withdraw.ts

Deposit

Withdraw

Read a Private Balance

Read Private History

Didn’t find what you were looking for?

Reach out! Telegram | E-Mail | Contact