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

> Add privacy support to an existing Solana wallet for the permissionless confidential Ring. Create private wallets, receive and send private transactions.

A private wallet consists of a Solana keypair for signing, a nullifier key, and a viewing key for encryption – together these keys form the shielded keypair.
Your key management holds the shielded keypair.
The wallet code uses the Privacy Interface and SDK to build private transactions.

![Key management and the Privacy SDK connect through the Privacy Interface. The Privacy SDK calls the Privacy API. The Privacy API submits the transaction to Solana.](https://mintcdn.com/helius/nNv9k5eJvwuxAzBn/images/privacy/stack.svg?fit=max\&auto=format\&n=nNv9k5eJvwuxAzBn\&q=85\&s=0e4087582b1d03c3d29d6a21f33149bb)

The integration enables your users to:

1. Create a self-custodial private wallet
2. Receive private transfers with a Solana address
3. Deposit tokens to a private balance
4. Send privately to a Solana address
5. Withdraw to any Solana address
6. Decrypt private balances and transaction history locally

The integration includes:

1. [**Shielded Keypair**](#integrate-the-shielded-keypair) – in your key management for encryption
2. [**Privacy Interface and SDK**](#integrate-the-privacy-interface-and-sdk) – to build private transactions
3. [**User Flows**](#integrate-user-flows) – to integrate into your wallet

## Context for your AI

Add a Private Wallet to an existing Solana wallet

\---
&#x20; description: Add a Private Wallet to an existing Solana wallet
&#x20; allowed-tools: Bash, Read, Write, Edit, Glob, Grep, WebFetch, AskUserQuestion, Task, TaskCreate, TaskGet, TaskList, TaskUpdate, TaskOutput, mcp\_\_deepwiki, mcp\_\_helius-docs
&#x20; \---

&#x20; \## Add a Private Wallet to an existing Solana wallet

&#x20; Voice:
&#x20; \- Point. Then a short, self-contained explanation in a real sentence.
&#x20; \- No fill words.

&#x20; Current Page: [https://www.helius.dev/docs/privacy/integration/wallet.md](https://www.helius.dev/docs/privacy/integration/wallet.md)

&#x20; Implement in this sequence. Do not skip or rearrange:
&#x20; 1\. Shielded keypair derivation
&#x20; 2\. Privacy Interface in key management
&#x20; 3\. User flows.

&#x20; Context:
&#x20; \- Docs index: [https://www.helius.dev/docs/privacy.md](https://www.helius.dev/docs/privacy.md)
&#x20; \- Spec: [https://github.com/helius-labs/zolana/blob/main/docs/spec.md](https://github.com/helius-labs/zolana/blob/main/docs/spec.md)
&#x20; \- MCP: [https://www.helius.dev/docs/privacy/ai-tools.md](https://www.helius.dev/docs/privacy/ai-tools.md)
&#x20; \- Packages: @heliuslabs/zolana, @solana/kit · zolana-client, zolana-interface, zolana-program, zolana-keypair, zolana-transaction
&#x20; \- Examples: [https://github.com/helius-labs/zolana-examples/tree/main/typescript-client](https://github.com/helius-labs/zolana-examples/tree/main/typescript-client) · [https://github.com/helius-labs/zolana-examples/tree/main/rust-client](https://github.com/helius-labs/zolana-examples/tree/main/rust-client)

&#x20; Key APIs: ShieldedKeypairLike, ViewingKeyLike, ShieldedKeypairTrait, ViewingKeyTrait, createZolanaClient. Flow APIs: the pages linked from the docs index.

&#x20; \### 1. Index project
&#x20; \- Grep \`ShieldedKeypairLike|ViewingKeyLike|createZolanaClient|@heliuslabs/zolana|zolana-keypair|TSPP/derive\` across src/
&#x20; \- Glob \`\*\*/\*.\{ts,tsx,js,rs}\` · read package.json / Cargo.toml
&#x20; \- Identify: existing Solana key management, seed vs hardware/MPC, account index, send path, balance/history UI
&#x20; \- Task subagent if multiple packages

&#x20; \### 2. Read references
&#x20; \- WebFetch the current page — both interface languages, both derivation tabs
&#x20; \- WebFetch the docs index — follow the pages for the sequence above. Add on-/off-ramp only if selected in step 3
&#x20; \- WebFetch the spec when protocol behavior is ambiguous (registry, spends, encryption)
&#x20; \- TaskCreate one todo per phase below

&#x20; \### 3. Clarify intention
&#x20; \- AskUserQuestion: Are you using TypeScript or Rust?
&#x20; \- AskUserQuestion: Are you a seed-based wallet, creating tests, or a non-seed-based wallet?
&#x20; \- Seed-based wallet → derive from the seed phrase
&#x20; \- Tests → derive from a fixed message
&#x20; \- Non-seed-based wallet → derive from a fixed message
&#x20; \- AskUserQuestion: Do you want on-/off-ramp?
&#x20; \- AskUserQuestion: Localnet or devnet?
&#x20; \- Summarize. Wait for confirmation before planning

&#x20; \### 4. Create plan
&#x20; \- Do not edit unrelated files. Do not add dead code.
&#x20; \- Take existing code byte for byte. Do not invent APIs, files, or helpers that are not on the fetched pages or in the repo.
&#x20; \- Every claimed path, symbol, and line number comes from step 1 or 2. If it was not read, do not write it.
&#x20; \- ASCII tree of files to add or change. One line per file: path, then what that file does.
&#x20; \- Follow the sequence. If unclear, AskUserQuestion. Present the plan. Wait for approval.

&#x20; \### 5. Implement
&#x20; \- TS: Bash \`pnpm add @heliuslabs/zolana @solana/kit\` if missing
&#x20; \- Rust: add the crates from the SDK page linked from the docs index
&#x20; \- Follow the approved plan. Write/Edit. Do not replace the Solana keypair
&#x20; \- TaskUpdate each step

&#x20; \### 6. Verify
&#x20; \- Every interface method for the chosen language is implemented
&#x20; \- No secrets in logs or network payloads
&#x20; \- TS: Bash \`tsc --noEmit\` + existing tests
&#x20; \- Rust: Bash \`cargo check\` + \`cargo test\` if tests exist
&#x20; \- TaskUpdate complete

&#x20; \### Tools
&#x20; \- mcp\_\_helius-docs for docs pages
&#x20; \- mcp\_\_deepwiki\_\_ask\_question("helius-labs/zolana", "\<q>")
&#x20; \- WebFetch \`[https://www.helius.dev/docs/\<path>.md\`](https://www.helius.dev/docs/\<path>.md`)
&#x20; \- Task subagent for parallel Grep/Read/WebFetch
&#x20; \- TaskList for remaining work

&#x20; Invariants:
&#x20; \- Wallet holds the shielded keypair. Never send key material to the Privacy API.
&#x20; \- Implement every method on the chosen language's interface. Do not copy Rust-only methods (\`sign\_hash\`, \`nullifier\_key\`) onto TypeScript.
&#x20; \- Viewing-key operations are local and synchronous. A sign-only custodian is invalid.
&#x20; \- Use only APIs from the current page, the docs index, and the flow pages in the sequence. If an API is not there, stop and AskUserQuestion.
&#x20; \- Do not log mnemonic, nullifier, viewing key, or the \`TSPP/derive/v1\` signature.
&#x20; \- Language is TypeScript or Rust, not both.
&#x20; \- Assets are SOL, SPL, and Token-2022.
&#x20; \- Every Solana public key of the user can add a Private Wallet. One Solana address maps to one Shielded Address.
&#x20; \- When the recipient of a private transfer does not have a Private Wallet, the Privacy API can resolve the transfer to a private-to-public withdrawal.
&#x20; We recommend requesting a separate user signature in the UI to ensure the user does not withdraw to a public balance by accident.

> [accordion] View prompt
>
> ```text theme={"system"}
> description: Add a Private Wallet to an existing Solana wallet
> allowed-tools: Bash, Read, Write, Edit, Glob, Grep, WebFetch, AskUserQuestion, Task, TaskCreate, TaskGet, TaskList, TaskUpdate, TaskOutput, mcp__deepwiki, mcp__helius-docs
> ---
>
> ## Add a Private Wallet to an existing Solana wallet
>
> Voice:
> - Point. Then a short, self-contained explanation in a real sentence.
> - No fill words.
>
> Current Page: https://www.helius.dev/docs/privacy/integration/wallet.md
>
> Implement in this sequence. Do not skip or rearrange:
> 1. Shielded keypair derivation
> 2. Privacy Interface in key management
> 3. User flows.
>
> Context:
> - Docs index: https://www.helius.dev/docs/privacy.md
> - Spec: https://github.com/helius-labs/zolana/blob/main/docs/spec.md
> - MCP: https://www.helius.dev/docs/privacy/ai-tools.md
> - Packages: @heliuslabs/zolana, @solana/kit · zolana-client, zolana-interface, zolana-program, zolana-keypair, zolana-transaction
> - Examples: https://github.com/helius-labs/zolana-examples/tree/main/typescript-client · https://github.com/helius-labs/zolana-examples/tree/main/rust-client
>
> Key APIs: ShieldedKeypairLike, ViewingKeyLike, ShieldedKeypairTrait, ViewingKeyTrait, createZolanaClient. Flow APIs: the pages linked from the docs index.
>
> ### 1. Index project
> - Grep \`ShieldedKeypairLike|ViewingKeyLike|createZolanaClient|@heliuslabs/zolana|zolana-keypair|TSPP/derive\` across src/
> - Glob \`**/*.{ts,tsx,js,rs}\` · read package.json / Cargo.toml
> - Identify: existing Solana key management, seed vs hardware/MPC, account index, send path, balance/history UI
> - Task subagent if multiple packages
>
> ### 2. Read references
> - WebFetch the current page — both interface languages, both derivation tabs
> - WebFetch the docs index — follow the pages for the sequence above. Add on-/off-ramp only if selected in step 3
> - WebFetch the spec when protocol behavior is ambiguous (registry, spends, encryption)
> - TaskCreate one todo per phase below
>
> ### 3. Clarify intention
> - AskUserQuestion: Are you using TypeScript or Rust?
> - AskUserQuestion: Are you a seed-based wallet, creating tests, or a non-seed-based wallet?
>   - Seed-based wallet → derive from the seed phrase
>   - Tests → derive from a fixed message
>   - Non-seed-based wallet → derive from a fixed message
> - AskUserQuestion: Do you want on-/off-ramp?
> - AskUserQuestion: Localnet or devnet?
> - Summarize. Wait for confirmation before planning
>
> ### 4. Create plan
> - Do not edit unrelated files. Do not add dead code.
> - Take existing code byte for byte. Do not invent APIs, files, or helpers that are not on the fetched pages or in the repo.
> - Every claimed path, symbol, and line number comes from step 1 or 2. If it was not read, do not write it.
> - ASCII tree of files to add or change. One line per file: path, then what that file does.
> - Follow the sequence. If unclear, AskUserQuestion. Present the plan. Wait for approval.
>
> ### 5. Implement
> - TS: Bash \`pnpm add @heliuslabs/zolana @solana/kit\` if missing
> - Rust: add the crates from the SDK page linked from the docs index
> - Follow the approved plan. Write/Edit. Do not replace the Solana keypair
> - TaskUpdate each step
>
> ### 6. Verify
> - Every interface method for the chosen language is implemented
> - No secrets in logs or network payloads
> - TS: Bash \`tsc --noEmit\` + existing tests
> - Rust: Bash \`cargo check\` + \`cargo test\` if tests exist
> - TaskUpdate complete
>
> ### Tools
> - mcp__helius-docs for docs pages
> - mcp__deepwiki__ask_question("helius-labs/zolana", "<q>")
> - WebFetch \`https://www.helius.dev/docs/<path>.md\`
> - Task subagent for parallel Grep/Read/WebFetch
> - TaskList for remaining work
>
> Invariants:
> - Wallet holds the shielded keypair. Never send key material to the Privacy API.
> - Implement every method on the chosen language's interface. Do not copy Rust-only methods (\`sign_hash\`, \`nullifier_key\`) onto TypeScript.
> - Viewing-key operations are local and synchronous. A sign-only custodian is invalid.
> - Use only APIs from the current page, the docs index, and the flow pages in the sequence. If an API is not there, stop and AskUserQuestion.
> - Do not log mnemonic, nullifier, viewing key, or the \`TSPP/derive/v1\` signature.
> - Language is TypeScript or Rust, not both.
> - Assets are SOL, SPL, and Token-2022.
> - Every Solana public key of the user can add a Private Wallet. One Solana address maps to one Shielded Address.
> - When the recipient of a private transfer does not have a Private Wallet, the Privacy API can resolve 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.
> ```

**Step 1**

## Integrate the Shielded Keypair

A private wallet integration adds a shielded keypair to your key management.
A shielded keypair signs, encrypts and decrypts transactions.

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

* **Signing key:** The wallet's Ed25519 Solana keypair to sign transactions.
* **Viewing key:** An X25519 keypair, used to encrypt and decrypt transactions.
* **Nullifier key:** A key used to derive nullifiers. (A nullifier ensures a private balance cannot be spent twice. A private balance is the sum of all UTXOs (unspent transaction outputs) owned by an address. Each transaction spends (nullifies) input UTXOs and creates new outputs.)

A wallet can derive the shielded keypair from an EdDSA signature over a fixed message.

EdDSA signatures are deterministic, so the signature of the same Solana keypair for the same message always produces the same shielded keypair.
We use the signature as a seed to derive a shielded keypair.

The fixed message payload is `"TSPP/derive/v1"`, encoded as a Solana off-chain message v0.

The shielded signing key is the wallet’s Solana EdDSA key.
The nullifier and viewing keys are additional keys derived from the signature.

> [!WARNING]
>
> The signature is the derivation seed.
> Whoever holds it can derive the nullifier and viewing keys and can decrypt user transactions.
> Reject any sign request for the `"TSPP/derive/v1"` message from a third party.

Pseudocode to derive a shielded keypair from a Solana signature:

```jsx theme={"system"}
function deriveShieldedKeypair(solanaKeypair):
      solanaPubkey = solanaKeypair.publicKeyBytes() // 32 bytes
	
      payload = UTF8("TSPP/derive/v1")

      // Solana off-chain message v0 envelope
      derivationMessage =
          bytes("\xffsolana offchain")            // 16 bytes
          || u8(0)                                // version
          || SHA256(payload)                      // application domain
          || u8(0)                                // restricted ASCII format
          || u8(1)                                // one signer
          || solanaPubkey
          || u16LE(length(payload))
          || payload

      // Ed25519 signatures are deterministic, so the same wallet produces
      // the same 64-byte seed every time.
      derivationSeed = ED25519_SIGN(
          solanaKeypair.secretKey,
          derivationMessage
      )

      // HKDF-Extract with no salt
      prk = HKDF_SHA256_EXTRACT(
          salt = empty,
          ikm  = derivationSeed
      )

      nullifierSecret = HKDF_SHA256_EXPAND(
          prk,
          info   = UTF8("TSPP/nf_key/ed25519/v1"),
          length = 31
      )

      viewingSecret = HKDF_SHA256_EXPAND(
          prk,
          info   = UTF8("TSPP/view_key/ed25519/x25519/v1"),
          length = 32
      )

      return ShieldedKeypair {
          signingKey:    solanaKeypair,
          nullifierKey:  NullifierKey(nullifierSecret),
          viewingKey:    X25519Keypair(clamp(viewingSecret))
}
```

> [!NOTE]
>
> Example Implementation: [TypeScript](https://github.com/helius-labs/zolana/blob/ts-sdk-v0.5.0-alpha/sdk-libs/ts/src/keypair/derivation.ts#L241) · [Rust](https://github.com/helius-labs/zolana/blob/v0.5.0-alpha/sdk-libs/keypair/src/shielded.rs#L183)

**Step 2**

## Integrate the Privacy Interface and SDK

The Privacy Interface enables your code to use the Privacy SDK to build private transactions without accessing the key material directly.
Your key management implements the Privacy Interface in two parts.

1. Signing Interface for Transaction Building with the Privacy SDK. View Source: [TypeScript](https://github.com/helius-labs/zolana/blob/ts-sdk-v0.5.0-alpha/sdk-libs/ts/src/keypair/shielded.ts#L167)/ [Rust](https://github.com/helius-labs/zolana/blob/v0.5.0-alpha/sdk-libs/keypair/src/traits/shielded_keypair.rs)
2. Encryption & Decryption Interface. View Source: [TypeScript](https://github.com/helius-labs/zolana/blob/ts-sdk-v0.5.0-alpha/sdk-libs/ts/src/keypair/shielded.ts#L191)/ [Rust](https://github.com/helius-labs/zolana/blob/v0.5.0-alpha/sdk-libs/keypair/src/traits/view_key.rs)

> [!NOTE]
>
> The Privacy Interface is stateless and therefore does not store balances or transaction history.
> For performance, we recommend caching user balances and transaction history locally.

### On Proof Generation

Zero-knowledge proof generation requires plaintext inputs of the complete user transaction data and nullifier keys.

Currently, server proof generation is available through the standard Helius RPC URL.

Local proof generation is coming soon.
The SDKs' default prover is an attested AWS Nitro enclave.

**Step 3**

## Integrate User Flows

### Create Private Wallet

A private wallet is created in two steps:

1. Derive the shielded keypair
2. Register the Solana address in an on-chain registry.

Creating a private wallet does not require a deposit to the private balance.

> [!NOTE]
>
> 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.

The registry links the signing public key to the nullifier and viewing public keys. Together these public keys form the shielded address.

```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 SDK
    participant Solana

    User->>Privacy SDK: Create Private Wallet
    Privacy SDK->>Solana: Register Solana address

```

> [!NOTE]
>
> Each registry record is a PDA owned by the [registry program](https://orbmarkets.io/address/regyS5rkAcw2YzDJCmTwCTHs2s246FXxbmuRZ42u2PD) (regyS5rkAcw2YzDJCmTwCTHs2s246FXxbmuRZ42u2PD), which anyone can look up to see whether a Solana address can receive private transfers.

> [tab] TypeScript Client
>
> ```typescript theme={"system"}
> import { MergeEncryptionScheme } from "@heliuslabs/zolana";
> import {
>   getRegisterInstructionAsync,
>   getSetMergeEncryptionInstructionAsync,
> } from "@heliuslabs/zolana/instructions";
> import { isWalletRegistered } from "@heliuslabs/zolana/wallet";
>
> await sendTransaction([
>   await getRegisterInstructionAsync({
>     owner: signer,
>     nullifierPublicKey: address.nullifierPublicKey,
>     viewingPublicKey: address.viewingPublicKey.toBytes(),
>   }),
>   await getSetMergeEncryptionInstructionAsync({
>     owner: signer,
>     scheme: MergeEncryptionScheme.PoseidonCtrV1,
>   }),
> ]);
>
> const registered = await isWalletRegistered({
>   rpc: client,
>   owner: signer.address,
> });
> ```
>
> * `owner` is the user's Solana signer, used to derive the registry PDA.
> * `address` is the user's Shielded Address.
> * `getRegisterInstructionAsync` registers the Shielded Address's keys.
> * `getSetMergeEncryptionInstructionAsync` turns on merging for the user's UTXOs. A transfer can spend at most 40 UTXOs, so merging ensures users can always spend their entire balance in one transfer. Learn more about [merging UTXOs](https://www.helius.dev/docs/privacy/concepts/utxo.md#merging-utxos).
> * `sendTransaction` signs and sends the transaction.
> * `isWalletRegistered` reads the registry PDA to check registration.

> [tab] Rust Client
>
> ```rust theme={"system"}
> use solana_signer::Signer;
> use zolana_client::user_registry::is_wallet_registered_sync;
> use zolana_client::Rpc;
> use zolana_user_registry_interface::{
>     instruction::{register, set_merge_encryption, RegisterData},
>     user_record_pda, MergeEncryptionScheme,
> };
>
> let address = owner.shielded_address()?;
> let user_record = user_record_pda(&owner.pubkey()).0;
> client.create_and_send_transaction(
>     &[
>         register(
>             user_record,
>             owner.pubkey(),
>             owner.pubkey(),
>             RegisterData {
>                 nullifier_pubkey: address.nullifier_pubkey,
>                 viewing_pubkey: *address.viewing_pubkey.as_bytes(),
>             },
>         ),
>         set_merge_encryption(
>             user_record,
>             owner.pubkey(),
>             MergeEncryptionScheme::PoseidonCtrV1,
>         ),
>     ],
>     owner.pubkey(),
>     &[&owner],
>     client.compute_budget(),
> )?;
>
> assert!(is_wallet_registered_sync(&client, owner.pubkey())?);
> ```
>
> * `owner` is the user's `ShieldedKeypair`. Its Solana pubkey identifies the registry record, and its signature authorizes registration.
> * `owner.shielded_address()` returns the user's Shielded Address.
> * `register` registers the Shielded Address's keys.
> * `set_merge_encryption` turns on merging for the user's UTXOs. A transfer can spend at most 40 UTXOs, so merging ensures users can always spend their entire balance in one transfer. Learn more about [merging UTXOs](https://www.helius.dev/docs/privacy/concepts/utxo.md#merging-utxos).
> * `create_and_send_transaction` signs, sends and confirms the transaction.
> * `is_wallet_registered_sync` reads the registry PDA to check registration.

> [!NOTE]
>
> Full registration examples: [TypeScript](https://github.com/helius-labs/zolana-examples/blob/main/typescript-client/examples/register_private_wallet.ts) · [Rust](https://github.com/helius-labs/zolana-examples/blob/main/rust-client/examples/register_private_wallet.rs)

### Sync Private Balance & Transaction History

Balance sync fetches encrypted state from the RPC and decrypts it in the wallet.

The RPC does not decrypt. Only the owner can decrypt a private balance with a viewing key.

> [!NOTE]
>
> A wallet should decrypt and update balances before private transactions and wherever it runs backfills: on wallet unlock, private wallet open, app resume, network reconnect, stream gap, or wallet restore.

> [card] [Read a Private Balance](https://www.helius.dev/docs/privacy/guides/read-balance.md)

> [card] [Read Private Transaction History](https://www.helius.dev/docs/privacy/guides/read-history.md)

### Deposit to a Private Balance

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 SPL account to a private balance

On-ramp and deposit differ in their privacy guarantees.
We recommend using on-ramp over deposit for better privacy.

| Field | On-ramp from Fiat | Deposit from SPL |
| - | - | - |
| Asset | Private | Public |
| Amount | Private | Public |
| Source | Not onchain | Public |
| Destination address | Public | Public |

> [card] [On-/Off-Ramp](https://www.helius.dev/docs/privacy/integration/on-off-ramp.md)
>
> On-/Off-ramp fiat directly to and from a private balance.

> [card] [Deposit](https://www.helius.dev/docs/privacy/guides/deposit.md)
>
> Deposit from a public crypto balance.

### Private Transfers

Users can send private transfers to any Solana wallet address.

For every private transfer, the Privacy SDK looks up the wallet address in the onchain registry to check if the recipient has a private wallet.

The Privacy SDK can resolve a private transfer to a private-to-public withdrawal, when the recipient does not have a Private Wallet.

> [!NOTE]
>
> We recommend labeling the transaction in the UI as a withdrawal to ensure the user does not withdraw to a public balance by accident.
>
> We recommend requesting a separate user signature in the UI to ensure the user does not withdraw to a public balance by accident.

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

> [card] [Private Transfer](https://www.helius.dev/docs/privacy/guides/transfer.md)
>
> Transfer privately to a private balance.

### Withdraw to a Public Balance

Withdrawals can be used in two ways:

1. As fallback for private transfers to recipients that don't have a private wallet, or
2. Direct withdrawal to a public SPL token account.

For withdrawals, we recommend clearly labeling that the user is leaving the private balance, similar to a seed phrase or private key export.

| Field | Visibility | Why |
| - | - | - |
| Asset | Public | The asset is visible onchain |
| Amount | Public | The withdrawn amount is visible onchain |
| Source private wallet | Public | In a confidential Ring the source private wallet is visible onchain. |
| Destination public wallet | Public | The destination wallet address is visible onchain |
| Destination public balance | Public | The resulting public balance is visible onchain |
| Remaining private balance | Private | The remaining balance is encrypted onchain |

> [card] [Withdraw](https://www.helius.dev/docs/privacy/guides/withdraw.md)
>
> Withdraw to a public crypto balance.

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

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


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