> ## Documentation Index
> Fetch the complete documentation index at: https://www.helius.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Concepts

> Understand how Helius Privacy works, including user flows, what stays public or private on a transfer, and rings with customizable policy.

## 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.

<img src="https://mintcdn.com/helius/ANceN_N6YhA2y60D/images/privacy/transaction-visibility-cards.svg?fit=max&auto=format&n=ANceN_N6YhA2y60D&q=85&s=d775dd0d4ebcf7b299ef0f3ccfb025ca" alt="Two Solana transaction cards side by side. The confidential card shows the transaction hash, sender, and recipient while the asset and amount are blacked out. The anonymous card shows only the transaction hash; the sender, asset, amount, and recipient are all blacked out. The transaction hash stays public on both." width="2320" height="540" data-path="images/privacy/transaction-visibility-cards.svg" />

Different kinds of Rings exist:

1. The Default Ring is permissionless and confidential. It encrypts asset and amount, can be used by anyone, and does not have custom policy controls.
2. 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.

<Info>
  For most applications we recommend the permissionless, confidential Default Ring.
  For custom policy and compliance controls, [contact us](https://www.helius.dev/contact) to get started with a custom Ring.
</Info>

<img src="https://mintcdn.com/helius/iNBk0QZ3vdaUiFW5/images/privacy/privacy-network.svg?fit=max&auto=format&n=iNBk0QZ3vdaUiFW5&q=85&s=af61b324d07f1184da1f3912d9f050fb" alt="Private Balances shown as a dashed onchain zone holding two rings. The Default Ring is confidential, self-custodial, and permissionless. The Custom Ring is confidential or anonymous, self-custodial, and adds custom policy and custom compliance. A private transfer connects the rings, and each deposits into and withdraws from a Solana Public Balances layer below." width="850" height="540" data-path="images/privacy/privacy-network.svg" />

### 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.

<Accordion title="Example Custom Ring policies">
  | Policy                | Example                                                               |
  | --------------------- | --------------------------------------------------------------------- |
  | Allowed assets        | Only USDC or a specific issued stablecoin                             |
  | Transfer limits       | Require extra approval above a threshold                              |
  | Co-signing            | Require compliance approval for selected payments                     |
  | Allowlists            | Only allow transfers to approved accounts, merchants, or rings        |
  | Blocklists            | Block sanctioned or risky destinations                                |
  | Freeze/thaw           | Freeze a balance while an investigation is pending                    |
  | Permanent delegate    | Allow a configured authority to move funds under policy               |
  | Auditor visibility    | Let compliance, accounting, or regulators view ring activity          |
  | Cross-ring rules      | Allow or block movement between specific rings                        |
  | Withdrawal rules      | Restrict public withdrawals or require approval                       |
  | Smart account support | Allow native use with smart accounts                                  |
  | Account recovery      | Allow a configured authority or fallback identity to recover accounts |
</Accordion>

Ring authorities are declared at ring creation, so users can inspect the policy before choosing a ring.
The Default Ring is permissionless and does not have a custom authority or auditor.

<Tip>
  If you want to set up your own Custom Ring, [contact us](https://www.helius.dev/contact) and we will get you set up.
  You can either bring your own compliance, or use Helius’ managed KYC and sanction screening solution.
</Tip>

## 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.

<img src="https://mintcdn.com/helius/df3Lmw7POmPkuUJN/images/privacy/privacy-rings-lifecycle.svg?fit=max&auto=format&n=df3Lmw7POmPkuUJN&q=85&s=54d057b2674ad3a194575858d35db167" alt="Diagram of the Ring lifecycle. A public balance of 100 USDC deposits into a Ring and a fiat balance of USD / EUR / ... on-ramps into the same Ring, funds move between two private balances via private transfer, then withdraw to a public balance or off-ramp to a fiat balance." width="1420" height="240" data-path="images/privacy/privacy-rings-lifecycle.svg" />

Users send tokens to a Solana wallet address, as they do with public balances.
The Privacy API routes the transfer automatically to the Private Wallet, if the recipient has one.
Otherwise the transfer is a withdrawal to the recipient's public address.

Anyone can deposit to any Private Wallet by knowing only the recipient's public wallet address.
When the sender's app does not support privacy, the recipient receives the funds publicly and can encrypt them to their own private balance in a second step.

### Deposit to a Private Balance: What Is Private

Users can deposit to a private balance in two ways:

1. On-ramp from a fiat balance to a private crypto balance
2. Deposit from a public to a private crypto balance

<Tabs>
  <Tab title="On-ramp from fiat balance">
    | Field                    | Visibility                                                  | Why                                                                                                                                                                                                                                                                                                   |
    | ------------------------ | ----------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | Fiat balance and payment | Not onchain                                                 | The fiat payment is not onchain                                                                                                                                                                                                                                                                       |
    | Asset                    | Private                                                     | The asset is encrypted onchain                                                                                                                                                                                                                                                                        |
    | Amount                   | Private                                                     | The transferred amount is encrypted onchain                                                                                                                                                                                                                                                           |
    | Onramp Provider          | Public in a confidential Ring, private in an anonymous Ring | In a confidential Ring the wallet address of the provider is visible. An anonymous Ring uses a relayer, so the public ledger does not reveal the source private wallet.                                                                                                                               |
    | User wallet address      | Public in a confidential Ring, private in an anonymous Ring | In a confidential Ring the user wallet address is visible onchain. The user address used for the onramp cannot be linked to the user, if it is new and is not later tied to a known wallet address by a confidential transfer. In an anonymous Ring, the public ledger does not reveal the recipient. |
  </Tab>

  <Tab title="Deposit from public crypto balance">
    | Field                      | Visibility | Why                                                |
    | -------------------------- | ---------- | -------------------------------------------------- |
    | Source public wallet       | Public     | The source wallet address is visible onchain       |
    | Asset                      | Public     | The asset is visible onchain                       |
    | Amount                     | Public     | The deposited amount is visible onchain            |
    | Destination wallet address | Public     | The destination wallet address is visible onchain  |
    | Resulting private balance  | Private    | The resulting private balance is encrypted onchain |
  </Tab>
</Tabs>

### Private Transfer: What Is Private

Users can send a private transfer in two ways:

1. Transfer in the same Ring
2. Transfer to a different Ring

<Info>
  For most applications we recommend the permissionless, confidential Default Ring.
  For custom policy and compliance controls, [contact us](https://www.helius.dev/contact) to get started with a custom Ring.
</Info>

<Tabs>
  <Tab title="Permissionless Confidential Ring">
    | Field                 | Visibility | Why                                                                  |
    | --------------------- | ---------- | -------------------------------------------------------------------- |
    | Asset                 | Private    | The asset is encrypted onchain                                       |
    | Amount                | Private    | The transferred amount is encrypted onchain                          |
    | Source private wallet | Public     | In a confidential Ring the source private wallet is visible onchain. |
    | Recipient             | Public     | In a confidential Ring the recipient is visible onchain.             |
  </Tab>

  <Tab title="Custom Rings">
    Transfers from and within a ring reveal the program ID of the custom Ring.

    A balance in a Custom Ring can exit to an SPL token account, to the Default Ring, or to another Ring, as long as the source Ring's policy permits it.
    The default Ring is permissionless and does not have a policy or authority.

    One transaction can combine balances from the Default Ring and one Custom Ring. A transfer between two Custom Rings routes through the Default Ring.

    | Private transfer       | Custom confidential Rings | Custom anonymous Rings                                                                                      | Default confidential to or from Custom confidential | Confidential (Default or Custom) to Custom anonymous | Custom anonymous to confidential (Default or Custom)                                                        |
    | ---------------------- | ------------------------- | ----------------------------------------------------------------------------------------------------------- | --------------------------------------------------- | ---------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
    | Amount                 | Private                   | Private                                                                                                     | Private                                             | Private                                              | Private                                                                                                     |
    | Asset                  | Private                   | Private                                                                                                     | Private                                             | Private                                              | Private                                                                                                     |
    | Source private wallet  | Public                    | Private. A relayer submits the transaction, so the public ledger does not reveal the source private wallet. | Public                                              | Public                                               | Private. A relayer submits the transaction, so the public ledger does not reveal the source private wallet. |
    | Recipient              | Public                    | Private                                                                                                     | Public                                              | Private                                              | Public                                                                                                      |
    | Custom Ring program ID | Public                    | Public                                                                                                      | Public                                              | Public                                               | Public                                                                                                      |
  </Tab>
</Tabs>

### Withdraw from a Private Balance: What Is Private

Users can withdraw from a private balance in two ways:

1. Off-ramp from private crypto to a fiat balance
2. Withdraw from private crypto to a public crypto balance

<Tabs>
  <Tab title="Off-ramp to fiat balance">
    | Field               | Visibility                                                  | Why                                                                                                                                                                                                                                                                                                        |
    | ------------------- | ----------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | Fiat payout         | Not onchain                                                 | The fiat payout is not onchain                                                                                                                                                                                                                                                                             |
    | Asset               | Private                                                     | The asset is encrypted onchain                                                                                                                                                                                                                                                                             |
    | Amount              | Private                                                     | The transferred amount is encrypted onchain                                                                                                                                                                                                                                                                |
    | Offramp Provider    | Public in a confidential Ring, private in an anonymous Ring | In a confidential Ring an observer can see that a user offramped with this Offramp Provider. In an anonymous Ring the public ledger does not reveal sender and recipient.                                                                                                                                  |
    | User wallet address | Public in a confidential Ring, private in an anonymous Ring | In a confidential Ring the user wallet address is visible onchain. Another wallet address cannot be linked to this address if this address is new and is not later tied to a known wallet address by a confidential transfer. In an anonymous Ring the public ledger does not reveal sender and recipient. |
  </Tab>

  <Tab title="Withdraw to public crypto balance">
    | Field                     | Visibility | Why                                                                  |
    | ------------------------- | ---------- | -------------------------------------------------------------------- |
    | Source private wallet     | Public     | In a confidential Ring the source private wallet is visible onchain. |
    | Asset                     | Public     | The asset is visible onchain                                         |
    | Amount                    | Public     | The withdrawn amount is visible onchain                              |
    | Destination public wallet | Public     | The destination wallet address is visible onchain                    |
    | Resulting public balance  | Public     | The resulting public balance is visible onchain                      |
    | Remaining private balance | Private    | The remaining balance is encrypted onchain                           |
  </Tab>
</Tabs>

## High-Level Transaction Flow

A private transfer behaves similarly to public transfers and is executed in a single Solana transaction.

<Tabs>
  <Tab title="Private transfer">
    1. The user's SOL or SPL balance is encrypted onchain.
    2. Fetch encrypted state and decrypt locally, or a delegated provider decrypts and serves decrypted state.
    3. The wallet sets amount and recipient, then requests a ZK proof.
    4. The RPC provider generates the ZK proof by default and returns it. A Custom Ring also requires a policy proof.
    5. 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:

    |                  | Permissionless Confidential Ring                 | Custom Confidential Ring                         | Custom Anonymous Ring                                      |
    | ---------------- | ------------------------------------------------ | ------------------------------------------------ | ---------------------------------------------------------- |
    | Invoked Programs | Solana Privacy Program                           | Custom Ring program and Solana Privacy Program   | Custom Ring program and Solana Privacy Program             |
    | Signer           | The sender signs. A gas sponsor may pay the fee. | The sender signs. A gas sponsor may pay the fee. | A relayer, so the sender is not linked to the transaction. |

    6. The app tracks status via the Solana transaction hash.

    <Tabs sync={false}>
      <Tab title="Permissionless Confidential Ring">
        ```mermaid theme={"system"}
        %%{init: {
          'theme': 'base',
          'themeVariables': {
            'lineColor':           '#FF6B35',
            'primaryTextColor':    '#737373',
            'primaryBorderColor':  '#9CA3AF',
            'actorBkg':            '#FFFFFF',
            'actorBorder':         '#9CA3AF',
            'actorTextColor':      '#737373',
            'signalColor':         '#FF6B35',
            'signalTextColor':     '#737373',
            'labelBoxBkgColor':    '#FF6B351F',
            'labelBoxBorderColor': '#FF6B35',
            'noteBkgColor':        '#F5F5F5',
            'noteTextColor':       '#737373',
            'noteBorderColor':     '#9CA3AF'
          }
        }}%%
        sequenceDiagram
            participant Wallet
            participant RPC as RPC Provider
            participant Solana

            rect rgba(255, 107, 53, 0.06)
                Note left of Wallet: Local
                Wallet->>RPC: Fetch encrypted state
                RPC-->>Wallet: Encrypted state
                Note over Wallet: Decrypt locally
            end
            rect rgba(255, 107, 53, 0.06)
                Note left of Wallet: Delegated
                Wallet->>RPC: Get private balance
                Note over RPC: Decrypt and serve decrypted state
                RPC-->>Wallet: Decrypted state
            end

            Note over Wallet: Set amount and recipient
            Wallet->>RPC: Request ZK proof
            Note over RPC: Generate ZK proof
            RPC-->>Wallet: ZK proof
            Note over Wallet: Build transaction, sign

            Wallet->>RPC: Submit Transaction
            RPC->>Solana: Forward transaction
            Note over Solana: Verify signatures
            Note over Solana: CPI Solana Privacy Program
            Note over Solana: Verify ZK proof
            RPC-->>Wallet: Transaction signature
        ```
      </Tab>

      <Tab title="Custom Confidential Ring">
        ```mermaid theme={"system"}
        %%{init: {
          'theme': 'base',
          'themeVariables': {
            'lineColor':           '#FF6B35',
            'primaryTextColor':    '#737373',
            'primaryBorderColor':  '#9CA3AF',
            'actorBkg':            '#FFFFFF',
            'actorBorder':         '#9CA3AF',
            'actorTextColor':      '#737373',
            'signalColor':         '#FF6B35',
            'signalTextColor':     '#737373',
            'labelBoxBkgColor':    '#FF6B351F',
            'labelBoxBorderColor': '#FF6B35',
            'noteBkgColor':        '#F5F5F5',
            'noteTextColor':       '#737373',
            'noteBorderColor':     '#9CA3AF'
          }
        }}%%
        sequenceDiagram
            participant Wallet
            participant RPC as RPC Provider
            participant Solana

            rect rgba(255, 107, 53, 0.06)
                Note left of Wallet: Local
                Wallet->>RPC: Fetch encrypted state
                RPC-->>Wallet: Encrypted state
                Note over Wallet: Decrypt locally
            end
            rect rgba(255, 107, 53, 0.06)
                Note left of Wallet: Delegated
                Wallet->>RPC: Get private balance
                Note over RPC: Decrypt and serve decrypted state
                RPC-->>Wallet: Decrypted state
            end

            Note over Wallet: Set amount and recipient
            Wallet->>RPC: Request ZK proof
            Note over RPC: Generate ZK proof and policy proof
            RPC-->>Wallet: ZK proof and policy proof
            Note over Wallet: Build transaction, sign

            Wallet->>RPC: Submit Transaction
            RPC->>Solana: Forward transaction
            Note over Solana: Verify signatures
            Note over Solana: CPI Custom Ring Program
            Note over Solana: Verify policy ZK proof
            Note over Solana: CPI Solana Privacy Program
            Note over Solana: Verify SPP ZK proof
            RPC-->>Wallet: Transaction signature
        ```
      </Tab>

      <Tab title="Custom Anonymous Ring">
        ```mermaid theme={"system"}
        %%{init: {
          'theme': 'base',
          'themeVariables': {
            'lineColor':           '#FF6B35',
            'primaryTextColor':    '#737373',
            'primaryBorderColor':  '#9CA3AF',
            'actorBkg':            '#FFFFFF',
            'actorBorder':         '#9CA3AF',
            'actorTextColor':      '#737373',
            'signalColor':         '#FF6B35',
            'signalTextColor':     '#737373',
            'labelBoxBkgColor':    '#FF6B351F',
            'labelBoxBorderColor': '#FF6B35',
            'noteBkgColor':        '#F5F5F5',
            'noteTextColor':       '#737373',
            'noteBorderColor':     '#9CA3AF'
          }
        }}%%
        sequenceDiagram
            participant Wallet
            participant RPC as RPC Provider
            participant Relayer
            participant Solana

            rect rgba(255, 107, 53, 0.06)
                Note left of Wallet: Delegated
                Wallet->>RPC: Get private balance
                Note over RPC: Decrypt and serve decrypted state
                RPC-->>Wallet: Decrypted state
            end

            Note over Wallet: Set amount and recipient, sign
            Wallet->>RPC: Request ZK proof
            Note over RPC: Generate ZK proof and policy proof
            RPC-->>Wallet: ZK proof and policy proof
            Note over Wallet: Build transaction

            Wallet->>Relayer: Submit Transaction
            Relayer->>Solana: Forward transaction
            Note over Solana: Verify signatures
            Note over Solana: CPI Custom Ring Program
            Note over Solana: Verify policy ZK proof
            Note over Solana: CPI Solana Privacy Program
            Note over Solana: Verify SPP ZK proof
            Relayer-->>Wallet: Transaction signature
        ```
      </Tab>
    </Tabs>
  </Tab>

  <Tab title="Solana transfer">
    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.

    ```mermaid theme={"system"}
    %%{init: {
      'theme': 'base',
      'themeVariables': {
        'lineColor':           '#FF6B35',
        'primaryTextColor':    '#737373',
        'primaryBorderColor':  '#9CA3AF',
        'actorBkg':            '#FFFFFF',
        'actorBorder':         '#9CA3AF',
        'actorTextColor':      '#737373',
        'signalColor':         '#FF6B35',
        'signalTextColor':     '#737373',
        'labelBoxBkgColor':    '#FF6B351F',
        'labelBoxBorderColor': '#FF6B35',
        'noteBkgColor':        '#F5F5F5',
        'noteTextColor':       '#737373',
        'noteBorderColor':     '#9CA3AF'
      }
    }}%%
    sequenceDiagram
        participant Wallet
        participant RPC
        participant Solana

        Wallet->>RPC: Get public balance
        RPC-->>Wallet: Public state
        Note over Wallet: Build transfer, owner signs
        Wallet->>RPC: sendTransaction
        RPC->>Solana: Forward transaction
        Note over Solana: Verify signatures
        Note over Solana: CPI System / Token Program
        Note over Solana: Update balance
        RPC-->>Wallet: Transaction signature
    ```
  </Tab>
</Tabs>

## 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 the `inbox`.
This way, the sender always enters the recipient's Solana address, as they do with public transfers.

| Comparison        | Solana wallet         | Private Wallet                 |
| ----------------- | --------------------- | ------------------------------ |
| Public Identifier | Solana address        | The same Solana address        |
| Onchain Storage   | Public token accounts | Private token accounts (UTXOs) |

### 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 <Tooltip tip="The Shielded Address is not a Solana keypair, it is deterministically derived from the signing, nullifier, and viewing public keys.">Shielded Address</Tooltip>.
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 <Tooltip tip="regyS5rkAcw2YzDJCmTwCTHs2s246FXxbmuRZ42u2PD">[registry program](https://orbmarkets.io/address/regyS5rkAcw2YzDJCmTwCTHs2s246FXxbmuRZ42u2PD)</Tooltip>, which anyone can look up to see whether a Solana address can receive private transfers.

<img src="https://mintcdn.com/helius/NHXFc7ysJd1NoY9P/images/privacy/wallet-shape.svg?fit=max&auto=format&n=NHXFc7ysJd1NoY9P&q=85&s=7e96783047c79ed30c4e995623f197a6" alt="User wallet showing two public Solana wallets and a private wallet. An inbox arrow connects Public Wallet A's public key to the private wallet's Shielded Address." width="1120" height="570" data-path="images/privacy/wallet-shape.svg" />

<Info>
  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.
</Info>

### 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.

<Info>
  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.
</Info>

```mermaid theme={"system"}
%%{init: {
  'theme': 'base',
  'themeVariables': {
    'lineColor':           '#FF6B35',
    'primaryTextColor':    '#737373',
    'primaryBorderColor':  '#9CA3AF',
    'actorBkg':            '#FFFFFF',
    'actorBorder':         '#9CA3AF',
    'actorTextColor':      '#737373',
    'signalColor':         '#FF6B35',
    'signalTextColor':     '#737373',
    'labelBoxBkgColor':    '#FF6B351F',
    'labelBoxBorderColor': '#FF6B35',
    'noteBkgColor':        '#F5F5F5',
    'noteTextColor':       '#737373',
    'noteBorderColor':     '#9CA3AF'
  }
}}%%
sequenceDiagram
    participant User
    participant Privacy API
    participant Solana

    User->>Privacy API: Initiate Private Transfer
    Privacy API->>Solana: Look up Solana address
    rect rgba(255, 107, 53, 0.06)
        Note left of User: Recipient has a Private Wallet
        Solana-->>Privacy API: Registered
        Privacy API->>User: Request Signature
        User->>Privacy API: Sign private transfer
        Privacy API-->>Solana: Send Private Transfer
    end
    rect rgba(255, 107, 53, 0.06)
        Note left of User: Recipient has no Private Wallet
        Solana-->>Privacy API: Not Registered
        Privacy API->>User: Request Signature
        User->>Privacy API: Sign to Confirm Withdrawal
        Privacy API-->>Solana: Private-to-Public Withdrawal
    end
```

### Key Management

A Private Wallet adds a shielded keypair to a Solana wallet from your Solana keypair.

| Key              | Solana wallet               | Private Wallet                                     |
| ---------------- | --------------------------- | -------------------------------------------------- |
| Public key       | Shared to receive transfers | Shared to receive transfers                        |
| Private key      | Used to sign transactions   | Used to sign transactions                          |
| Seed phrase      | Recreates the wallet        | Recreates the wallet                               |
| Shielded keypair | N/A                         | Bundle of the signing, nullifier, and viewing keys |

The shielded keypair is a bundle of the signing, nullifier, and viewing keys. It is not a Solana keypair.

| Key           | Confidential Rings                | Custom Anonymous Rings            |
| ------------- | --------------------------------- | --------------------------------- |
| Signing key   | The public wallet's Ed25519       | The private wallet's P-256        |
| Nullifier key | Mark UTXOs as spent               | Mark UTXOs as spent               |
| Viewing key   | Read-only P-256. Decrypt balances | Read-only P-256. Decrypt balances |

The Shielded Address is the shielded keypair's public keys. Transfers encrypt to it.

```
ShieldedAddress = (signing_pk, nullifier_pk, viewing_pk)
```

#### Creating a Shielded Keypair

A wallet can create the shielded keypair in two ways.

1. **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.

2. **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](/docs/privacy/integration/wallet).
With the [Embedded Privacy Wallet](/docs/privacy/integration/embedded-wallet), a Wallet Provider holds the shielded keypair and runs its key operations instead.

| Key management                                                  | Who holds the shielded keypair | Where its operations run                |
| --------------------------------------------------------------- | ------------------------------ | --------------------------------------- |
| Native [Wallet Integration](/docs/privacy/integration/wallet)        | The wallet                     | On the device                           |
| [Embedded Privacy Wallet](/docs/privacy/integration/embedded-wallet) | A Wallet Provider              | In the Wallet Provider's infrastructure |

### 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.

| Mode      | Where decryption happens | Who has view access | Wallet tradeoff                                                 | Who uses this                                                      |
| --------- | ------------------------ | ------------------- | --------------------------------------------------------------- | ------------------------------------------------------------------ |
| Local     | Inside the wallet        | Wallet only         | Best privacy but requires local state and sync lifecycle        | Confidential rings: the Default Ring and confidential Custom Rings |
| Delegated | Authorized RPC provider  | Wallet + provider   | Less local sync work but provider can view balances and history | Any ring; the only mode in anonymous Custom Rings                  |

<Info>
  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.
</Info>

#### 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 <Tooltip text="https://devnet.helius-rpc.com/?api-key=<API_KEY> / https://mainnet.helius-rpc.com/?api-key=<API_KEY>">Helius RPC URL</Tooltip>; 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.

<img src="https://mintcdn.com/helius/KNGZoSuCXDUVm6yU/images/privacy/utxo-set-to-wallet.svg?fit=max&auto=format&n=KNGZoSuCXDUVm6yU&q=85&s=92c14cfcf72e56cbaff498a7b95c638b" alt="A Private Wallet connected by dashed lines to several UTXO notes, each holding one amount of one asset. The wallet balance is the sum of the notes." width="700" height="276" data-path="images/privacy/utxo-set-to-wallet.svg" />

<Accordion title="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 `amount` field 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.

  <Tabs>
    <Tab title="One input note">
      <img src="https://mintcdn.com/helius/KNGZoSuCXDUVm6yU/images/privacy/spend-comparison.svg?fit=max&auto=format&n=KNGZoSuCXDUVm6yU&q=85&s=3d0ce046ce268daf75ab8546766bc427" alt="Alice sends Bob 35 of her 50 USDC. On a Solana token account the amount field changes in place from 50 to 15. With UTXOs, Alice's single 50 USDC note is spent and two new notes are created: a 35 USDC note for Bob and a 15 USDC change note for Alice." width="1016" height="240" data-path="images/privacy/spend-comparison.svg" />
    </Tab>

    <Tab title="Multiple input notes">
      <img src="https://mintcdn.com/helius/KNGZoSuCXDUVm6yU/images/privacy/spend-comparison-multi.svg?fit=max&auto=format&n=KNGZoSuCXDUVm6yU&q=85&s=f4502ff2086c41f41908e337f3c12f7b" alt="Alice sends Bob 35 of her 50 USDC, held as three notes of 20, 20, and 10. On a Solana token account the amount field changes in place from 50 to 15. With UTXOs, all three input notes are spent and two new notes are created: a 35 USDC note for Bob and a 15 USDC change note for Alice." width="1016" height="240" data-path="images/privacy/spend-comparison-multi.svg" />
    </Tab>
  </Tabs>

  |            | Solana token account                       | Private Solana token account (UTXO)               |
  | ---------- | ------------------------------------------ | ------------------------------------------------- |
  | Holds      | One balance number in the `amount` field   | A set of notes, each a fixed amount of one asset  |
  | Balance    | The number on the account                  | The sum of all unspent UTXOs                      |
  | Spending   | Subtracts from the `amount` field in place | Consumes input UTXOs and writes a new change UTXO |
  | Visibility | Public onchain                             | Encrypted onchain                                 |

  <Info>
    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.
  </Info>
</Accordion>

One Private Wallet can hold balances in multiple Rings at the same time without limits to the number of Rings.
Which Ring a balance belongs to is not a property of the wallet. It is a property of each UTXO, stored in its `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.

Still, Private Solana Token Accounts store the same data, such as:

* **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 `asset` the 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

<Tabs>
  <Tab title="Diagram">
    <img src="https://mintcdn.com/helius/KNGZoSuCXDUVm6yU/images/privacy/account-comparison-private.svg?fit=max&auto=format&n=KNGZoSuCXDUVm6yU&q=85&s=860d0d2f3894b0a0ac169b82e538a934" alt="A Private Solana Token Account. The Solana Privacy Program owns a Private Token Account, which expands into its PrivateAccount fields: owner, asset, amount, policy data, and policy program id." width="1080" height="590" data-path="images/privacy/account-comparison-private.svg" />
  </Tab>

  <Tab title="Code">
    The full record stored for each Private Solana Token Account is a flat UTXO:

    ```rust Private Solana Token Account (UTXO) theme={"system"}
    struct Utxo {
        /// Constant separating UTXOs from other Poseidon-hashed records.
        domain: u16,
        /// Recipient's `owner_hash` from their Shielded Address.
        /// Senders write this value directly; the spender supplies the preimage
        /// components as proof witness.
        owner: [u8; 32],
        /// Asset mint. SOL is Address::default().
        asset: Address,
        /// Amount in the smallest unit of `asset`.
        amount: u64,
        /// Random bytes ensuring distinct UTXO hashes for equal
        /// `(owner, asset, amount)` triples.
        blinding: [u8; 31],
        /// Arbitrary program data.
        program_data: Option<Vec<u8>>,
        /// Arbitrary policy data.
        policy_data: Option<Vec<u8>>,
        /// The policy program that authorizes spends of this UTXO.
        policy_program_id: Option<Address>,
    }
    ```
  </Tab>
</Tabs>

<Accordion title="View Solana token account">
  <Tabs>
    <Tab title="Diagram">
      <img src="https://mintcdn.com/helius/KNGZoSuCXDUVm6yU/images/privacy/account-comparison-solana.svg?fit=max&auto=format&n=KNGZoSuCXDUVm6yU&q=85&s=89ff901091255f2038972208fcb5d538" alt="A Solana Token Account. The Token Program owns a Token Account, expanded into its AccountInfo fields (Data, Executable, Lamports, Owner) and the Data field expanded into Account Data (Mint, Owner, Amount)." width="1080" height="462" data-path="images/privacy/account-comparison-solana.svg" />
    </Tab>

    <Tab title="Code">
      ```rust Solana Token Account theme={"system"}
      pub struct Account {
          /// The mint associated with this account
          pub mint: Pubkey,
          /// The owner of this account.
          pub owner: Pubkey,
          /// The amount of tokens this account holds.
          pub amount: u64,
          /// If `delegate` is `Some` then `delegated_amount` represents
          /// the amount authorized by the delegate
          pub delegate: COption<Pubkey>,
          /// The account's state
          pub state: AccountState,
          /// If is_native.is_some, this is a native token, and the value logs the
          /// rent-exempt reserve. An Account is required to be rent-exempt, so
          /// the value is used by the Processor to ensure that wrapped SOL
          /// accounts do not drop below this threshold.
          pub is_native: COption<u64>,
          /// The amount delegated
          pub delegated_amount: u64,
          /// Optional authority to close the account.
          pub close_authority: COption<Pubkey>,
      }
      ```
    </Tab>
  </Tabs>
</Accordion>

## On Concurrency

Users can spend private balances as soon as transactions are final.

Namely, the balance of a keypair can be used concurrently when it is split up between a number of UTXOs.
The wallet selects which UTXOs to spend.

One Merkle tree stores private state for the permissionless Default Ring and all Custom Rings.
The tree is stored in one writable Solana account.
Private transfers that write to this account share Solana's 12 million CU per-account write-lock limit per block.

A private transfer consumes approximately 220,000 CU.
One tree therefore supports approximately 54 private transfers per block, or approximately 130 transactions per second at current Solana block times of about 400 milliseconds.

The protocol can add more trees to increase throughput.
Each tree is a separate writable account with its own per-account compute budget, so transactions on different trees do not compete for the same write-lock budget.

## On Proof Generation Time

Zero-knowledge proofs are currently generated by a prover server; a typical confidential transfer proves in tens of milliseconds; local proving for confidential Rings is planned.
Proving time is actively being optimized to ensure a smooth user experience.

## Terms

| Term                         | Meaning                                                                                                                                                                                                           | Also written as                                                                                                |
| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| AES-GCM                      | The symmetric cipher that encrypts UTXO contents and detects tampering                                                                                                                                            |                                                                                                                |
| Auditor                      | A configured viewer for a Custom Ring. Auditors can read ring data but cannot spend                                                                                                                               |                                                                                                                |
| Custom Ring                  | A dedicated enterprise ring with custom rules, visibility, and authorities.                                                                                                                                       | formerly "Policy Ring"                                                                                         |
| Decryption mode              | How a Ring supports balance and history decryption: confidential rings (the Default Ring and confidential Custom Rings) support Local and Delegated; anonymous Custom Rings support Delegated only                |                                                                                                                |
| Delegated decryption         | Sync mode in which a selected provider uses a shared viewing key to read balances and history without spending authority                                                                                          |                                                                                                                |
| Default Ring                 | A group of private wallets with balances in the permissionless confidential Ring; asset and amount are private while sender and recipient remain public                                                           |                                                                                                                |
| Deposit                      | A public-to-private transfer into a Ring from a public Solana address                                                                                                                                             | enter, public-to-private deposit                                                                               |
| ECDH                         | Elliptic Curve Diffie-Hellman, which lets the sender and recipient derive a shared secret from their keys without exchanging it                                                                                   |                                                                                                                |
| Freeze Authority             | A configured authority that can freeze or thaw balances when policy allows                                                                                                                                        |                                                                                                                |
| HPKE                         | Hybrid Public Key Encryption, the scheme combining ECDH key agreement with a symmetric cipher to encrypt a UTXO for a recipient's key                                                                             |                                                                                                                |
| Inbox                        | The Solana public key used as the public receive address for a Private Wallet                                                                                                                                     | public receive address                                                                                         |
| Owner                        | The Solana public key, smart account, or provider-backed signer that authorizes Private Wallet actions                                                                                                            |                                                                                                                |
| Policy program               | The public onchain program that enforces a Custom Ring's rules and reveals which Custom Ring a transaction uses                                                                                                   | policy ruleset, ring policy                                                                                    |
| Privacy API                  | Helius API for creating Private Wallets, reading balances/history, preparing transfers, generating proofs, and submitting transactions                                                                            | RPC Provider (the docs combine the two as "Privacy API / RPC Provider"; see the Trust Model in enterprise.mdx) |
| Private balance              | A user's encrypted balance held inside a Ring                                                                                                                                                                     | shielded balance, encrypted balance, private funds                                                             |
| Private Solana Token Account | An onchain UTXO holding one amount of one asset, owned by a Private Wallet. The Private Wallet Balance is the sum of all unspent UTXOs                                                                            | UTXO, note, unspent transaction output                                                                         |
| Private transfer             | A transfer between Private Wallets that does not reveal asset or amount. On the confidential public ring the sender and recipient stay visible; an anonymous ring uses a relayer and does not reveal either party | private-to-private transfer, in-ring transfer                                                                  |
| Private Wallet               | A payment account with encrypted onchain balances and transfers                                                                                                                                                   | private account, private payment account, managed private account                                              |
| Private Wallet Balance       | The sum of all unspent UTXOs owned by a Private Wallet                                                                                                                                                            |                                                                                                                |
| Public balance               | A user's normal, public SPL token balance outside any Ring                                                                                                                                                        | public account, public token account, SPL token balance                                                        |
| Registry                     | Maps a Solana address to its Shielded Address so senders can pay the address privately. A record means the address can receive private transfers, not that it holds private funds                                 |                                                                                                                |
| Relayer                      | Submits anonymous-Ring transactions and pays their Solana transaction fee, so the sender is not linked to the transaction                                                                                         |                                                                                                                |
| Custom Ring                  | A group of private wallets with balances in a custom Ring governed by a policy.                                                                                                                                   |                                                                                                                |
| Shielded Address             | The public key bundle (signing, nullifier, and viewing keys) published in a user's registry record. Not an onchain address                                                                                        | Shielded Public Key, shielded pubkey                                                                           |
| Shielded keypair             | The bundle of the signing, nullifier, and viewing keys of a Private Wallet. Held by the wallet, or by a Wallet Provider in the Embedded Privacy Wallet                                                            | shielded keys                                                                                                  |
| TSPP                         | The open-source Solana Privacy Protocol, a standard built and maintained by Helius Labs that powers the underlying state transitions onchain.                                                                     | Solana Privacy Protocol                                                                                        |
| Viewing Key                  | A read-only P-256 keypair that decrypts a Private Wallet Balance. The owner can share it with an auditor for selective disclosure                                                                                 | view key                                                                                                       |
| Wallet Provider              | The system that manages user accounts, signers, smart accounts, or treasury wallets and holds non-exportable privacy keys, such as Turnkey, Privy, Squads, Rain, or an internal custody stack                     | Wallet Key Manager, key management service, secure key infrastructure                                          |
| Withdrawal                   | A private-to-public transfer out of a Ring to a public address                                                                                                                                                    | private-to-public withdrawal                                                                                   |

## Didn't find what you were looking for?

<Callout type="info">
  Reach out! [Telegram](https://t.me/tilo_light) | [E-Mail](mailto:sales@helius.xyz) | [Contact](https://www.helius.dev/contact)
</Callout>
