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

# Private State and UTXOs

> Learn how UTXOs store tokens and private state.

<a id="private-solana-token-accounts" />

A private balance is stored in UTXOs (unspent transaction outputs).
You can think of UTXOs as private SPL token accounts, with two core differences:

1. A UTXO is not a Solana account, so it needs no rent-exemption.
2. Its balance is encrypted onchain.

The UTXO data layout is similar to SPL token accounts:

* **Owner** – Solana keypair, PDA, or P-256 key.
* **Asset** – the mint (SOL, SPL or Token-2022).
* **Amount** – the number of units of `asset`, in its smallest unit.
* **Data** – a UTXO can store arbitrary data, for example the owner of escrowed tokens.
* **Program and policy data** – optional configured Ring compliance.

<Tabs>
  <Tab title="Diagram">
    <img src="https://mintcdn.com/helius/ITnDOhn5GRfQ1FQW/images/privacy/account-comparison-private.svg?fit=max&auto=format&n=ITnDOhn5GRfQ1FQW&q=85&s=4143bb77db4e5ff44225882d571b8830" alt="A Private Solana Token Account. The Solana Privacy Program owns a Private Token Account, which expands into its UTXO fields: owner, asset, amount, data, 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 theme={"system"}
    struct Utxo {
        /// Constant separating UTXOs from other Poseidon-hashed data.
        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>

<Info>
  View source code: [Spec](https://github.com/helius-labs/zolana/blob/680667f27cf9ba4203a0b69284f4c304b874092f/docs/spec.md#utxo) · [sdk-libs/transaction/src/utxo/note.rs](https://github.com/helius-labs/zolana/blob/680667f27cf9ba4203a0b69284f4c304b874092f/sdk-libs/transaction/src/utxo/note.rs#L16)
</Info>

<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="Solana token account." width="1080" height="462" data-path="images/privacy/account-comparison-solana.svg" />
    </Tab>

    <Tab title="Code">
      ```rust 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>

<a id="private-wallet-balance" />

## Private Balance

The private balance is the sum of all UTXOs of one asset owned by a private wallet.
The wallet displays one balance, regardless of whether a private balance
consists of one, or multiple UTXOs.
A private transfer can spend several UTXOs at once.

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

## Private Transfer

Private transfers with UTXOs work differently from public transfers with SPL token accounts:

* A transfer from an SPL token account updates the `amount`.
* A private transfer with UTXOs does not update the `amount` of a UTXO.
  Instead, private transfers spend existing UTXOs
  and create new UTXOs for the recipient and for the sender's remaining balance.

Still for the user, public transfer with Solana token accounts
and private transfers with UTXOs feel similar.

For example, Alice has 50 USDC and sends Bob 35 USDC.

* With SPL token accounts, Alice's `amount` decreases from 50 to 15, and Bob's increases by 35.
* With UTXOs, Alice holds one UTXO for 50 USDC. The transaction spends the existing UTXO and creates two new UTXOs: one with 35 USDC for Bob and one with 15 USDC for Alice.

<Tabs>
  <Tab title="Transfer with one UTXO">
    <img src="https://mintcdn.com/helius/ITnDOhn5GRfQ1FQW/images/privacy/spend-comparison.svg?fit=max&auto=format&n=ITnDOhn5GRfQ1FQW&q=85&s=c25d35501ca9fcff9b4c0c7c8654dd73" alt="Spend one UTXO" width="1016" height="240" data-path="images/privacy/spend-comparison.svg" />
  </Tab>

  <Tab title="Transfer with multiple UTXOs">
    <img src="https://mintcdn.com/helius/ITnDOhn5GRfQ1FQW/images/privacy/spend-comparison-multi.svg?fit=max&auto=format&n=ITnDOhn5GRfQ1FQW&q=85&s=c35ec9ab7c87c8d3cdb9e3d1caaf7742" alt="Spend multiple UTXOs" width="1016" height="240" data-path="images/privacy/spend-comparison-multi.svg" />
  </Tab>
</Tabs>

| | Solana token account | Private Solana token account (UTXO) |
| - | - | - |
| Balance | The `amount` in the account | The sum of all unspent UTXOs, each a fixed amount of one asset |
| Spending | Updates the `amount` field | Invalidate spent UTXOs and create a new UTXO |
| Visibility | Public onchain | Encrypted onchain |

### UTXO Selection

For private transfers, the SDK selects unspent UTXOs that cover the amount you want to transfer.

The UTXO selection algorithm spends as few UTXOs as possible per transfer:

1. The SDK filters your UTXOs by the asset being sent and sorts them by amount.
2. The SDK selects as many UTXOs as necessary to cover the transfer amount.
   It selects the largest UTXOs first until the transfer amount is covered.

For example, Alice sends Bob 80 USDC. Her three largest UTXOs are enough to cover the transfer:

<img src="https://mintcdn.com/helius/ITnDOhn5GRfQ1FQW/images/privacy/utxo-selection.svg?fit=max&auto=format&n=ITnDOhn5GRfQ1FQW&q=85&s=07ed7a4483eee5a3605ceb08291d5e02" alt="Alice sends 80 USDC. Her UTXOs are sorted largest first: 40, 25, 20, 10, and 5 USDC. The SDK selects 40, 25, and 20 USDC, which cover 80 USDC, and leaves 10 and 5 USDC unselected." width="770" height="140" data-path="images/privacy/utxo-selection.svg" />

Fewer UTXOs keep the transaction small.
Each spent UTXO adds 66 bytes for a nullifier account, which marks the UTXO as spent.
The account prevents the UTXO <Tooltip tip="The nullifier account is temporary. Asynchronously, the Forester inserts the nullifier into the nullifier Merkle tree, which records the UTXO as spent permanently. The Forester then closes the account and reclaims the rent-exemption.">from being spent again</Tooltip>.

Example private transfers without additional data, or other instructions:

| UTXOs spent | Transaction size | Accounts |
| - | - | - |
| 2 | 927 bytes | 6 |
| 5 | 1,125 bytes | 9 |
| 36 | 3,055 bytes | 40 |

### Transaction Variants

Every private transfer uses a transaction variant: a fixed number of slots for UTXOs to spend and new UTXOs to create.

Variants range from 1 to 36 spent UTXOs, and each variant has its own ZK circuit.
The circuit proves that the spent UTXOs are valid and that the new UTXOs hold the same total amount.
If a transfer needs more than 36 UTXOs, [merge them first](#merging-utxos).

The SDK picks a variant that fits the transfer and fills unused slots with dummy UTXOs.
Dummy UTXOs hold no value and look like real UTXOs onchain.

<Tabs>
  <Tab title="1 spent, 2 new">
    Alice sends Bob 30 USDC. Her largest UTXO covers the transfer.

    <img src="https://mintcdn.com/helius/ITnDOhn5GRfQ1FQW/images/privacy/utxo-variant-1-2.svg?fit=max&auto=format&n=ITnDOhn5GRfQ1FQW&q=85&s=728d31824165d27d6fd5f98704431f5e" alt="Alice's 40 USDC UTXO is spent, and her 25, 20, 10, and 5 USDC UTXOs stay unspent. The transaction creates 30 USDC for Bob and 10 USDC for Alice." width="520" height="286" data-path="images/privacy/utxo-variant-1-2.svg" />
  </Tab>

  <Tab title="2 spent, 2 new">
    Alice sends Bob 60 USDC. Her two largest UTXOs cover the transfer.

    <img src="https://mintcdn.com/helius/ITnDOhn5GRfQ1FQW/images/privacy/utxo-variant-2-2.svg?fit=max&auto=format&n=ITnDOhn5GRfQ1FQW&q=85&s=75d3804ee8a731e2b213dcbda9f3edbc" alt="Alice's 40 and 25 USDC UTXOs are spent, and her 20, 10, and 5 USDC UTXOs stay unspent. The transaction creates 60 USDC for Bob and 5 USDC for Alice." width="520" height="286" data-path="images/privacy/utxo-variant-2-2.svg" />
  </Tab>

  <Tab title="3 spent, 3 new">
    Alice sends Bob 80 USDC. Her three largest UTXOs cover the transfer. The SDK fills the unused slot with a dummy UTXO.

    <img src="https://mintcdn.com/helius/ITnDOhn5GRfQ1FQW/images/privacy/utxo-variant-3-3.svg?fit=max&auto=format&n=ITnDOhn5GRfQ1FQW&q=85&s=7cb4508367f8caaa674bd419719aec0b" alt="Alice's 40, 25, and 20 USDC UTXOs are spent, and her 10 and 5 USDC UTXOs stay unspent. The transaction creates 80 USDC for Bob, 5 USDC for Alice, and one dummy UTXO of 0 USDC in the unused slot." width="520" height="286" data-path="images/privacy/utxo-variant-3-3.svg" />
  </Tab>

  <Tab title="4 spent, 3 new">
    Alice sends Bob 95 USDC. Her four largest UTXOs cover the transfer. The SDK fills the two unused slots with dummy UTXOs.

    <img src="https://mintcdn.com/helius/ITnDOhn5GRfQ1FQW/images/privacy/utxo-variant-4-3.svg?fit=max&auto=format&n=ITnDOhn5GRfQ1FQW&q=85&s=cf9bdd94965b8861ca25247bbe078c1c" alt="Alice's 40, 25, 20, and 10 USDC UTXOs are spent, and her 5 USDC UTXO stays unspent. The transaction creates 95 USDC for Bob and two dummy UTXOs of 0 USDC in the unused slots." width="520" height="286" data-path="images/privacy/utxo-variant-4-3.svg" />
  </Tab>
</Tabs>

<Accordion title="Supported transaction variants">
  | UTXOs spent | New UTXOs |
  | - | - |
  | 1 | 1, 2, or 8 |
  | 2 | 2 or 3 |
  | 3 | 3 |
  | 4 | 3 or 4 |
  | 5 | 3 or 4 |
  | 36 | 2 |

  <Info>
    View source code: [Spec](https://github.com/helius-labs/zolana/blob/a3ddedda42b40d4951ae8fcdde2500374c4f2125/docs/spec.md#circuit-variants) · [Supported variants](https://github.com/helius-labs/zolana/blob/a3ddedda42b40d4951ae8fcdde2500374c4f2125/program-libs/interface/src/shape.rs)
  </Info>
</Accordion>

## Merging UTXOs

Receiving many transfers without spending can fragment the private balance across many UTXOs.

If a transfer needs more than 36 UTXOs, merge them first so the user can spend the entire balance in one transfer.
Most users will rarely encounter a fragmented balance since one transfer can spend up to 36 UTXOs.

* A merge combines UTXOs of the same owner and asset into one UTXO with the same total value.
* A merge cannot spend funds or change the owner.
* Merging can be done under the hood without impacting UX for end users.

<img src="https://mintcdn.com/helius/ITnDOhn5GRfQ1FQW/images/privacy/utxo-merge.svg?fit=max&auto=format&n=ITnDOhn5GRfQ1FQW&q=85&s=2fb4f8d2bad662806127ff1c4fb7687f" alt="A merge spends five of Alice's UTXOs of 1 USDC each and creates one new UTXO of 5 USDC for Alice." width="520" height="270" data-path="images/privacy/utxo-merge.svg" />

The number of merges needed to spend the whole balance in one transfer depends on how many UTXOs hold it:

| UTXOs in the balance | Merges before the transfer | Transfer |
| - | - | - |
| 1 to 36 | None | One transfer spends every UTXO |
| 37 | 1 merge of 2 UTXOs | One transfer with 36 UTXOs |
| 100 | 2 merges of 36 UTXOs, in parallel | One transfer with 30 UTXOs |
| 1,296 | 36 merges of 36 UTXOs, in parallel | One transfer with 36 UTXOs |

Each merge is one Solana transaction with a ZK proof and turns up to 36 UTXOs into one.
Because the merge outcome is deterministic, the merge proofs and the transfer proof are generated in parallel.

### Example Merge Instruction Usage

Your application can merge at two points:

* **When syncing the private balance:** on wallet unlock, private wallet open, app resume, network reconnect, stream gap, or wallet restore.
* **Before a transfer:** when the transfer needs more than 36 UTXOs.

<Info>
  Merges run without a user signature. Custom Rings set their own merge permissions.
  When using the embedded private wallet, merge is done for you under the hood.
</Info>

<Accordion title="Example for Merge">
  For example, a private wallet holds 1,296 USDC in 1,296 UTXOs after receiving 1,296 private transfers of 1 USDC.
  A transfer spends at most 36 UTXOs, so the wallet spends the balance in two stages:

  1. 36 merge transactions run in parallel and create 36 UTXOs of 36 USDC each.
  2. One 36-input transfer spends the entire 1,296 USDC balance.

  <Info>
    View example code: [sdk-tests/client/rust/optimized\_merge\_transfer.rs](https://github.com/helius-labs/zolana/blob/1be5fe5c8865badd2852617d18a8f5ca06b592f1/sdk-tests/client/rust/optimized_merge_transfer.rs) · [sdk-tests/client/typescript/optimized-merge-transfer.test.ts](https://github.com/helius-labs/zolana/blob/1be5fe5c8865badd2852617d18a8f5ca06b592f1/sdk-tests/client/typescript/optimized-merge-transfer.test.ts)
  </Info>
</Accordion>

<a id="latency-and-concurrency" />

## UTXO Concurrency

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

A private balance can be spent concurrently when it is split across several UTXOs. Each UTXO can be spent in a separate transaction at the same time. For example, a balance of three UTXOs of 100 USDC each can fund three transfers of up to 100 USDC at once. The wallet selects which UTXOs to spend.

<img src="https://mintcdn.com/helius/ITnDOhn5GRfQ1FQW/images/privacy/utxo-concurrency.svg?fit=max&auto=format&n=ITnDOhn5GRfQ1FQW&q=85&s=5888bf355ed8aef63ea7cb43c63f0bcd" alt="Alice's three UTXOs of 100 USDC each fund three transfers at the same time, one UTXO per transfer." width="420" height="200" data-path="images/privacy/utxo-concurrency.svg" />

A single UTXO can only be spent once.

For the protocol's throughput limits, see [State Merkle Tree and Forester](/docs/privacy/concepts/architecture#state-merkle-tree-and-forester).

## Learn More

<CardGroup cols={2}>
  <Card title="Overview" icon="book" href="/docs/privacy/concepts/overview">
    Rings, privacy guarantees, and transaction flow.
  </Card>

  <Card title="Architecture" icon="diagram-project" href="/docs/privacy/concepts/architecture">
    How wallets, RPC services, and Solana programs interact.
  </Card>

  <Card title="Encryption and Privacy Guarantees" icon="key" href="/docs/privacy/concepts/encryption">
    How assets are encrypted and the role of the shielded keypair.
  </Card>

  <Card title="Custom Enterprise Rings" icon="building" href="/docs/privacy/integration/enterprise">
    Learn how to configure a custom Ring.
  </Card>
</CardGroup>

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


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.