# Encrypted Balances
Source: https://sdk.umbraprivacy.com/concepts/encrypted-balances
Encrypted Token Accounts (ETAs): MXE-only mode (Arcium network decryptable) vs Shared mode (user X25519 decryptable). Covers nonces, ciphertext structure, and encryption upgrades.
## Public vs. Encrypted Token Accounts
Solana uses **Associated Token Accounts (ATAs)** to hold SPL and Token-2022 tokens. These are fully public - anyone can query your balance.
Umbra introduces **Encrypted Token Accounts (ETAs)** - on-chain accounts that hold your token balance in encrypted form.
**ATA (standard public account)**
* Balance visible on-chain
* Supports standard SPL transfers
* No registration required
* Works with any SPL token
**ETA (Umbra encrypted account)**
* Balance hidden on-chain
* Use Umbra deposit/withdraw instead of standard transfers
* One-time registration required
* Works with any SPL token
## Depositing: ATA → ETA
When you call `deposit`, your tokens move from your ATA into the shielded pool. The program records an encrypted version of your balance in an ETA tied to your `(wallet address, mint)` pair.
```
Your ATA ──deposit──► Shielded Pool (on-chain SPL)
│
▼
Your ETA (encrypted balance stored here)
```
The shielded pool holds the real tokens. The ETA holds the cryptographic proof of how much of those tokens belong to you.
## Withdrawing: ETA → ATA
When you withdraw, the process reverses. Arcium MPC verifies that your encrypted balance is sufficient and authorizes the transfer from the shielded pool back to your ATA.
## The Two Encryption Modes
### MXE-Only
In MXE-only mode, your balance is encrypted under the **Arcium MXE (Multi-party Exchange) public key**. Only the Arcium network can decrypt it.
* Withdrawals require Arcium to perform the decryption computation
* You cannot query your own balance client-side without Arcium
* This is the default mode for users who have not registered an [X25519](https://www.rfc-editor.org/rfc/rfc7748) key
### Shared Mode
In Shared mode, your balance is encrypted under **two keys simultaneously**: the Arcium MXE key and your personal **X25519 public key**.
* You can decrypt and read your own balance locally, without a network call
* Withdrawals still use Arcium's MPC for the on-chain operation
* Available after completing X25519 key registration (part of the standard registration flow)
If you register with `confidential: true` (the default), your deposits will automatically use Shared mode. This is strongly recommended - it lets you call `queryEncryptedBalance` to read your balance without waiting for Arcium.
## Account Lifecycle
An ETA is created on first deposit and exists for the lifetime of the wallet/mint pair. Subsequent deposits update the encrypted balance in place.
```typescript theme={null}
// First deposit for a given mint → creates the ETA
await deposit(signer.address, USDC_MINT, 1_000_000n);
// Second deposit → adds to the existing ETA balance
await deposit(signer.address, USDC_MINT, 500_000n);
```
## Nonces and Replay Protection
Each encrypted token account has a **nonce** - a monotonically increasing counter used to prevent replay attacks. The nonce is derived from the account's `generationIndex` field combined with entropy stored on-chain. You don't manage nonces directly; the SDK handles them.
## Viewing Your Balance
If you are registered in Shared mode, you can query your current encrypted balance:
```typescript theme={null}
import { getEncryptedBalanceQuerierFunction } from "@umbra-privacy/sdk";
const query = getEncryptedBalanceQuerierFunction({ client });
const balances = await query([USDC_MINT]);
const result = balances.get(USDC_MINT);
switch (result?.state) {
case "shared":
console.log("Balance:", result.balance); // decrypted MathU64
break;
case "mxe":
console.log("Account is in MXE mode, cannot decrypt balance client-side");
break;
case "uninitialized":
console.log("Account exists but balance not initialized");
break;
case "non_existent":
console.log("No encrypted token account for this mint");
break;
}
```
Only Shared-mode accounts can be decrypted client-side. MXE-mode accounts return `{ state: "mxe" }` without a balance. Convert to Shared mode to enable local balance queries.
## Protocol Fees
Deposits subtract a small protocol fee from your balance. The fee has two components:
* **Base fee** - a fixed amount in the token's native units
* **Commission** - a percentage of the deposit amount
Fees are deducted automatically. The amount credited to your ETA is the deposit amount minus fees. For Token-2022 mints with a transfer fee extension, the transfer fee is also subtracted before protocol fees are applied - see [Token-2022 Support](/advanced/token-2022).
# How Umbra Works
Source: https://sdk.umbraprivacy.com/concepts/how-umbra-works
How Umbra works: encrypted balances via Arcium MPC dual-instruction pattern (queue_computation + arcium_callback), UTXO mixer with Indexed Merkle Tree, and compliance grants.
## The Problem with Public Blockchains
Every transaction on Solana is publicly readable. Anyone can look up your wallet address and see exactly how much of any token you hold and every transfer you've ever made. For many use cases - payroll, business payments, personal finance - this is unacceptable.
Umbra fixes this without requiring you to move your tokens off Solana or trust a centralized custodian.
## The Two Privacy Modes
Umbra provides two distinct privacy tools that can be used independently or together:
Your token balance is stored on-chain, but the amount is encrypted. Transfers still happen on-chain - what's hidden is *how much*.
Tokens are deposited into a shared pool and withdrawn separately, with no on-chain link between the deposit and withdrawal addresses. *Who transferred to whom* is hidden.
You can use encrypted balances alone (hide amounts), the mixer alone (hide the transfer path), or both together for the strongest privacy guarantees.
## How Encrypted Balances Work
When you deposit tokens into an encrypted balance:
1. Your tokens move from your public SPL or Token-2022 account to the **shielded pool** - still on-chain, still real tokens, locked in the program
2. The program creates an **Encrypted Token Account (ETA)** for your `(wallet, mint)` pair
3. Your balance is encrypted and stored in that ETA using **Arcium MPC**
The encryption means no one - not Umbra, not Arcium, not Solana validators - can read your balance without the decryption key.
### What is Arcium MPC?
Arcium is a network of nodes that perform computation on encrypted data using **[Multi-Party Computation (MPC)](https://dl.acm.org/doi/10.1145/62212.62213)**. You don't need to understand the details, but the key property is:
> No single node can decrypt your data. The computation is split across multiple independent parties so that learning your balance requires compromising a majority of them simultaneously.
From a developer's perspective: Arcium is the computation backend. The SDK handles all communication with it automatically.
### Encryption Modes
When your balance is encrypted, it can be in one of two modes:
* **MXE-only** - Only the Arcium MPC network can decrypt it. Withdrawals require MPC to perform the decryption. You cannot decrypt it locally.
* **Shared** - Encrypted under both the Arcium MPC key *and* your personal [X25519](https://www.rfc-editor.org/rfc/rfc7748) key. You can decrypt your own balance client-side without MPC. Available after you register your X25519 key.
After [registering](/sdk/registration) with `confidential: true`, your deposits automatically use Shared mode so you can query your balance locally.
## How the Mixer Works
The mixer uses a cryptographic structure called an **[Indexed Merkle Tree](https://link.springer.com/chapter/10.1007/3-540-48184-2_32)** combined with **[zero-knowledge proofs](https://dl.acm.org/doi/10.1145/22145.22178)**.
Here's the simplified version:
You create a **commitment** - a cryptographic hash of `(amount, recipient, secret)` - and insert it as a leaf into the Merkle tree. The commitment is public; the contents are not.
More users deposit into the same tree. Your commitment is now one of many thousands of leaves. The larger the set, the stronger the anonymity.
You present a **zero-knowledge proof** that you know the secret behind one of the leaves in the tree without revealing *which* leaf. The proof is verified on-chain, the nullifier is burned to prevent double-spending, and the tokens are released.
The deposit address and the claim address need not be related. An observer sees a deposit and a withdrawal happening, but cannot link them.
## The Dual-Instruction Pattern
Every confidential operation in Umbra involves two on-chain transactions:
1. **Handler** - your wallet signs a transaction that validates inputs and queues a computation request with Arcium
2. **Callback** - Arcium completes the off-chain computation and posts the result back on-chain, triggering the program's callback instruction to update state
The SDK waits for both transactions to confirm before returning. You don't need to manage this yourself.
This is why Umbra operations take a few seconds longer than a standard token transfer - there is an off-chain MPC round-trip between the two on-chain instructions.
## Privacy Guarantees Summary
* **Token balance amount** - hidden by Encrypted Balances. Encrypted with MPC; auditors can be granted selective access.
* **Transfer counterparty** - hidden by the Mixer. Requires a sufficient anonymity set (other users in the same tree).
* **Transaction history** - strongest guarantees when mixer + encrypted balances are used together.
## What is NOT Hidden
* **That you are using Umbra** - on-chain interactions with the Umbra program are visible
* **Deposit and withdrawal amounts** when using the mixer - the amount is committed at deposit and revealed at claim
* **Timing** - if you deposit and immediately withdraw, temporal analysis can correlate them
## Compliance
Umbra includes a built-in compliance system. Users can create **viewing grants** that allow specific parties (auditors, regulators) to decrypt their balances. See the [Compliance](/sdk/compliance) guide for details.
# UTXOs and the Mixer
Source: https://sdk.umbraprivacy.com/concepts/utxos-and-mixer
UTXO mixer internals: Indexed Merkle Tree for commitments, nullifier treap for double-spend prevention, X25519 ciphertext discovery, and Groth16 ZK proof verification on claim.
## What is a UTXO?
A **[UTXO](https://bitcoin.org/bitcoin.pdf) (Unspent Transaction Output)** in Umbra is a cryptographic commitment to a token deposit. It represents the right to claim a specific amount of tokens - without publicly linking that right to who created it.
A UTXO encodes:
* **Amount** - how many tokens are locked
* **Recipient address** - who is authorized to claim
* **Secret randomness** - private entropy known only to the depositor
Only the **[Poseidon](https://eprint.iacr.org/2019/458.pdf) hash** of these values (the *commitment*) is stored on-chain. The inputs remain private.
## The Mixer: How It Works
The mixer is a shared **[Indexed Merkle Tree](https://link.springer.com/chapter/10.1007/3-540-48184-2_32)** stored on-chain. Each leaf in the tree is a UTXO commitment.
You call one of the `createUtxo` functions. The SDK computes a commitment from `(amount, recipient, randomness)` and inserts it as a new leaf into the tree. Your tokens are locked in the shielded pool.
At this point, anyone can see that *a deposit happened* and *the tree grew by one leaf* - but cannot see the amount, recipient, or any other detail.
As more users deposit into the same tree, your commitment becomes one of many. The larger the set, the harder it is to link your deposit to your eventual withdrawal. Trees hold up to **1,048,576 leaves** (depth-20 tree).
The SDK queries the indexer to find UTXO ciphertexts addressed to your [X25519](https://www.rfc-editor.org/rfc/rfc7748) key, decrypts them locally, and fetches the Merkle inclusion proof for each one. See [Fetching UTXOs](/sdk/mixer/fetching-utxos).
You present a **[zero-knowledge proof](https://dl.acm.org/doi/10.1145/22145.22178)** that proves:
* You know the secret inputs behind a commitment that exists in the tree
* You haven't claimed it before (nullifier is unspent)
Without revealing *which* commitment it is. The on-chain program verifies the proof, burns the nullifier, and releases the tokens to your wallet.
## UTXO Types
Umbra supports four kinds of UTXOs depending on who can claim them and where the funds come from:
* **Self-claimable (ephemeral)** - funded from your encrypted balance or public ATA. Claimable only by you (same wallet).
* **Receiver-claimable** - funded from another user's ATA. Claimable by a specified recipient address.
The "self-claimable" pattern is useful when you want to move funds through the mixer yourself. The "receiver-claimable" pattern lets you *send* tokens anonymously - you deposit for a recipient, and they claim without you having direct access.
## Nullifiers: Preventing Double-Spends
Each UTXO has a corresponding **[nullifier](https://eprint.iacr.org/2014/349.pdf)** - a deterministic hash derived from its private inputs. When a UTXO is claimed, its nullifier is stored in an on-chain **[treap](https://dl.acm.org/doi/10.1145/324133.324247)** (a self-balancing sorted tree).
Before allowing a claim, the on-chain program checks that:
* The nullifier has not been seen before
* The ZK proof is valid for a commitment in the current Merkle tree
This prevents any UTXO from being claimed twice, even if the claim transaction is replayed.
## Ciphertext Discovery
After you create a UTXO, the SDK publishes an **encrypted ciphertext** on-chain. This ciphertext is addressed to the recipient's X25519 public key - only the recipient can decrypt it to learn the commitment's secret inputs (amount, randomness, etc.).
The ciphertext payload contains:
* Amount (8 bytes)
* Recipient address (32 bytes)
* Generation index (16 bytes)
* Domain separator identifying the UTXO type (12 bytes)
The Umbra indexer stores all ciphertexts and serves them for efficient querying. Your X25519 private key is used locally to try decrypting each one - successful decryptions are your claimable UTXOs.
Your private key never leaves your device. Decryption happens in the SDK using your locally derived X25519 key.
## Anonymity Set Size
The privacy guarantee of the mixer depends on how many other UTXOs exist in the same tree at the time you claim. A tree with only one leaf offers no privacy - it's obvious which commitment is being claimed.
In practice:
* Wait for more users to deposit before claiming
* Claiming into a different address from your deposit address increases privacy
* Combining mixer withdrawals with encrypted balances hides the final destination further
## Trees Fill Up
Each Merkle tree has a maximum of 1,048,576 leaves. When a tree is full, the write service starts a new tree at the next sequential index. UTXOs from different trees have separate anonymity sets.
You specify the tree index when [fetching](/sdk/mixer/fetching-utxos) and [claiming](/sdk/mixer/claiming-utxos) UTXOs.
# Batch Merkle Proofs
Source: https://sdk.umbraprivacy.com/indexer/api-reference/batch-proofs
Retrieve multiple Merkle inclusion proofs atomically under the same tree root.
## Endpoint
`POST /v1/trees/{tree_index}/proofs`
Returns multiple Merkle inclusion proofs for the specified insertion indices, all guaranteed to be computed against the same tree root. This is critical for claim operations that batch multiple UTXOs into a single ZK proof.
## Path Parameters
* `tree_index` (u64) -- Zero-based index of the Merkle tree.
## Request Body
JSON object with a single field:
* `insertion_indices` (array of u64) -- Leaf positions to generate proofs for. Maximum 8 indices per request.
**Example:**
```json theme={null}
{
"insertion_indices": [42, 108, 255]
}
```
## Response
Protobuf `BatchProofResponse` containing an array of proof objects. Each proof has the same structure as the single proof endpoint:
* `root` (string) -- 64-character little-endian hex-encoded Poseidon root hash. Identical across all proofs in the batch.
* `tree_index` (int64) -- Echo of the path parameter.
* `insertion_index` (int64) -- The leaf position this proof corresponds to.
* `proof` (array of 20 strings) -- Sibling hashes from leaf to root, each 64-character hex.
* `leaf` (string) -- 64-character hex-encoded final commitment.
## Why Batch?
Single proof requests (`GET /v1/trees/{tree_index}/proof/{insertion_index}`) do not guarantee a consistent root across multiple calls -- the tree may grow between requests. The batch endpoint acquires a read lock on the tree, ensuring all returned proofs share the same root. This is required for Groth16 ZK proofs that reference multiple UTXO commitments.
The SDK's claim functions use this endpoint internally when claiming multiple UTXOs in a single batch.
# Health
Source: https://sdk.umbraprivacy.com/indexer/api-reference/health/basic
GET /health
Basic health check for the indexer service.
# Health (Detailed)
Source: https://sdk.umbraprivacy.com/indexer/api-reference/health/detailed
GET /health/detailed
Detailed health status including database and dependency checks.
# Liveness
Source: https://sdk.umbraprivacy.com/indexer/api-reference/health/liveness
GET /health/liveness
Kubernetes-style liveness probe. Returns 200 if the process is alive.
# Readiness
Source: https://sdk.umbraprivacy.com/indexer/api-reference/health/readiness
GET /health/readiness
Kubernetes-style readiness probe. Returns 200 only when the service is ready to serve traffic.
# Merkle Proof
Source: https://sdk.umbraprivacy.com/indexer/api-reference/merkle-proofs
GET /v1/trees/{tree_index}/proof/{insertion_index}
Generate a Merkle inclusion proof for a specific leaf in a Merkle tree.
Returns an authentication path of **20 sibling hashes** -- one per level -- ordered from the leaf level (index 0) up to the root level (index 19). All hash values are little-endian 64-character hex strings (32 bytes each).
Merkle proofs become **stale** when new leaves are inserted into the tree (because the root changes). Always fetch a fresh proof immediately before submitting a claim. Never cache proofs across user sessions.
## Verifying a Proof
Hash the target leaf with each sibling in order and compare the final result to the tree's current root hash (fetch the root via [Tree Metadata](/indexer/api-reference/tree-metadata)).
## Usage in the SDK
The Umbra SDK calls this endpoint automatically via `getClaimableUtxoScannerFunction`. The proof is passed directly to the claim functions -- you do not need to call this endpoint manually when using the SDK.
If you are building a custom claim flow or integrating with the protocol directly, use this endpoint to fetch fresh proofs immediately before submitting a claim transaction.
## Performance
Proof generation traverses all 20 tree levels. Under normal conditions, proofs are generated in under 100 ms. Operations exceeding 500 ms emit a slow-operation warning server-side but still return a valid proof.
# Stats
Source: https://sdk.umbraprivacy.com/indexer/api-reference/stats
GET /v1/stats
Aggregate statistics for the entire UTXO index across all Merkle trees.
Use this endpoint to get a quick snapshot before deciding which tree and index range to scan.
Response encoding is always `application/x-protobuf` -- content negotiation is not supported for this endpoint.
# Tree Metadata
Source: https://sdk.umbraprivacy.com/indexer/api-reference/tree-metadata
GET /v1/trees/{tree_index}
Returns metadata for a specific Indexed Merkle Tree: leaf count, current root hash, and UTXO record count.
The root hash changes every time a new leaf is inserted. Use this endpoint to check the current state of a tree before fetching UTXOs or generating Merkle proofs.
`utxo_count` may differ from `num_leaves` if some leaves were inserted without UTXO ciphertext data (e.g. padding leaves).
# Tree UTXOs
Source: https://sdk.umbraprivacy.com/indexer/api-reference/tree-utxos
GET /v1/trees/{tree_index}/utxos
Returns a paginated list of UTXO records for a specific Merkle tree, ordered by insertion index ascending.
Use this endpoint when you know which tree you want to scan. For global scans across all trees, use [Global UTXOs](/indexer/api-reference/utxos).
## Response Layouts
The `X-Response-Layout` header controls how records are packed in the protobuf response:
* **Row-oriented** (default, omit header): Each UTXO is a self-contained `UtxoDataItem`. Easy to iterate record by record.
* **Columnar** (set header to `"columnar"`): All fields across UTXOs are packed into parallel arrays. Significantly smaller on the wire -- preferred for data pipelines and bulk processing.
The Umbra SDK uses row-oriented layout internally. Use columnar layout only if you are building a custom bulk processing pipeline.
## Pagination
Initialize with `cursor = tree_index x 1,048,576`. Each response includes a `next_cursor` field. Repeat requests using `cursor = next_cursor` until the response contains fewer records than `limit`.
```bash theme={null}
# First page of tree 0
curl "https://utxo-indexer.api.umbraprivacy.com/v1/trees/0/utxos?cursor=0&limit=1000"
# Next page (using next_cursor from previous response)
curl "https://utxo-indexer.api.umbraprivacy.com/v1/trees/0/utxos?cursor=1000&limit=1000"
```
# Single UTXO
Source: https://sdk.umbraprivacy.com/indexer/api-reference/utxo-single
GET /v1/utxos/{absolute_index}
Returns the single UTXO record at the specified global absolute index.
The absolute index uniquely identifies a UTXO across all trees:
```
absolute_index = tree_index x 1,048,576 + insertion_index
```
For example, the 5th leaf of tree 2 has absolute index `2 x 1,048,576 + 4 = 2,097,156`.
Response is a `SingleUtxoResponse` wrapper containing one `UtxoDataItem`. The `X-Response-Layout` header has no effect on this endpoint.
# Global UTXOs
Source: https://sdk.umbraprivacy.com/indexer/api-reference/utxos
GET /v1/utxos
Returns a paginated list of UTXO records across all Merkle trees, ordered by absolute index ascending.
Use this endpoint when you need to scan UTXOs globally without knowing which tree they belong to. For tree-specific queries, prefer [Tree UTXOs](/indexer/api-reference/tree-utxos).
## Pagination
Use the `next_cursor` from each response as the `start` parameter for the next request. Repeat until the response contains fewer records than `limit`.
```bash theme={null}
# First page
curl "https://utxo-indexer.api.umbraprivacy.com/v1/utxos?start=0&limit=1000"
# Next page
curl "https://utxo-indexer.api.umbraprivacy.com/v1/utxos?start=1000&limit=1000"
# Fetch a specific range (e.g. all UTXOs in tree 1: absolute indices 1,048,576 to 2,097,151)
curl "https://utxo-indexer.api.umbraprivacy.com/v1/utxos?start=1048576&end=2097151&limit=5000"
```
## Response Layouts
Set `X-Response-Layout: columnar` for columnar layout (`UtxoColumnarResponse`). Omit for row-oriented layout (`UtxoResponse`).
# Indexer Overview
Source: https://sdk.umbraprivacy.com/indexer/overview
UTXO indexer REST API at utxo-indexer.api.umbraprivacy.com. Protobuf responses, paginated UTXOs, Merkle proofs, batch proofs. Used by getClaimableUtxoScannerFunction.
## What is the Indexer?
The Umbra indexer is an off-chain service that continuously watches the Solana chain for Umbra program transactions, extracts UTXO commitments and encrypted ciphertexts, and stores them in a queryable database. It acts as the data layer that bridges on-chain Merkle tree state with the SDK and any other client that needs to interact with the mixer.
It exposes two primary capabilities:
* **UTXO data** - fetch encrypted UTXO ciphertexts by tree or absolute index. The SDK uses this to scan for UTXOs addressed to your [X25519](https://www.rfc-editor.org/rfc/rfc7748) key by trying to decrypt each ciphertext.
* **Merkle proofs** - generate a Merkle inclusion proof (authentication path) for any leaf. This is required to construct a valid claim transaction on-chain.
Beyond the SDK, the indexer is useful for analytics, monitoring, and custom integrations such as building your own wallet or scanning pipeline.
## Base URLs
* **Mainnet** - `https://utxo-indexer.api.umbraprivacy.com`
* **Devnet** - `https://utxo-indexer.api-devnet.umbraprivacy.com`
## Response Format
All endpoints return **[Protobuf](https://protobuf.dev/)** (`application/x-protobuf`) regardless of the `Accept` header. Protobuf is used by design - binary data like 32-byte hash arrays and ciphertext payloads compress significantly better than JSON.
The health endpoints are the exception: they support both JSON (default) and Protobuf via `Accept` header negotiation.
## Rate Limiting
All endpoints are subject to rate limiting. Exceeded limits return `429 Too Many Requests`. Contact the Umbra team if you need higher rate limit allowances for production workloads.
## Tree Structure
UTXO commitments are organized into **Indexed Merkle Trees**, each holding up to **1,048,576 leaves** (depth-20). When a tree is full, the indexer begins a new tree at the next sequential index.
* Tree 0 holds leaves at absolute indices `0` to `1,048,575`
* Tree 1 holds leaves at absolute indices `1,048,576` to `2,097,151`
* And so on
The absolute index of any leaf is:
```
absolute_index = tree_index x 1_048_576 + insertion_index
```
## SDK Integration
When you pass `indexerApiEndpoint` to `getUmbraClient`, the SDK automatically constructs two internal providers:
* `client.fetchUtxoData` - calls `GET /v1/utxos` to retrieve ciphertext batches for decryption
* `client.fetchMerkleProof` - calls `GET /v1/trees/{tree_index}/proof/{insertion_index}` for each claimable UTXO
Both are consumed internally by `getClaimableUtxoScannerFunction`. You do not need to call the indexer directly when using the SDK.
## Planned Improvements
The SDK currently calls the indexer directly using the `indexerApiEndpoint` you provide. We are planning to migrate this to an **IP Obfuscation Service** that will proxy indexer requests through an anonymizing relay, so that fetching your UTXOs does not leak your IP address to the indexer. This will be a transparent upgrade -- the SDK interface will not change.
## API Reference
Aggregate statistics for the entire UTXO index.
Basic, detailed, liveness, and readiness health checks.
Current root hash, leaf count, and UTXO count for a specific tree.
Paginated UTXO records for a specific tree.
Generate an inclusion proof for a specific leaf.
Retrieve multiple proofs atomically under the same tree root.
Paginated UTXO queries spanning all trees.
Fetch a single UTXO by absolute index.
# Introduction
Source: https://sdk.umbraprivacy.com/introduction
Umbra privacy protocol for Solana: shield SPL/Token-2022 balances via Arcium MPC encrypted accounts, transfer anonymously through a UTXO mixer, and grant selective compliance access.
## What is Umbra?
Umbra adds a privacy layer on top of standard Solana [SPL and Token-2022 tokens](https://spl.solana.com/token). It lets you:
* **Shield balances** - move tokens from a public SPL or Token-2022 account into an encrypted account where the balance is hidden from everyone except authorized viewers
* **Transfer anonymously** - move tokens in and out of a shared mixer pool with no on-chain linkage revealed between the entry and exit points
* **Control who sees what** - grant selective viewing access to auditors or compliance systems without exposing your full history
Everything runs on Solana mainnet using a combination of on-chain programs and [Arcium](https://arcium.com) multi-party computation (MPC) for the confidential arithmetic.
You do **not** need to understand MPC or zero-knowledge proofs to use Umbra. The SDK handles all cryptographic operations for you.
## Core Concepts at a Glance
Shield any SPL or Token-2022 token balance in a confidential account. Only you - and anyone you explicitly grant access to - can see the amount.
Break the on-chain link between a deposit and a withdrawal using a shared Merkle tree and zero-knowledge proofs.
A TypeScript SDK that wraps all protocol operations into simple async functions. Works in Node.js and browser environments.
A read-only REST API for querying UTXO records and Merkle proofs - used internally by the SDK and available for direct integration.
## How It Fits Into Your Stack
Umbra is designed to slot into an existing Solana application. You bring your wallet adapter; Umbra handles the rest.
```
Your App
└── Wallet (Phantom, Solflare, etc.)
└── Umbra SDK (@umbra-privacy/sdk)
├── Solana RPC - sends and confirms transactions
├── Arcium MPC - performs confidential computation off-chain
└── Umbra Indexer - indexes UTXOs and generates Merkle proofs
```
## Program IDs
The Umbra on-chain program is deployed at different addresses per network:
* **Mainnet:** `UMBRAD2ishebJTcgCLkTkNUx1v3GyoAgpTRPeWoLykh`
* **Devnet:** `DSuKkyqGVGgo4QtPABfxKJKygUDACbUhirnuv63mEpAJ`
The SDK resolves the correct program address automatically based on the `network` parameter.
## Supported Networks
* `mainnet` - Production
* `devnet` - Development and integration testing
* `localnet` - Local validator for unit tests
## Next Steps
Install the SDK and run your first deposit in under 5 minutes.
Understand the privacy model before you start building.
# Pricing
Source: https://sdk.umbraprivacy.com/pricing
Fee structure: protocol fees (BPS_DIVISOR=16384), relayer fees on claims, mixer SOL fee for UTXO creation. Covers fee calculation formulas and Token-2022 transfer fee interaction.
## Overview
Umbra charges three distinct fees depending on the operation:
* **Protocol fee** — a percentage of the SPL token amount, collected by the protocol on most operations.
* **Relayer fee** — a percentage of the SPL token amount, collected by the relayer on claim operations. Currently 0.
* **Mixer SOL fee** — a fixed SOL amount paid at UTXO creation time to pre-fund the eventual claim.
***
## Protocol Fee
The protocol fee is deducted from the SPL or Token-2022 token amount involved in each operation.
**Current rates:**
* Fixed base: 0 token micro-units
* Proportional: 35 bps
**Formula:**
Umbra uses a power-of-two BPS divisor (`2^14 = 16,384`) rather than the traditional `10,000`. This means 35 bps corresponds to `35 / 16,384 ≈ 0.2136%`.
```
protocol_fee = floor(amount × 35 / 16_384)
```
### Example — 1,000 USDC withdrawal
```
1,000 USDC = 1,000,000,000 µUSDC
protocol_fee = floor(1,000,000,000 × 35 / 16,384) = 2,136,230 µUSDC ≈ 2.14 USDC
net received = 1,000,000,000 - 2,136,230 = 997,863,770 µUSDC ≈ 997.86 USDC
```
### Estimating programmatically
```typescript theme={null}
import { BPS_DIVISOR } from "@umbra-privacy/sdk";
const bps = 35n;
function estimateProtocolFee(amount: bigint): bigint {
return (amount * bps) / BPS_DIVISOR; // BPS_DIVISOR = 16_384n
}
const fee = estimateProtocolFee(1_000_000n); // 1 USDC (6 decimals)
// fee = 1_000_000n × 35n / 16_384n = 2_136n µUSDC
```
***
## Relayer Fee
The relayer submits claim transactions on your behalf so your wallet never appears on-chain as the fee payer. In exchange, relayers can charge a base SPL fee plus a BPS rate on the claimed amount.
**Current rates:** both are set to 0. Claiming a UTXO incurs no SPL relayer fee today.
When relayer fees are non-zero, they are applied on top of the protocol fee and deducted from the same token amount:
```
relayer_fee = RELAYER_BASE_FEE + floor(amount × RELAYER_BPS / 16_384)
net = amount - protocol_fee - relayer_fee
```
***
## Mixer SOL Fee
When you create a UTXO and insert it into the mixer, you pay a one-time SOL fee upfront. This pre-funds the eventual claim so neither you nor the relayer pays SOL out of pocket at claim time.
The SOL fee covers two costs:
* **Treap node rent** — Solana requires a minimum SOL balance to keep the nullifier account (a `TreapNode`) rent-exempt on-chain. This is approximately the minimum rent for a 48-byte account at current Solana rent rates.
* **Costliest claim path** — the fee includes enough SOL to cover the on-chain compute and transaction costs of the most expensive possible claim route out of the mixer.
The fee is dynamically calculated at UTXO creation time from the current Solana rent schedule. It is non-refundable — once inserted, the commitment is in the tree and the SOL is committed.
The mixer SOL fee is denominated in SOL (lamports), not in the token being transferred. It is separate from and in addition to the protocol fee and relayer fee, which are denominated in the transferred token.
***
## Which Operations Carry Which Fees
**Encrypted balance operations:**
* Self-deposit (public ATA → encrypted balance): 0 protocol fee for standard self-shielding
* Withdrawal (encrypted balance → public ATA): protocol fee applies
* Cross-account confidential transfer: protocol fee applies
**Mixer operations:**
* UTXO creation (encrypted balance → mixer): protocol fee on SPL amount + mixer SOL fee
* UTXO creation (public ATA → mixer): protocol fee on SPL amount + mixer SOL fee
* Claiming a UTXO (mixer → encrypted balance): protocol fee applies, relayer fee applies (currently 0)
* Claiming a UTXO (mixer → public ATA): protocol fee applies, relayer fee applies (currently 0)
***
## Token-2022 Transfer Fees
If the token mint has a Token-2022 transfer fee extension configured, that fee is deducted by the SPL program before tokens reach the Umbra pool. Umbra then measures the actual amount received and applies its protocol fee on top of that — it never charges protocol fees on the portion taken by the Token-2022 mechanism.
```
User sends: transfer_amount
T22 deducts: token_transfer_fee (set on the mint, outside Umbra's control)
Pool receives: actual_received = transfer_amount - token_transfer_fee
Umbra deducts: protocol_fee = floor(actual_received × 35 / 16,384)
Encrypted balance gets: actual_received - protocol_fee
```
***
## Fee Configuration
Protocol and relayer fee rates are stored in on-chain `ProtocolFeesConfiguration` and `RelayerFeesConfiguration` accounts, configurable by the pool admin and relayer operators respectively. The values above reflect the current live configuration.
If you are building a production integration and need exact fee amounts for display or pre-flight checks, fetch the on-chain fee configuration accounts directly rather than hardcoding the SDK defaults.
# Quickstart
Source: https://sdk.umbraprivacy.com/quickstart
End-to-end guide: install @umbra-privacy/sdk, call getUmbraClient, register, deposit into encrypted balance, create a UTXO, scan with getClaimableUtxoScannerFunction, and claim.
## Prerequisites
* Node.js 18+ or a modern browser environment
* A Solana wallet (or a generated keypair for testing)
* An RPC endpoint - any standard Solana JSON-RPC URL works
## 1. Install
```bash pnpm theme={null}
pnpm add @umbra-privacy/sdk
```
```bash npm theme={null}
npm install @umbra-privacy/sdk
```
```bash yarn theme={null}
yarn add @umbra-privacy/sdk
```
## 2. Create a Signer
For quick testing, generate an in-memory keypair. For production, see [Wallet Adapters](/sdk/wallet-adapters).
```typescript theme={null}
import { createInMemorySigner } from "@umbra-privacy/sdk";
// Generate a random keypair (for testing only)
const signer = await createInMemorySigner();
console.log("Wallet address:", signer.address);
```
An in-memory keypair is ephemeral - it disappears when your process exits. Use a browser wallet or a persistent keypair for anything beyond local testing.
## 3. Create the Umbra Client
```typescript theme={null}
import { getUmbraClient } from "@umbra-privacy/sdk";
const client = await getUmbraClient({
signer,
network: "mainnet",
rpcUrl: "https://api.mainnet-beta.solana.com",
rpcSubscriptionsUrl: "wss://api.mainnet-beta.solana.com",
indexerApiEndpoint: "https://utxo-indexer.api.umbraprivacy.com",
});
```
The Umbra program address differs between devnet and mainnet. The SDK resolves the correct address automatically based on the `network` parameter.
The first operation that requires the master seed (typically `register()` or `deposit()`) will prompt the user to sign a consent message. Subsequent operations reuse the cached seed without re-prompting.
## 4. Register Your Account
Registration sets up your on-chain Umbra identity. This function can be called regardless of whether the user is already registered - it handles the full setup, including key rotation when keys have changed. That said, each call submits on-chain transactions with SOL costs, so in practice you should check whether the account is already registered before calling it.
```typescript theme={null}
import { getUserRegistrationFunction } from "@umbra-privacy/sdk";
const register = getUserRegistrationFunction({ client });
// This is where the wallet signing prompt appears for the first time.
// The user signs once to derive the master seed; subsequent operations
// reuse the cached seed without prompting again.
const signatures = await register({
confidential: true, // enable encrypted balances
anonymous: true, // enable mixer / anonymous transfers
});
console.log(`Registered in ${signatures.length} transaction(s)`);
```
## 5. Deposit Tokens
Shield an SPL or Token-2022 token balance by moving it from your public wallet into an encrypted account.
```typescript theme={null}
import { getPublicBalanceToEncryptedBalanceDirectDepositorFunction } from "@umbra-privacy/sdk";
const deposit = getPublicBalanceToEncryptedBalanceDirectDepositorFunction({ client });
const MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
const amount = 1_000_000n; // 1 USDC (6 decimals)
const result = await deposit(
client.signer.address, // deposit into your own encrypted account
MINT,
amount,
);
console.log("Deposit queued:", result.queueSignature);
console.log("Callback confirmed:", result.callbackSignature);
```
## 6. Withdraw Tokens
Move tokens back from your encrypted account to your public wallet.
```typescript theme={null}
import { getEncryptedBalanceToPublicBalanceDirectWithdrawerFunction } from "@umbra-privacy/sdk";
const withdraw = getEncryptedBalanceToPublicBalanceDirectWithdrawerFunction({ client });
const result = await withdraw(
client.signer.address, // destination address
MINT,
amount,
);
console.log("Withdrawal queued:", result.queueSignature);
console.log("Callback confirmed:", result.callbackSignature);
```
## 7. Create a Receiver-Claimable UTXO
Send tokens privately to a recipient by depositing them into the [mixer](/sdk/mixer/overview). The recipient can later claim them with no on-chain link back to you as the sender.
UTXO creation requires a `zkProver` dependency for Groth16 proof generation. Install `@umbra-privacy/web-zk-prover` for the recommended browser-based prover - see [ZK Provers](/sdk/advanced/zk-provers) for details.
```typescript theme={null}
import { getPublicBalanceToReceiverClaimableUtxoCreatorFunction } from "@umbra-privacy/sdk";
import { getCreateReceiverClaimableUtxoFromPublicBalanceProver } from "@umbra-privacy/web-zk-prover";
const zkProver = getCreateReceiverClaimableUtxoFromPublicBalanceProver();
const createUtxo = getPublicBalanceToReceiverClaimableUtxoCreatorFunction(
{ client },
{ zkProver },
);
const RECIPIENT = "RecipientWalletAddressHere";
const signatures = await createUtxo({
destinationAddress: RECIPIENT,
mint: MINT,
amount,
});
console.log("UTXO created:", signatures[0]);
```
## 8. Fetch Claimable UTXOs
As the recipient, scan the Merkle tree for UTXOs addressed to your X25519 key. The SDK attempts to decrypt each ciphertext and returns only those belonging to you.
```typescript theme={null}
import { getClaimableUtxoScannerFunction } from "@umbra-privacy/sdk";
const fetchUtxos = getClaimableUtxoScannerFunction({ client });
// Scan tree 0 from the start - pass your last seen index to resume
const { received } = await fetchUtxos(0, 0);
console.log("Received UTXOs:", received.length);
```
## 9. Claim the UTXO
Present a ZK proof on-chain to burn the UTXO and receive the tokens. Claiming into an encrypted balance keeps the received amount private - no on-chain trace of who received what.
```typescript theme={null}
import {
getReceiverClaimableUtxoToEncryptedBalanceClaimerFunction,
getUmbraRelayer,
} from "@umbra-privacy/sdk";
import { getClaimReceiverClaimableUtxoIntoEncryptedBalanceProver } from "@umbra-privacy/web-zk-prover";
const zkProver = getClaimReceiverClaimableUtxoIntoEncryptedBalanceProver();
const relayer = getUmbraRelayer({
apiEndpoint: "https://relayer.api.umbraprivacy.com",
});
const claim = getReceiverClaimableUtxoToEncryptedBalanceClaimerFunction(
{ client },
{ zkProver, relayer },
);
const claimResult = await claim([received[0]]);
console.log("Claimed into encrypted balance:", claimResult);
```
## Full Example
```typescript theme={null}
import {
createInMemorySigner,
getUmbraClient,
getUserRegistrationFunction,
getPublicBalanceToEncryptedBalanceDirectDepositorFunction,
getEncryptedBalanceToPublicBalanceDirectWithdrawerFunction,
getPublicBalanceToReceiverClaimableUtxoCreatorFunction,
getClaimableUtxoScannerFunction,
getReceiverClaimableUtxoToEncryptedBalanceClaimerFunction,
getUmbraRelayer,
} from "@umbra-privacy/sdk";
import {
getCreateReceiverClaimableUtxoFromPublicBalanceProver,
getClaimReceiverClaimableUtxoIntoEncryptedBalanceProver,
} from "@umbra-privacy/web-zk-prover";
async function main() {
// 1. Signer (use your wallet adapter in production)
const signer = await createInMemorySigner();
// 2. Client
const client = await getUmbraClient({
signer,
network: "mainnet",
rpcUrl: "https://api.mainnet-beta.solana.com",
rpcSubscriptionsUrl: "wss://api.mainnet-beta.solana.com",
indexerApiEndpoint: "https://utxo-indexer.api.umbraprivacy.com",
});
// 3. Register (safe to call regardless of prior registration state)
const register = getUserRegistrationFunction({ client });
await register({ confidential: true, anonymous: true });
const MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
// 4. Deposit into encrypted balance
const deposit = getPublicBalanceToEncryptedBalanceDirectDepositorFunction({ client });
const depositResult = await deposit(signer.address, MINT, 1_000_000n);
console.log("Deposited:", depositResult.queueSignature);
// 5. Withdraw back to public wallet
const withdraw = getEncryptedBalanceToPublicBalanceDirectWithdrawerFunction({ client });
const withdrawResult = await withdraw(signer.address, MINT, 1_000_000n);
console.log("Withdrawn:", withdrawResult.queueSignature);
// 6. Set up ZK provers
const utxoProver = getCreateReceiverClaimableUtxoFromPublicBalanceProver();
const claimProver = getClaimReceiverClaimableUtxoIntoEncryptedBalanceProver();
// 7. Create a receiver-claimable UTXO
const createUtxo = getPublicBalanceToReceiverClaimableUtxoCreatorFunction(
{ client },
{ zkProver: utxoProver },
);
const RECIPIENT = "RecipientWalletAddressHere";
await createUtxo({
destinationAddress: RECIPIENT,
mint: MINT,
amount: 500_000n,
});
// 8. Fetch UTXOs addressed to this wallet (as the recipient)
const fetchUtxos = getClaimableUtxoScannerFunction({ client });
const { received } = await fetchUtxos(0, 0);
// 9. Claim the first received UTXO into an encrypted balance
const relayer = getUmbraRelayer({
apiEndpoint: "https://relayer.api.umbraprivacy.com",
});
if (received.length > 0) {
const claim = getReceiverClaimableUtxoToEncryptedBalanceClaimerFunction(
{ client },
{ zkProver: claimProver, relayer },
);
const claimResult = await claim([received[0]]);
console.log("Claimed:", claimResult);
}
}
main().catch(console.error);
```
## Next Steps
Connect Phantom, Solflare, or any Solana wallet.
Make transfers fully anonymous using the mixer.
Read encrypted balances and account state on-chain.
Handle deposit and withdrawal failures gracefully.
# Client
Source: https://sdk.umbraprivacy.com/reference/client
API reference: getUmbraClient(args, deps?) returns IUmbraClient. Args: signer, network, rpcUrl, offsets. No client-level commitment — per-call overrides only.
## getUmbraClient
**Import:** `@umbra-privacy/sdk`
```typescript theme={null}
async function getUmbraClient(
args: GetUmbraClientArgs,
deps?: GetUmbraClientDeps,
): Promise
```
The primary SDK entry point. Call this once per session with a connected wallet to construct the `IUmbraClient` that all factory functions consume.
By default this immediately prompts the wallet to sign the master seed derivation message. Pass `deferMasterSeedSignature: true` to skip that prompt at construction time - it will be requested on the first operation that requires key material.
There is no client-level `commitment` parameter. Each SDK function accepts per-call commitment overrides via its `options` argument (e.g., `accountInfoCommitment`, `epochInfoCommitment`, or `commitment`). Both default to `"confirmed"`.
***
### GetUmbraClientArgs
* `signer: IUmbraSigner` - Connected wallet. Must implement `signMessage` and `signTransaction`.
* `network: Network` - `"mainnet"` | `"devnet"` | `"localnet"`.
* `rpcUrl: string` - HTTPS RPC endpoint URL for the Solana cluster.
* `rpcSubscriptionsUrl: string` - WebSocket RPC endpoint URL for transaction confirmation.
* `indexerApiEndpoint?: string` - Base URL for the Umbra indexer (e.g. `"https://utxo-indexer.api.umbraprivacy.com"`). Required for mixer operations (`getClaimableUtxoScannerFunction`).
* `deferMasterSeedSignature?: boolean` - Skip the master seed signature prompt at construction time. Defaults to `false`.
* `versions?` - Override version specifiers. Leave unset to use SDK defaults.
* `protocol?: ProtocolVersionSpecifierFunction`
* `algorithm?: AlgorithmVersionSpecifierFunction`
* `scheme?: SchemeVersionSpecifierFunction`
* `offsets?` - Per-key derivation path offsets (`U512`). Used for key rotation - leave unset unless rotating.
* `masterViewingKey?: U512`
* `poseidonPrivateKey?: U512`
* `x25519UserAccountPrivateKey?: U512`
* `x25519MasterViewingKeyEncryptingPrivateKey?: U512`
* `mintX25519PrivateKey?: U512`
* `rescueCommitmentBlindingFactor?: U512`
* `randomCommitmentFactor?: U512`
***
### GetUmbraClientDeps
All fields are optional. Provide overrides for custom infrastructure or testing.
* `accountInfoProvider?: AccountInfoProviderFunction` - Override the default RPC-based account fetcher.
* `blockhashProvider?: GetLatestBlockhash` - Override the default blockhash fetcher.
* `transactionForwarder?: TransactionForwarder` - Override transaction broadcasting (e.g. for Jito bundles or custom retry logic).
* `epochInfoProvider?: GetEpochInfo` - Override the epoch info fetcher (used for Token-2022 fee schedules).
* `masterSeedStorage?` - Override master seed persistence.
* `load?: MasterSeedLoaderFunction` - Load the cached master seed (e.g. from encrypted local storage).
* `store?: MasterSeedStorerFunction` - Persist the master seed after derivation.
* `generate?: MasterSeedGeneratorFunction` - Derive the master seed from a wallet signature (override the default KMAC256-based derivation).
***
### Returns
`Promise` - The client configuration object. Pass `client` to every factory function.
***
### Example
```typescript theme={null}
import { getUmbraClient } from "@umbra-privacy/sdk";
const client = await getUmbraClient({
signer: walletAdapter,
network: "mainnet",
rpcUrl: "https://api.mainnet-beta.solana.com",
rpcSubscriptionsUrl: "wss://api.mainnet-beta.solana.com",
indexerApiEndpoint: "https://utxo-indexer.api.umbraprivacy.com",
});
```
***
## IUmbraClient
The read-only configuration object produced by `getUmbraClient`. All factory functions accept it as `args.client`. Do not call methods on it directly.
* `signer: IUmbraSigner` - The connected wallet.
* `network: Network` - The configured network.
* `networkConfig: NetworkConfig` - On-chain program IDs and addresses for the configured network.
* `versions: ResolvedVersions` - Resolved protocol, algorithm, and scheme version identifiers.
* `offsets: ResolvedOffsets` - Resolved key derivation offsets. Commitment is not stored on the client - each SDK function accepts per-call commitment overrides.
* `accountInfoProvider: AccountInfoProviderFunction` - Account data fetcher (RPC-based or overridden).
* `blockhashProvider: GetLatestBlockhash` - Blockhash fetcher (RPC-based or overridden).
* `transactionForwarder: TransactionForwarder` - Transaction broadcaster (WebSocket-based or overridden).
* `epochInfoProvider: GetEpochInfo` - Epoch info fetcher (RPC-based or overridden).
* `fetchMerkleProof?: FetchMerkleProofFunction` - Indexer-based Merkle proof fetcher. Present only when `indexerApiEndpoint` is set.
* `fetchUtxoData?: FetchUtxoDataFunction` - Indexer-based UTXO discovery fetcher. Present only when `indexerApiEndpoint` is set.
* `masterSeed: { load, store, generate, getMasterSeed }` - Master seed storage operations. `load` attempts to load an existing seed, `store` persists it, `generate` derives a new seed via signature, and `getMasterSeed` is a convenience method combining load and generate.
***
## IUmbraSigner
The wallet interface the SDK requires. Any connected wallet that can sign messages and transactions satisfies this interface.
```typescript theme={null}
interface IUmbraSigner {
readonly address: Address;
signTransaction(transaction: SignableTransaction): Promise;
signTransactions(transactions: readonly SignableTransaction[]): Promise;
signMessage(message: Uint8Array): Promise;
}
```
### address
The base58-encoded Ed25519 public key that uniquely identifies this signer on the Solana network. Used as the account owner, fee payer identifier, and key lookup in signed transactions.
* `address: Address` - Read-only public address.
### signTransaction
Signs a compiled Solana versioned transaction.
* `transaction: SignableTransaction` - The versioned transaction to sign.
* Returns: `Promise` - The signed transaction ready for network submission.
### signTransactions
Signs multiple transactions in a batch. Returns them in the same order with signatures added.
* `transactions: readonly SignableTransaction[]` - Array of transactions to sign.
* Returns: `Promise` - The signed transactions in the same order as input.
### signMessage
Signs an arbitrary byte sequence. Used once per session to derive the master seed - this is the only non-transaction signature the user sees.
* `message: Uint8Array` - The bytes to sign. The SDK passes a deterministic human-readable message encoding the derivation intent.
* Returns: `Promise` - Contains the original message bytes, the 64-byte Ed25519 signature, and the signer's address.
***
## Network
```typescript theme={null}
type Network = "mainnet" | "devnet" | "localnet";
```
Selects the set of on-chain program IDs and addresses the SDK uses. Pass this in the `network` field of `getUmbraClient` args.
# Compliance
Source: https://sdk.umbraprivacy.com/reference/compliance
API reference: getComplianceGrantIssuerFunction, getComplianceGrantRevokerFunction, getUserComplianceGrantQuerierFunction, re-encryption for user/network grants.
Compliance grants allow authorized third parties (regulators, compliance tools) to read encrypted data. Grants are stored on-chain and come in two kinds:
* **User grants** - Authorized by the user. Grants a third party access to the user's shared-mode ciphertexts.
* **Network grants** - Authorized by the Arcium network. Grants access to MXE-mode or shared-mode ciphertexts without requiring user interaction.
***
## getComplianceGrantIssuerFunction
**Import:** `@umbra-privacy/sdk`
```typescript theme={null}
function getComplianceGrantIssuerFunction(
args: GetCreateUserGrantedComplianceGrantFunctionArgs,
deps?: GetCreateUserGrantedComplianceGrantFunctionDeps,
): CreateUserGrantedComplianceGrantFunction
```
Creates an on-chain compliance grant signed by the user, authorizing a third-party receiver to re-encrypt and read the user's shared-mode ciphertexts.
### GetCreateUserGrantedComplianceGrantFunctionArgs
* `client: IUmbraClient`
### GetCreateUserGrantedComplianceGrantFunctionDeps
* `getLatestBlockhash?: GetLatestBlockhash`
* `transactionForwarder?: TransactionForwarder`
* `masterViewingKeyX25519KeypairGenerator?: MasterViewingKeyX25519KeypairGeneratorFunction`
### Returns
`CreateUserGrantedComplianceGrantFunction`
```typescript theme={null}
type CreateUserGrantedComplianceGrantFunction = (
receiver: Address,
granterX25519: X25519PublicKey,
receiverX25519: X25519PublicKey,
nonce: RcEncryptionNonce,
optionalData?: OptionalData32,
callbacks?: TransactionCallbacks,
) => Promise
```
* `receiver: Address` - The third-party account that will be authorized to call re-encryption.
* `granterX25519: X25519PublicKey` - The granter's X25519 public key (MVK encrypting key).
* `receiverX25519: X25519PublicKey` - The receiver's X25519 public key (the target re-encryption key).
* `nonce: RcEncryptionNonce` - Grant nonce, used to differentiate multiple grants to the same receiver.
### Example
```typescript theme={null}
import { getComplianceGrantIssuerFunction } from "@umbra-privacy/sdk";
const createGrant = getComplianceGrantIssuerFunction({ client });
const signature = await createGrant(
receiverAddress,
granterX25519PublicKey,
receiverX25519PublicKey,
grantNonce,
);
```
***
## getComplianceGrantRevokerFunction
**Import:** `@umbra-privacy/sdk`
```typescript theme={null}
function getComplianceGrantRevokerFunction(
args: GetDeleteUserGrantedComplianceGrantFunctionArgs,
deps?: GetDeleteUserGrantedComplianceGrantFunctionDeps,
): DeleteUserGrantedComplianceGrantFunction
```
Revokes an existing user-granted compliance grant, removing the third-party's authorization to re-encrypt.
### GetDeleteUserGrantedComplianceGrantFunctionArgs
* `client: IUmbraClient`
### GetDeleteUserGrantedComplianceGrantFunctionDeps
* `getLatestBlockhash?: GetLatestBlockhash`
* `transactionForwarder?: TransactionForwarder`
* `masterViewingKeyX25519KeypairGenerator?: MasterViewingKeyX25519KeypairGeneratorFunction`
### Returns
`DeleteUserGrantedComplianceGrantFunction`
```typescript theme={null}
type DeleteUserGrantedComplianceGrantFunction = (
receiver: Address,
granterX25519: X25519PublicKey,
receiverX25519: X25519PublicKey,
nonce: RcEncryptionNonce,
optionalData?: OptionalData32,
callbacks?: TransactionCallbacks,
) => Promise
```
Parameters are identical to `CreateUserGrantedComplianceGrantFunction` - pass the same values used when creating the grant.
***
## getUserComplianceGrantQuerierFunction
**Import:** `@umbra-privacy/sdk`
```typescript theme={null}
function getUserComplianceGrantQuerierFunction(
args: GetQueryUserComplianceGrantFunctionArgs,
deps?: GetQueryUserComplianceGrantFunctionDeps,
): QueryUserComplianceGrantFunction
```
Checks whether a specific user-granted compliance grant exists on-chain.
### GetQueryUserComplianceGrantFunctionArgs
* `client: IUmbraClient`
### GetQueryUserComplianceGrantFunctionDeps
* `accountInfoProvider?: AccountInfoProviderFunction`
### Returns
`QueryUserComplianceGrantFunction`
```typescript theme={null}
type QueryUserComplianceGrantFunction = (
granterX25519: X25519PublicKey,
nonce: RcEncryptionNonce,
receiverX25519: X25519PublicKey,
) => Promise
```
***
## getQueryNetworkMxeComplianceGrantFunction
**Import:** `@umbra-privacy/sdk`
```typescript theme={null}
function getQueryNetworkMxeComplianceGrantFunction(
args: GetQueryNetworkMxeComplianceGrantFunctionArgs,
deps?: GetQueryNetworkMxeComplianceGrantFunctionDeps,
): QueryNetworkMxeComplianceGrantFunction
```
Checks whether a network MXE compliance grant exists for the given nonce and receiver key. Network MXE grants allow the Arcium network to re-encrypt MXE-mode ciphertexts.
### Returns
`QueryNetworkMxeComplianceGrantFunction`
```typescript theme={null}
type QueryNetworkMxeComplianceGrantFunction = (
nonce: RcEncryptionNonce,
receiverX25519: X25519PublicKey,
) => Promise
```
***
## getQueryNetworkSharedComplianceGrantFunction
**Import:** `@umbra-privacy/sdk`
```typescript theme={null}
function getQueryNetworkSharedComplianceGrantFunction(
args: GetQueryNetworkSharedComplianceGrantFunctionArgs,
deps?: GetQueryNetworkSharedComplianceGrantFunctionDeps,
): QueryNetworkSharedComplianceGrantFunction
```
Checks whether a network shared compliance grant exists. Network shared grants allow the Arcium network to re-encrypt shared-mode ciphertexts.
### Returns
`QueryNetworkSharedComplianceGrantFunction`
```typescript theme={null}
type QueryNetworkSharedComplianceGrantFunction = (
granterX25519: X25519PublicKey,
nonce: RcEncryptionNonce,
receiverX25519: X25519PublicKey,
) => Promise
```
### QueryComplianceGrantResult
Returned by all three query functions:
* `{ state: "exists" }` - The grant is present on-chain.
* `{ state: "non_existent" }` - No grant exists for the given parameters.
***
## getReencryptMxeCiphertextsNetworkGrantFunction
**Import:** `@umbra-privacy/sdk`
```typescript theme={null}
function getReencryptMxeCiphertextsNetworkGrantFunction(
args: GetReencryptMxeCiphertextsNetworkGrantFunctionArgs,
deps?: GetReencryptMxeCiphertextsNetworkGrantFunctionDeps,
): ReencryptMxeCiphertextsNetworkGrantFunction
```
Re-encrypts MXE-mode ciphertexts under a network grant, making them readable by the grant receiver. Queues an Arcium MPC computation.
### GetReencryptMxeCiphertextsNetworkGrantFunctionArgs
* `client: IUmbraClient`
### GetReencryptMxeCiphertextsNetworkGrantFunctionDeps
* `getLatestBlockhash?: GetLatestBlockhash`
* `transactionForwarder?: TransactionForwarder`
### Returns
`ReencryptMxeCiphertextsNetworkGrantFunction`
```typescript theme={null}
type ReencryptMxeCiphertextsNetworkGrantFunction = (
receiverX25519Key: X25519PublicKey,
nonce: RcEncryptionNonce,
inputEncryptionNonce: RcEncryptionNonce,
ciphertexts: readonly Uint8Array[],
optionalData?: OptionalData32,
callbacks?: TransactionCallbacks,
) => Promise
```
* `receiverX25519Key: X25519PublicKey` - The receiver's X25519 public key.
* `nonce: RcEncryptionNonce` - The grant nonce identifying which network grant to use.
* `inputEncryptionNonce: RcEncryptionNonce` - The nonce used when the ciphertexts were originally encrypted.
* `ciphertexts: readonly Uint8Array[]` - The MXE-encrypted ciphertexts to re-encrypt. Must contain between 1 and 6 elements.
***
## getSharedCiphertextReencryptorForNetworkGrantFunction
**Import:** `@umbra-privacy/sdk`
```typescript theme={null}
function getSharedCiphertextReencryptorForNetworkGrantFunction(
args: GetSharedCiphertextReencryptorForNetworkGrantFunctionArgs,
deps?: GetSharedCiphertextReencryptorForNetworkGrantFunctionDeps,
): SharedCiphertextReencryptorForNetworkGrantFunction
```
Re-encrypts shared-mode ciphertexts under a network shared grant.
### Returns
`ReencryptSharedCiphertextsNetworkGrantFunction`
```typescript theme={null}
type ReencryptSharedCiphertextsNetworkGrantFunction = (
granterX25519Key: X25519PublicKey,
receiverX25519Key: X25519PublicKey,
nonce: RcEncryptionNonce,
inputEncryptionNonce: RcEncryptionNonce,
ciphertexts: readonly Uint8Array[],
optionalData?: OptionalData32,
callbacks?: TransactionCallbacks,
) => Promise
```
* `granterX25519Key: X25519PublicKey` - The granter's X25519 public key.
* `receiverX25519Key: X25519PublicKey` - The receiver's X25519 public key.
* `ciphertexts: readonly Uint8Array[]` - Must contain between 1 and 6 elements.
***
## getSharedCiphertextReencryptorForUserGrantFunction
**Import:** `@umbra-privacy/sdk`
```typescript theme={null}
function getSharedCiphertextReencryptorForUserGrantFunction(
args: GetReencryptSharedCiphertextsUserGrantFunctionArgs,
deps?: GetReencryptSharedCiphertextsUserGrantFunctionDeps,
): ReencryptSharedCiphertextsUserGrantFunction
```
Re-encrypts shared-mode ciphertexts under a user-granted compliance grant.
### Returns
`ReencryptSharedCiphertextsUserGrantFunction`
```typescript theme={null}
type ReencryptSharedCiphertextsUserGrantFunction = (
granterX25519Key: X25519PublicKey,
receiverX25519Key: X25519PublicKey,
nonce: RcEncryptionNonce,
inputEncryptionNonce: RcEncryptionNonce,
ciphertexts: readonly Uint8Array[],
optionalData?: OptionalData32,
callbacks?: TransactionCallbacks,
) => Promise
```
Identical signature to `ReencryptSharedCiphertextsNetworkGrantFunction`. The difference is which on-chain grant account is used to authorize the re-encryption.
* `ciphertexts: readonly Uint8Array[]` - Must contain between 1 and 6 elements.
# Conversion
Source: https://sdk.umbraprivacy.com/reference/conversion
API reference: getNetworkEncryptionToSharedEncryptionConverterFunction (MXE to Shared batch) + getMintEncryptionKeyRotatorFunction (X25519 key rotation per mint).
## getNetworkEncryptionToSharedEncryptionConverterFunction
**Import:** `@umbra-privacy/sdk`
```typescript theme={null}
function getNetworkEncryptionToSharedEncryptionConverterFunction(
args: GetNetworkEncryptionToSharedEncryptionConverterFunctionArgs,
deps?: GetNetworkEncryptionToSharedEncryptionConverterFunctionDeps,
): NetworkEncryptionToSharedEncryptionConverterFunction
```
Converts one or more encrypted token accounts from MXE-mode (network-decryptable only) to shared-mode (user-decryptable). Conversion is per-mint and involves an on-chain Arcium MPC computation. Mints that are already in shared mode, uninitialized, or non-existent are skipped rather than erroring.
***
### GetNetworkEncryptionToSharedEncryptionConverterFunctionArgs
* `client: IUmbraClient`
### GetNetworkEncryptionToSharedEncryptionConverterFunctionDeps
* `accountInfoProvider?: AccountInfoProviderFunction`
* `getLatestBlockhash?: GetLatestBlockhash`
* `transactionForwarder?: TransactionForwarder`
### Returns
`NetworkEncryptionToSharedEncryptionConverterFunction`
```typescript theme={null}
type NetworkEncryptionToSharedEncryptionConverterFunction = (
mints: readonly Address[],
optionalData?: OptionalData32,
callbacks?: TransactionCallbacks,
) => Promise
```
* `mints: readonly Address[]` - Token mints to convert. Processed sequentially - each successful conversion is independent.
### ConvertToSharedEncryptionResult
```typescript theme={null}
interface ConvertToSharedEncryptionResult {
converted: Map;
skipped: Map;
}
```
* `converted` - Mints successfully converted. Value is the transaction signature.
* `skipped` - Mints not converted. Value is the reason.
### ConvertToSharedEncryptionSkipReason
* `"non_existent"` - No encrypted token account exists for this mint.
* `"not_initialised"` - The encrypted token account has not been initialised.
* `"already_shared"` - This mint is already in shared mode.
* `"balance_not_initialised"` - The balance state is not yet initialised (e.g. no deposits have occurred).
***
### Errors
Throws `ConversionError` if any non-skippable error occurs. See [Errors](./errors#conversionerror).
### Example
```typescript theme={null}
import { getNetworkEncryptionToSharedEncryptionConverterFunction } from "@umbra-privacy/sdk";
const convert = getNetworkEncryptionToSharedEncryptionConverterFunction({ client });
const result = await convert([usdcMint, solMint]);
for (const [mint, sig] of result.converted) {
console.log(`Converted ${mint}:`, sig);
}
for (const [mint, reason] of result.skipped) {
console.log(`Skipped ${mint}: ${reason}`);
}
```
***
## getMintEncryptionKeyRotatorFunction
**Import:** `@umbra-privacy/sdk`
```typescript theme={null}
function getMintEncryptionKeyRotatorFunction(
args: GetMintEncryptionKeyRotatorFunctionArgs,
deps?: GetMintEncryptionKeyRotatorFunctionDeps,
): MintEncryptionKeyRotatorFunction
```
Rotates the X25519 encryption key for a specific mint's shared-mode encrypted token account. Use this when the per-mint key may have been compromised. The account must already be in shared mode.
***
### GetMintEncryptionKeyRotatorFunctionArgs
* `client: IUmbraClient`
### GetMintEncryptionKeyRotatorFunctionDeps
* `accountInfoProvider?: AccountInfoProviderFunction`
* `getLatestBlockhash?: GetLatestBlockhash`
* `transactionForwarder?: TransactionForwarder`
* `mintX25519KeypairGenerator?: MintX25519KeypairGeneratorFunction` - Override the per-mint X25519 keypair derivation.
### Returns
`MintEncryptionKeyRotatorFunction`
```typescript theme={null}
type MintEncryptionKeyRotatorFunction = (
mint: Address,
optionalData?: OptionalData32,
callbacks?: TransactionCallbacks,
) => Promise
```
* `mint: Address` - The token mint whose X25519 key to rotate.
***
### Errors
Throws `ConversionError`. See [Errors](./errors#conversionerror).
### Example
```typescript theme={null}
import { getMintEncryptionKeyRotatorFunction } from "@umbra-privacy/sdk";
const rotateMintKey = getMintEncryptionKeyRotatorFunction({ client });
const signature = await rotateMintKey(usdcMint);
```
***
## ConversionError
Thrown by both conversion functions.
Stage values: `"initialization"` | `"account-fetch"` | `"pda-derivation"` | `"instruction-build"` | `"transaction-build"` | `"transaction-compile"` | `"transaction-sign"` | `"transaction-validate"` | `"transaction-send"`
See [Errors](./errors#conversionerror) for full documentation.
# Deposit
Source: https://sdk.umbraprivacy.com/reference/deposit
API reference: getPublicBalanceToEncryptedBalanceDirectDepositorFunction (DepositResult) + 4 UTXO creators for self/receiver-claimable from encrypted/public balance.
## getPublicBalanceToEncryptedBalanceDirectDepositorFunction
**Import:** `@umbra-privacy/sdk`
```typescript theme={null}
function getPublicBalanceToEncryptedBalanceDirectDepositorFunction(
args: GetPublicBalanceToEncryptedBalanceDirectDepositorFunctionArgs,
deps?: GetPublicBalanceToEncryptedBalanceDirectDepositorFunctionDeps,
): PublicBalanceToEncryptedBalanceDirectDepositorFunction
```
Transfers tokens from a public associated token account (ATA) into a destination address's encrypted balance. The destination must be registered on Umbra. This is the primary deposit path - no ZK proof required. Follows the [dual-instruction pattern](/concepts/how-umbra-works#the-dual-instruction-pattern).
***
### GetPublicBalanceToEncryptedBalanceDirectDepositorFunctionArgs
* `client: IUmbraClient`
### GetPublicBalanceToEncryptedBalanceDirectDepositorFunctionDeps
* `accountInfoProvider?: AccountInfoProviderFunction`
* `getLatestBlockhash?: GetLatestBlockhash`
* `transactionForwarder?: TransactionForwarder`
* `getEpochInfo?: GetEpochInfo` - Required for Token-2022 transfer fee calculation.
### Returns
`PublicBalanceToEncryptedBalanceDirectDepositorFunction`
```typescript theme={null}
type PublicBalanceToEncryptedBalanceDirectDepositorFunction = (
destinationAddress: Address,
mint: Address,
transferAmount: U64,
options?: DepositIntoEncryptedBalanceOptions,
) => Promise
```
* `destinationAddress: Address` - The Umbra-registered recipient.
* `mint: Address` - Token mint address.
* `transferAmount: U64` - Amount in base token units.
### DepositIntoEncryptedBalanceOptions
* `priorityFees?: U64` - Compute unit price in micro-lamports. Default: `0n`.
* `purpose?: number` - Caller-defined purpose tag stored on-chain. Default: `0`.
* `optionalData?: OptionalData32` - 32-byte caller metadata. Default: 32 zero bytes.
* `awaitCallback?: boolean` - Whether to wait for the MPC callback. Default: `true`.
* `skipPreflight?: boolean` - Skip Solana preflight simulation. Default: `false`.
* `maxRetries?: number` - Max RPC retry attempts for transaction sending.
* `accountInfoCommitment?: Commitment` - Per-call commitment for RPC account reads. Default: `"confirmed"`.
* `epochInfoCommitment?: Commitment` - Per-call commitment for epoch info fetches. Default: `"confirmed"`.
### DepositResult
```typescript theme={null}
interface DepositResult {
queueSignature: TransactionSignature;
callbackStatus?: "finalized" | "pruned" | "timed-out";
callbackSignature?: TransactionSignature;
callbackElapsedMs?: number;
rentClaimSignature?: TransactionSignature;
rentClaimError?: string;
}
```
* `queueSignature` - Signature of the handler (queue computation) transaction.
* `callbackStatus` - The outcome of computation monitoring: `"finalized"`, `"pruned"`, or `"timed-out"`. Present when `awaitCallback` is `true`.
* `callbackSignature` - Signature of the Arcium MPC callback. Present when `callbackStatus` is `"finalized"`.
* `callbackElapsedMs` - Milliseconds spent waiting for the callback.
* `rentClaimSignature` - Signature for reclaiming rent from the computation account. Attempted regardless of callback outcome.
* `rentClaimError` - Error from rent reclaim attempt, if any. The deposit itself still succeeded.
### Errors
Throws `EncryptedDepositError`. See [Errors](./errors#encrypteddepositerror).
### Example
```typescript theme={null}
import { getPublicBalanceToEncryptedBalanceDirectDepositorFunction } from "@umbra-privacy/sdk";
const deposit = getPublicBalanceToEncryptedBalanceDirectDepositorFunction({ client });
const result = await deposit(recipientAddress, mintAddress, 1_000_000n);
console.log("Queue signature:", result.queueSignature);
```
***
## getEncryptedBalanceToSelfClaimableUtxoCreatorFunction
**Import:** `@umbra-privacy/sdk`
```typescript theme={null}
function getEncryptedBalanceToSelfClaimableUtxoCreatorFunction(
args: GetCreateSelfClaimableUtxoFromEncryptedBalanceFunctionArgs,
deps: GetCreateSelfClaimableUtxoFromEncryptedBalanceFunctionDeps,
): CreateSelfClaimableUtxoFromEncryptedBalanceFunction
```
Creates a self-claimable UTXO in the mixer funded from the caller's encrypted balance (encrypted balance -> Mixer). The created UTXO can only be claimed by the creator. Requires a ZK proof - `deps.zkProver` is **required**.
***
### GetCreateSelfClaimableUtxoFromEncryptedBalanceFunctionArgs
* `client: IUmbraClient`
### GetCreateSelfClaimableUtxoFromEncryptedBalanceFunctionDeps
* `zkProver: IZkProverForSelfClaimableUtxo` - **Required.** ZK proof generator for UTXO commitments. Use `getCreateSelfClaimableUtxoFromEncryptedBalanceProver` from `@umbra-privacy/web-zk-prover` -- see [ZK Provers](/sdk/advanced/zk-provers). This operation submits two transactions: a proof account creation followed by the UTXO instruction.
* `blockhashProvider?: GetLatestBlockhash`
* `accountInfoProvider?: AccountInfoProviderFunction`
* `transactionForwarder?: TransactionForwarder`
* `getEpochInfo?: GetEpochInfo`
* Additional cryptographic dependency overrides (key derivation, Poseidon, AES, commitment generators - all optional, default to SDK implementations).
### Returns
`CreateSelfClaimableUtxoFromEncryptedBalanceFunction`
```typescript theme={null}
type CreateSelfClaimableUtxoFromEncryptedBalanceFunction = (
args: CreateUtxoArgs,
options?: CreateUtxoOptions,
) => Promise
```
Returns two signatures: `[proofAccountSignature, utxoCreationSignature]`.
### CreateUtxoArgs
* `amount: U64` - Token amount in base units to lock into the UTXO.
* `destinationAddress: Address` - The Umbra-registered owner of the UTXO (must equal the caller for self-claimable).
* `mint: Address` - Token mint address.
### CreateUtxoOptions
* `generationIndex?: U256` - Override the generation index used for key derivation. Defaults to the on-chain value.
* `optionalData?: OptionalData32` - 32-byte caller metadata.
* `createProofAccount?: TransactionCallbacks` - Lifecycle hooks for the proof account creation transaction.
* `createUtxo?: TransactionCallbacks` - Lifecycle hooks for the UTXO creation transaction.
### Errors
Throws `CreateUtxoError`. See [Errors](./errors#createutxoerror).
***
## getEncryptedBalanceToReceiverClaimableUtxoCreatorFunction
**Import:** `@umbra-privacy/sdk`
```typescript theme={null}
function getEncryptedBalanceToReceiverClaimableUtxoCreatorFunction(
args: GetCreateReceiverClaimableUtxoFromEncryptedBalanceFunctionArgs,
deps: GetCreateReceiverClaimableUtxoFromEncryptedBalanceFunctionDeps,
): CreateReceiverClaimableUtxoFromEncryptedBalanceFunction
```
Creates a receiver-claimable UTXO in the mixer funded from the caller's encrypted balance (encrypted balance -> Mixer). The UTXO is encrypted for the recipient's X25519 key - only the recipient can claim it. Requires `deps.zkProver` (**required**).
***
### GetCreateReceiverClaimableUtxoFromEncryptedBalanceFunctionArgs
* `client: IUmbraClient`
### GetCreateReceiverClaimableUtxoFromEncryptedBalanceFunctionDeps
* `zkProver: IZkProverForReceiverClaimableUtxo` - **Required.** Use `getCreateReceiverClaimableUtxoFromEncryptedBalanceProver` from `@umbra-privacy/web-zk-prover` -- see [ZK Provers](/sdk/advanced/zk-provers).
* `blockhashProvider?: GetLatestBlockhash`
* `accountInfoProvider?: AccountInfoProviderFunction`
* `transactionForwarder?: TransactionForwarder`
* `getEpochInfo?: GetEpochInfo`
* Additional cryptographic dependency overrides (all optional).
### Returns
`CreateReceiverClaimableUtxoFromEncryptedBalanceFunction`
```typescript theme={null}
type CreateReceiverClaimableUtxoFromEncryptedBalanceFunction = (
args: CreateUtxoArgs,
options?: CreateUtxoOptions,
) => Promise
```
See `CreateUtxoArgs` and `CreateUtxoOptions` above.
### Errors
Throws `CreateUtxoError`. See [Errors](./errors#createutxoerror).
***
## getPublicBalanceToSelfClaimableUtxoCreatorFunction
**Import:** `@umbra-privacy/sdk`
```typescript theme={null}
function getPublicBalanceToSelfClaimableUtxoCreatorFunction(
args: GetCreateSelfClaimableUtxoFromPublicBalanceFunctionArgs,
deps: GetCreateSelfClaimableUtxoFromPublicBalanceFunctionDeps,
): CreateSelfClaimableUtxoFromPublicBalanceFunction
```
Creates a self-claimable UTXO in the mixer funded from a public ATA (ATA -> Mixer). No MPC required - the ZK proof is generated client-side. `deps.zkProver` is **required**.
***
### GetCreateSelfClaimableUtxoFromPublicBalanceFunctionArgs
* `client: IUmbraClient`
### GetCreateSelfClaimableUtxoFromPublicBalanceFunctionDeps
* `zkProver: ZkProverForSelfClaimableUtxoFromPublicBalance` - **Required.** Use `getCreateSelfClaimableUtxoFromPublicBalanceProver` from `@umbra-privacy/web-zk-prover` -- see [ZK Provers](/sdk/advanced/zk-provers).
* `blockhashProvider?: GetLatestBlockhash`
* `accountInfoProvider?: AccountInfoProviderFunction`
* `transactionForwarder?: TransactionForwarder`
* `getEpochInfo?: GetEpochInfo`
* `masterViewingKeyGenerator?: MasterViewingKeyGeneratorFunction`
* `masterViewingKeyBlindingFactorGenerator?: MasterViewingKeyBlindingFactorGeneratorFunction`
* `poseidonPrivateKeyGenerator?: PoseidonPrivateKeyGeneratorFunction`
* `poseidonBlindingFactorGenerator?: PoseidonBlindingFactorGeneratorFunction`
* `userAccountX25519KeypairGenerator?: Curve25519KeypairGeneratorFunction`
* `secondViewingKeyGenerator?: SecondViewingKeyGeneratorFunction`
* `ephemeralUtxoMasterViewingKeyGenerator?: EphemeralUtxoMasterViewingKeyGeneratorFunction`
* `ephemeralUtxoMasterViewingKeyBlindingFactorGenerator?: EphemeralUtxoMasterViewingKeyBlindingFactorGeneratorFunction`
* `ephemeralUtxoPoseidonKeyGenerator?: EphemeralUtxoPoseidonPrivateKeyGeneratorFunction`
* `ephemeralUtxoPoseidonKeyBlindingFactorGenerator?: EphemeralUtxoPoseidonPrivateKeyBlindingFactorGeneratorFunction`
* `poseidonKeystreamBlindingFactorGenerator?: PoseidonKeystreamBlindingFactorGeneratorFunction`
* `poseidonHasher?: PoseidonHashFunction`
* `aesEncryptor?: AesEncryptorFunction`
* `userCommitmentGenerator?: UserCommitmentGeneratorFunction`
* `h2Generator?: H2GeneratorFns`
* `keystreamCommitmentGenerator?: KeystreamCommitmentFunction`
* `poseidonEncryptor?: PoseidonEncryptorFunction`
* `poseidonKeystreamGenerator?: PoseidonKeystreamGeneratorFunction`
* `getUtcNow?: () => UtcTimestampComponents`
### Returns
`CreateSelfClaimableUtxoFromPublicBalanceFunction`
```typescript theme={null}
type CreateSelfClaimableUtxoFromPublicBalanceFunction = (
args: CreateUtxoArgs,
options?: CreateUtxoFromPublicBalanceOptions,
) => Promise
```
### CreateUtxoFromPublicBalanceOptions
* `generationIndex?: U256`
* `optionalData?: OptionalData32`
* `createUtxo?: TransactionCallbacks` - Lifecycle hooks for the single UTXO creation transaction.
### Errors
Throws `CreateUtxoError`. See [Errors](./errors#createutxoerror).
***
## getPublicBalanceToReceiverClaimableUtxoCreatorFunction
**Import:** `@umbra-privacy/sdk`
```typescript theme={null}
function getPublicBalanceToReceiverClaimableUtxoCreatorFunction(
args: GetCreateReceiverClaimableUtxoFromPublicBalanceFunctionArgs,
deps: GetCreateReceiverClaimableUtxoFromPublicBalanceFunctionDeps,
): CreateReceiverClaimableUtxoFromPublicBalanceFunction
```
Creates a receiver-claimable UTXO funded from a public ATA (ATA -> Mixer). The UTXO is encrypted for the recipient - only they can claim it. `deps.zkProver` is **required**.
***
### GetCreateReceiverClaimableUtxoFromPublicBalanceFunctionArgs
* `client: IUmbraClient`
### GetCreateReceiverClaimableUtxoFromPublicBalanceFunctionDeps
* `zkProver: ZkProverForReceiverClaimableUtxoFromPublicBalance` - **Required.** Use `getCreateReceiverClaimableUtxoFromPublicBalanceProver` from `@umbra-privacy/web-zk-prover` -- see [ZK Provers](/sdk/advanced/zk-provers).
* `blockhashProvider?: GetLatestBlockhash`
* `accountInfoProvider?: AccountInfoProviderFunction`
* `transactionForwarder?: TransactionForwarder`
* `getEpochInfo?: GetEpochInfo`
* Additional cryptographic dependency overrides (all optional).
### Returns
`CreateReceiverClaimableUtxoFromPublicBalanceFunction`
```typescript theme={null}
type CreateReceiverClaimableUtxoFromPublicBalanceFunction = (
args: CreateUtxoArgs,
options?: CreateReceiverClaimableUtxoFromPublicBalanceOptions,
) => Promise
```
### CreateReceiverClaimableUtxoFromPublicBalanceOptions
* `generationIndex?: U256`
* `optionalData?: OptionalData32`
* `createUtxo?: TransactionCallbacks`
### Errors
Throws `CreateUtxoError`. See [Errors](./errors#createutxoerror).
***
## EncryptedDepositError
Thrown by `getPublicBalanceToEncryptedBalanceDirectDepositorFunction`.
Stage values: `"initialization"` | `"validation"` | `"mint-fetch"` | `"fee-calculation"` | `"pda-derivation"` | `"account-fetch"` | `"instruction-build"` | `"transaction-build"` | `"transaction-compile"` | `"transaction-sign"` | `"transaction-validate"` | `"transaction-send"`
See [Errors](./errors#encrypteddepositerror) for full documentation.
***
## CreateUtxoError
Thrown by all four UTXO creation functions.
Stage values: `"initialization"` | `"validation"` | `"account-fetch"` | `"mint-fetch"` | `"fee-calculation"` | `"key-derivation"` | `"zk-proof-generation"` | `"pda-derivation"` | `"instruction-build"` | `"transaction-build"` | `"transaction-compile"` | `"transaction-sign"` | `"transaction-validate"` | `"transaction-send"`
See [Errors](./errors#createutxoerror) for full documentation.
# Errors
Source: https://sdk.umbraprivacy.com/reference/errors
UmbraError hierarchy: EncryptedDepositError, ClaimUtxoError, RegistrationError etc. with stage fields for precise failure location. Retry guidance per stage.
## Import Path
```typescript theme={null}
import {
// Staged errors
EncryptedDepositError,
EncryptedWithdrawalError,
RegistrationError,
ConversionError,
CreateUtxoError,
FetchUtxosError,
ClaimUtxoError,
QueryError,
isEncryptedDepositError,
isEncryptedWithdrawalError,
isRegistrationError,
isConversionError,
isCreateUtxoError,
isFetchUtxosError,
isClaimUtxoError,
isQueryError,
// Base primitives
UmbraError,
CryptographyError,
InstructionError,
RpcError,
TransactionError,
TransactionSigningError,
MasterSeedSigningRejectedError,
isUmbraError,
isCryptographyError,
isInstructionError,
isRpcError,
isTransactionError,
isTransactionSigningError,
} from "@umbra-privacy/sdk/errors";
```
***
## Error Hierarchy
```
Error
└── UmbraError
├── CryptographyError
├── InstructionError
├── RpcError
├── TransactionError
│ └── TransactionSigningError
│ └── MasterSeedSigningRejectedError
├── EncryptedDepositError
├── EncryptedWithdrawalError
├── RegistrationError
├── ConversionError
├── CreateUtxoError
├── FetchUtxosError
├── ClaimUtxoError
└── QueryError
```
***
## Staged Errors
Every major SDK operation throws a dedicated staged error. The `stage` field pinpoints exactly where in the pipeline the failure occurred.
***
### EncryptedDepositError
Thrown by `getPublicBalanceToEncryptedBalanceDirectDepositorFunction`.
```typescript theme={null}
class EncryptedDepositError extends UmbraError {
readonly stage: EncryptedDepositStage;
}
function isEncryptedDepositError(error: unknown): error is EncryptedDepositError;
```
**Stage values:**
* `"initialization"` - Factory construction failed. Missing required arguments.
* `"validation"` - Invalid mint address, zero amount, or unregistered destination.
* `"mint-fetch"` - Failed to fetch the mint account. Check RPC and mint address.
* `"fee-calculation"` - Token-2022 transfer fee calculation failed. Epoch info issue.
* `"pda-derivation"` - Failed to derive required PDAs.
* `"account-fetch"` - Failed to fetch the destination user or token account.
* `"instruction-build"` - Failed to construct the instruction. Protocol state mismatch.
* `"transaction-build"` - Failed to assemble the transaction. Blockhash fetch failure.
* `"transaction-compile"` - Failed to compile the transaction. Lookup table mismatch.
* `"transaction-sign"` - Wallet rejected signing.
* `"transaction-validate"` - Pre-flight simulation failed. Insufficient funds or account state mismatch.
* `"transaction-send"` - Transaction sent but confirmation timed out. May still have landed.
***
### EncryptedWithdrawalError
Thrown by `getEncryptedBalanceToPublicBalanceDirectWithdrawerFunction`.
```typescript theme={null}
class EncryptedWithdrawalError extends UmbraError {
readonly stage: EncryptedWithdrawalStage;
}
function isEncryptedWithdrawalError(error: unknown): error is EncryptedWithdrawalError;
```
**Stage values:**
* `"initialization"` - Factory construction failed.
* `"validation"` - Zero amount or no encrypted balance exists for this mint.
* `"mint-fetch"` - Failed to fetch the mint account.
* `"pda-derivation"` - Failed to derive required PDAs.
* `"instruction-build"` - Failed to construct the instruction.
* `"transaction-build"` - Blockhash fetch failure.
* `"transaction-compile"` - Failed to compile the transaction.
* `"transaction-sign"` - Wallet rejected signing.
* `"transaction-send"` - Confirmation timed out. May still have landed.
***
### RegistrationError
Thrown by `getUserRegistrationFunction`.
```typescript theme={null}
class RegistrationError extends UmbraError {
readonly stage: RegistrationStage;
}
function isRegistrationError(error: unknown): error is RegistrationError;
```
**Stage values:**
* `"initialization"` - Factory construction failed.
* `"master-seed-derivation"` - User declined to sign the master seed derivation message.
* `"account-fetch"` - Failed to fetch on-chain account state. RPC connectivity issue.
* `"key-derivation"` - Cryptographic key derivation from master seed failed.
* `"zk-proof-generation"` - Groth16 proof generation failed (anonymous step only).
* `"pda-derivation"` - Failed to derive required PDAs.
* `"instruction-build"` - Failed to construct an instruction.
* `"transaction-build"` - Blockhash fetch failure.
* `"transaction-compile"` - Failed to compile the transaction.
* `"transaction-sign"` - Wallet rejected signing.
* `"transaction-validate"` - Pre-flight simulation failed.
* `"transaction-send"` - Confirmation timed out. May still have landed.
***
### ConversionError
Thrown by `getNetworkEncryptionToSharedEncryptionConverterFunction` and `getMintEncryptionKeyRotatorFunction`.
```typescript theme={null}
class ConversionError extends UmbraError {
readonly stage: ConversionStage;
}
function isConversionError(error: unknown): error is ConversionError;
```
**Stage values:**
* `"initialization"` - Factory construction failed.
* `"account-fetch"` - Failed to fetch token account state.
* `"pda-derivation"` - Failed to derive required PDAs.
* `"instruction-build"` - Failed to construct the conversion instruction.
* `"transaction-build"` - Blockhash fetch or transaction assembly failed.
* `"transaction-compile"` - Failed to compile the transaction.
* `"transaction-sign"` - Wallet rejected a per-mint transaction.
* `"transaction-validate"` - Pre-flight simulation failed.
* `"transaction-send"` - Confirmation timed out. Already-converted mints remain on-chain.
***
### CreateUtxoError
Thrown by all four UTXO creation factory functions.
```typescript theme={null}
class CreateUtxoError extends UmbraError {
readonly stage: CreateUtxoStage;
}
function isCreateUtxoError(error: unknown): error is CreateUtxoError;
```
**Stage values:**
* `"initialization"` - Factory construction failed.
* `"validation"` - Invalid recipient, mint, or amount.
* `"account-fetch"` - Failed to fetch the recipient's on-chain account.
* `"mint-fetch"` - Failed to fetch mint account data.
* `"fee-calculation"` - Token-2022 transfer fee calculation failed.
* `"key-derivation"` - Key derivation from master seed failed.
* `"zk-proof-generation"` - Groth16 proof generation failed.
* `"pda-derivation"` - Failed to derive required PDAs.
* `"instruction-build"` - Failed to construct the instruction.
* `"transaction-build"` - Blockhash fetch or transaction assembly failed.
* `"transaction-compile"` - Failed to compile the transaction.
* `"transaction-sign"` - Wallet rejected signing.
* `"transaction-validate"` - Pre-flight simulation failed.
* `"transaction-send"` - Confirmation timed out. May still have landed - check before retrying.
***
### FetchUtxosError
Thrown by `getClaimableUtxoScannerFunction`.
```typescript theme={null}
class FetchUtxosError extends UmbraError {
readonly stage: FetchUtxosStage;
}
function isFetchUtxosError(error: unknown): error is FetchUtxosError;
```
**Stage values:**
* `"initialization"` - Factory construction failed. `indexerApiEndpoint` not configured.
* `"validation"` - Invalid `treeIndex` or insertion index parameters.
* `"key-derivation"` - X25519 private key derivation from master seed failed.
* `"indexer-fetch"` - Indexer HTTP call failed (unreachable, rate-limited, or error response).
* `"proof-fetch"` - Merkle proof HTTP call failed.
***
### ClaimUtxoError
Thrown by all three claim factory functions.
```typescript theme={null}
class ClaimUtxoError extends UmbraError {
readonly stage: ClaimUtxoStage;
}
function isClaimUtxoError(error: unknown): error is ClaimUtxoError;
```
**Stage values:**
* `"initialization"` - Factory construction failed.
* `"validation"` - Invalid UTXO data or parameters.
* `"key-derivation"` - Key derivation from master seed failed.
* `"zk-proof-generation"` - Groth16 proof generation failed.
* `"pda-derivation"` - Failed to derive required PDAs.
* `"instruction-build"` - Failed to construct the instruction.
* `"transaction-build"` - Blockhash fetch or transaction assembly failed.
* `"transaction-compile"` - Failed to compile the transaction.
* `"transaction-sign"` - Wallet rejected signing.
* `"transaction-validate"` - Pre-flight simulation failed. Often indicates a stale Merkle proof - fetch fresh UTXOs.
* `"transaction-send"` - Confirmation timed out. Verify on-chain before retrying - the nullifier may have been burned.
***
### QueryError
Thrown by `getUserAccountQuerierFunction` and `getEncryptedBalanceQuerierFunction`.
```typescript theme={null}
class QueryError extends UmbraError {
readonly stage: QueryStage;
}
function isQueryError(error: unknown): error is QueryError;
```
**Stage values:**
* `"initialization"` - Factory-level validation failed.
* `"pda-derivation"` - PDA address generation failed.
* `"account-fetch"` - RPC fetch failed (node unreachable or returned an error).
* `"account-decode"` - On-chain account data could not be decoded. Unexpected on-chain state.
* `"key-derivation"` - X25519 key derivation failed (encrypted balance query only).
* `"decryption"` - Rescue cipher decryption failed (encrypted balance query only).
***
## Base Error Primitives
These are the underlying error types. All staged errors extend `UmbraError`.
***
### UmbraError
Base class for all SDK errors.
```typescript theme={null}
class UmbraError extends Error {
constructor(
message: string,
options?: {
code?: string;
context?: Record;
cause?: unknown;
},
);
readonly code: string; // Machine-readable code. Defaults to "UMBRA_ERROR".
readonly context?: Record;
override readonly cause?: unknown;
}
function isUmbraError(error: unknown): error is UmbraError;
```
***
### CryptographyError
Thrown when a cryptographic operation fails - most commonly ZK proof generation or Rescue cipher encryption/decryption.
```typescript theme={null}
class CryptographyError extends UmbraError {
constructor(
message: string,
options?: {
code?: string;
operation?: string;
context?: Record;
cause?: unknown;
},
);
readonly operation?: string; // Name of the failed operation, e.g. "groth16-prove".
}
function isCryptographyError(error: unknown): error is CryptographyError;
```
***
### InstructionError
Thrown when instruction construction fails - typically a protocol state mismatch or missing on-chain account.
```typescript theme={null}
class InstructionError extends UmbraError {
constructor(
message: string,
options?: {
code?: string;
instructionName?: string;
context?: Record;
cause?: unknown;
},
);
readonly instructionName?: string; // Name of the instruction that failed to build.
}
function isInstructionError(error: unknown): error is InstructionError;
```
***
### RpcError
Thrown when an RPC or indexer HTTP call fails.
```typescript theme={null}
class RpcError extends UmbraError {
constructor(
message: string,
options?: {
code?: string;
endpoint?: string;
statusCode?: number;
rpcErrorCode?: number;
context?: Record;
cause?: unknown;
},
);
readonly endpoint?: string; // The URL that was called.
readonly statusCode?: number; // HTTP status code (e.g. 429 for rate limit, 503 for unavailable).
readonly rpcErrorCode?: number; // JSON-RPC error code from the node.
}
function isRpcError(error: unknown): error is RpcError;
```
***
### TransactionError
Thrown when a transaction fails on-chain (simulation rejection or network rejection).
```typescript theme={null}
class TransactionError extends UmbraError {
constructor(
message: string,
options?: {
code?: string;
signature?: string;
simulationLogs?: string[];
context?: Record;
cause?: unknown;
},
);
readonly signature?: string; // Base58 tx signature. Present even on failure - tx may have landed.
readonly simulationLogs?: string[] // Program log output from pre-flight simulation.
}
function isTransactionError(error: unknown): error is TransactionError;
```
***
### TransactionSigningError
Thrown when a wallet rejects a transaction signing request. Extends `TransactionError`.
```typescript theme={null}
class TransactionSigningError extends TransactionError {
constructor(
message: string,
options?: {
code?: string;
wasRejected?: boolean;
signerAddress?: string;
signature?: string;
context?: Record;
cause?: unknown;
},
);
readonly wasRejected: boolean; // true = user explicitly cancelled; false = wallet error.
readonly signerAddress?: string; // Address of the wallet that rejected.
}
function isTransactionSigningError(error: unknown): error is TransactionSigningError;
```
***
### MasterSeedSigningRejectedError
Thrown when the user declines to sign the master seed derivation message. Extends `TransactionSigningError`. Always has `wasRejected: true`. Error code: `"MASTER_SEED_SIGNING_REJECTED"`.
This surfaces during the first operation that needs key material in a session - typically `register()`, or `getUmbraClient()` in non-deferred mode.
```typescript theme={null}
class MasterSeedSigningRejectedError extends TransactionSigningError {
constructor(
message?: string,
options?: {
signerAddress?: string;
context?: Record;
cause?: unknown;
},
);
}
```
***
## Retry Strategy
* `"transaction-sign"` / `MasterSeedSigningRejectedError` - **Do not retry.** User cancelled. Never auto-retry a wallet prompt rejection.
* `"transaction-send"` with a `signature` present - **Check first.** The transaction may have landed. Verify on-chain before submitting again to avoid double submissions.
* `"transaction-validate"` - **Fix inputs first.** Pre-flight simulation failed. Diagnose the root cause before retrying.
* `"transaction-validate"` for claims - May indicate a stale Merkle proof. Fetch fresh UTXOs before retrying.
* `isRpcError` - **Safe to retry with exponential backoff.** Transient network issue.
* All other stages - **Safe to retry with exponential backoff.**
# Mixer
Source: https://sdk.umbraprivacy.com/reference/mixer
API reference: getClaimableUtxoScannerFunction (ScannedUtxoResult) + 3 claim variants into encrypted or public balance with relayer and ZK prover deps.
## getClaimableUtxoScannerFunction
**Import:** `@umbra-privacy/sdk`
```typescript theme={null}
function getClaimableUtxoScannerFunction(
args: GetClaimableUtxoScannerFunctionArgs,
deps?: GetClaimableUtxoScannerFunctionDeps,
): ClaimableUtxoScannerFunction
```
Returns a function that discovers UTXOs claimable by the caller within a given insertion index range of a mixer tree. Queries the Umbra indexer to find UTXO ciphertexts, then attempts to decrypt them using the caller's X25519 private key and AES decryption. UTXOs that decrypt successfully are returned as claimable.
`client.indexerApiEndpoint` must be set (pass it to `getUmbraClient`).
***
### GetClaimableUtxoScannerFunctionArgs
* `client: IUmbraClient` - Must have `indexerApiEndpoint` configured.
### GetClaimableUtxoScannerFunctionDeps
* `fetchUtxoData?: FetchUtxoDataFunction` - Override the default indexer UTXO data fetcher.
* `fetchMerkleProof?: FetchMerkleProofFunction` - Override the default indexer Merkle proof fetcher.
* `aesDecryptor?: AesDecryptorFunction` - Override the AES-256-GCM decryptor used for receiver-claimable UTXOs.
* `x25519GetSharedSecret?: (privateKey: X25519PrivateKey, publicKey: X25519PublicKey) => Uint8Array` - Override the X25519 shared secret derivation.
### Returns
`ClaimableUtxoScannerFunction`
```typescript theme={null}
type ClaimableUtxoScannerFunction = (
treeIndex: U32,
startInsertionIndex: U32,
endInsertionIndex?: U32,
) => Promise
```
* `treeIndex: U32` - The mixer tree index to scan. Branded `U32` type.
* `startInsertionIndex: U32` - Inclusive start of the UTXO insertion range to scan. Branded `U32` type.
* `endInsertionIndex?: U32` - Exclusive end of the range. Defaults to the current tree size. Branded `U32` type.
### ScannedUtxoResult
```typescript theme={null}
interface ScannedUtxoResult {
selfBurnable: ClaimableUtxoData[];
received: ClaimableUtxoData[];
publicSelfBurnable: ClaimableUtxoData[];
publicReceived: ClaimableUtxoData[];
}
```
* `selfBurnable` - UTXOs you created yourself (from encrypted balance). You can burn these.
* `received` - UTXOs sent to you by others (from encrypted balance), addressed to the caller.
* `publicSelfBurnable` - UTXOs you created yourself (from public balance). You can burn these.
* `publicReceived` - UTXOs sent to you by others (from public balance), addressed to the caller.
### ClaimableUtxoData
Fields present on every claimable UTXO:
* `merkleRoot: U256LeBytes` - The Merkle root at the time this UTXO's proof was fetched (little-endian bytes).
* `merklePath: U256LeBytes[]` - Sibling hashes forming the Merkle inclusion proof (little-endian bytes).
* `leafIndex: U128` - The UTXO's leaf index in the Indexed Merkle Tree.
* `amount: U64` - Token amount in base units.
* `destinationAddress: Address` - The Umbra-registered owner who can claim this UTXO.
* `depositModifiedGenerationIndex: U128` - Generation index encoded in the deposit.
* `version: U64` - Protocol version of the UTXO.
* `commitmentIndex: U128` - Commitment index used in key derivation.
* `senderAddressLow: U128` - Low 128 bits of the sender address (for the ZK circuit).
* `senderAddressHigh: U128` - High 128 bits of the sender address.
* `relayerFixedSolFees: U64` - Fixed SOL fees paid to relayer (in lamports).
* `mintAddressLow: U128` - Low 128 bits of the mint address.
* `mintAddressHigh: U128` - High 128 bits of the mint address.
* `timestamp: TimestampComponents` - UTXO creation timestamp components for TVK derivation.
* `purpose: number` - Caller-defined purpose tag.
* `h1CircuitProvableOnChainDataHash: U256LeBytes` - H1 hash for the ZK circuit public inputs (little-endian bytes).
* `h1SmartProgramProvableOnChainDataHash: U256LeBytes` - H1 hash for the on-chain verifier (little-endian bytes).
***
### Errors
Throws `FetchUtxosError`. See [Errors](./errors#fetchutxoserror).
### Example
```typescript theme={null}
import { getClaimableUtxoScannerFunction } from "@umbra-privacy/sdk";
const scanUtxos = getClaimableUtxoScannerFunction({ client });
// Scan tree 0 from insertion index 0 to 1000
const result = await scanUtxos(0, 0, 1000);
console.log("Self-burnable UTXOs:", result.selfBurnable.length);
console.log("Received UTXOs:", result.received.length);
```
***
## getSelfClaimableUtxoToEncryptedBalanceClaimerFunction
**Import:** `@umbra-privacy/sdk`
```typescript theme={null}
function getSelfClaimableUtxoToEncryptedBalanceClaimerFunction(
args: GetSelfClaimableUtxoToEncryptedBalanceClaimerFunctionArgs,
deps: GetSelfClaimableUtxoToEncryptedBalanceClaimerFunctionDeps,
): SelfClaimableUtxoToEncryptedBalanceClaimerFunction
```
Claims a self-burnable UTXO (one you created yourself) and deposits its value into the caller's encrypted balance. Requires a Groth16 ZK proof - `deps.zkProver` is **required**. The nullifier is burned on-chain after a successful claim to prevent double-spending.
***
### GetSelfClaimableUtxoToEncryptedBalanceClaimerFunctionArgs
* `client: IUmbraClient`
### GetSelfClaimableUtxoToEncryptedBalanceClaimerFunctionDeps
* `zkProver: IZkProverForClaimSelfClaimableUtxo` - **Required.** Groth16 prover for the claim circuit.
* `accountInfoProvider?: AccountInfoProviderFunction`
* `blockhashProvider?: GetLatestBlockhash`
* `transactionForwarder?: TransactionForwarder`
* Additional cryptographic dependency overrides (key derivation, Poseidon, Rescue - all optional).
### Returns
`SelfClaimableUtxoToEncryptedBalanceClaimerFunction`
```typescript theme={null}
type SelfClaimableUtxoToEncryptedBalanceClaimerFunction = (
utxos: readonly ClaimableUtxoData[],
optionalData?: OptionalData32,
) => Promise
```
* `utxos: readonly ClaimableUtxoData[]` - Array of claimable UTXOs from `getClaimableUtxoScannerFunction`. Use UTXOs from `result.selfBurnable`.
* `optionalData?: OptionalData32` - Optional 32-byte caller metadata.
Returns `ClaimUtxoIntoEncryptedBalanceResult` with `signatures: Record` organized by batch index.
***
### Errors
Throws `ClaimUtxoError`. See [Errors](./errors#claimutxoerror). The `"transaction-validate"` stage often indicates a stale Merkle proof - fetch fresh UTXOs and retry.
***
## getSelfClaimableUtxoToPublicBalanceClaimerFunction
**Import:** `@umbra-privacy/sdk`
```typescript theme={null}
function getSelfClaimableUtxoToPublicBalanceClaimerFunction(
args: GetSelfClaimableUtxoToPublicBalanceClaimerFunctionArgs,
deps: GetSelfClaimableUtxoToPublicBalanceClaimerFunctionDeps,
): SelfClaimableUtxoToPublicBalanceClaimerFunction
```
Claims a self-burnable UTXO (one you created yourself) and sends its value to a public ATA. Same flow as `getSelfClaimableUtxoToEncryptedBalanceClaimerFunction` but the output goes to a public account instead of an encrypted balance. `deps.zkProver` is **required**.
### Returns
`SelfClaimableUtxoToPublicBalanceClaimerFunction`
```typescript theme={null}
type SelfClaimableUtxoToPublicBalanceClaimerFunction = (
utxos: readonly ClaimableUtxoData[],
optionalData?: OptionalData32,
) => Promise
```
* `utxos: readonly ClaimableUtxoData[]` - Array of claimable UTXOs. Use UTXOs from `result.selfBurnable` or `result.publicSelfBurnable`.
* `optionalData?: OptionalData32` - Optional 32-byte caller metadata.
Returns `ClaimUtxoIntoPublicBalanceResult` with `signatures: Record` organized by batch index.
### Errors
Throws `ClaimUtxoError`. See [Errors](./errors#claimutxoerror).
***
## getReceiverClaimableUtxoToEncryptedBalanceClaimerFunction
**Import:** `@umbra-privacy/sdk`
```typescript theme={null}
function getReceiverClaimableUtxoToEncryptedBalanceClaimerFunction(
args: GetReceiverClaimableUtxoToEncryptedBalanceClaimerFunctionArgs,
deps: GetReceiverClaimableUtxoToEncryptedBalanceClaimerFunctionDeps,
): ReceiverClaimableUtxoToEncryptedBalanceClaimerFunction
```
Claims a receiver-addressed UTXO (one sent to the caller by another user) and deposits its value into the caller's encrypted balance. `deps.zkProver` is **required**.
### Returns
`ReceiverClaimableUtxoToEncryptedBalanceClaimerFunction`
```typescript theme={null}
type ReceiverClaimableUtxoToEncryptedBalanceClaimerFunction = (
utxos: readonly ClaimableUtxoData[],
optionalData?: OptionalData32,
) => Promise
```
* `utxos: readonly ClaimableUtxoData[]` - Array of claimable UTXOs. Use UTXOs from `result.received` or `result.publicReceived`.
* `optionalData?: OptionalData32` - Optional 32-byte caller metadata.
Returns `ClaimUtxoIntoEncryptedBalanceResult` with `signatures: Record` organized by batch index.
### Errors
Throws `ClaimUtxoError`. See [Errors](./errors#claimutxoerror).
***
## Full Mixer Flow Example
```typescript theme={null}
import {
getClaimableUtxoScannerFunction,
getSelfClaimableUtxoToEncryptedBalanceClaimerFunction,
getReceiverClaimableUtxoToEncryptedBalanceClaimerFunction,
} from "@umbra-privacy/sdk";
import { getSelfClaimableUtxoToEncryptedBalanceClaimerProver, getReceiverClaimableUtxoToEncryptedBalanceClaimerProver } from "@umbra-privacy/web-zk-prover";
// Build functions (once)
const scanUtxos = getClaimableUtxoScannerFunction({ client });
const claimSelf = getSelfClaimableUtxoToEncryptedBalanceClaimerFunction(
{ client },
{ zkProver: getSelfClaimableUtxoToEncryptedBalanceClaimerProver() },
);
const claimReceived = getReceiverClaimableUtxoToEncryptedBalanceClaimerFunction(
{ client },
{ zkProver: getReceiverClaimableUtxoToEncryptedBalanceClaimerProver() },
);
// Fetch claimable UTXOs
const { selfBurnable, received } = await scanUtxos(0, 0, 1000);
// Claim each one
if (selfBurnable.length > 0) {
const result = await claimSelf(selfBurnable);
console.log("Claimed self UTXOs:", result.signatures);
}
if (received.length > 0) {
const result = await claimReceived(received);
console.log("Claimed received UTXOs:", result.signatures);
}
```
***
## FetchUtxosError
Thrown by `getClaimableUtxoScannerFunction`.
Stage values: `"initialization"` | `"validation"` | `"key-derivation"` | `"indexer-fetch"` | `"proof-fetch"`
See [Errors](./errors#fetchutxoserror) for full documentation.
***
## ClaimUtxoError
Thrown by all claim functions.
Stage values: `"initialization"` | `"validation"` | `"key-derivation"` | `"zk-proof-generation"` | `"pda-derivation"` | `"instruction-build"` | `"transaction-build"` | `"transaction-compile"` | `"transaction-sign"` | `"transaction-validate"` | `"transaction-send"`
See [Errors](./errors#claimutxoerror) for full documentation. Before retrying after `"transaction-send"`, verify on-chain - the nullifier may have been burned.
# SDK Reference
Source: https://sdk.umbraprivacy.com/reference/overview
Complete SDK reference index: all factory functions from @umbra-privacy/sdk, import paths (/types, /interfaces, /utils, /constants, /errors), naming conventions.
This reference covers the full public API surface of `@umbra-privacy/sdk`. Pages are organized by feature area - each section documents factory functions alongside their parameter types, result types, and associated errors.
## Import Paths
* `@umbra-privacy/sdk` - `getUmbraClient`, all service factory functions, cryptographic primitives
* `@umbra-privacy/sdk/errors` - All error classes, type guards, and stage union types
* `@umbra-privacy/sdk/types` - Branded primitive types and assertion functions
* `@umbra-privacy/sdk/interfaces` - TypeScript function type definitions
* `@umbra-privacy/sdk/utils` - Converter utilities, PDA derivation helpers, address utilities
* `@umbra-privacy/sdk/constants` - Network configs, program constants, domain separators
***
## Factory Function Pattern
Every SDK operation follows the same two-step pattern:
```typescript theme={null}
// Step 1 - build the function once (at setup time)
const operation = getOperationFunction({ client }, deps?);
// Step 2 - call it at runtime
const result = await operation(/* runtime arguments */);
```
The factory (`getOperationFunction`) captures configuration and optional dependency overrides. The returned function is what you call per-operation. The optional `deps` argument accepts injectable overrides for RPC providers, key generators, and ZK provers - useful for unit testing or custom infrastructure (e.g. Jito bundles, hardware key storage).
***
## Common Types
Types referenced across all pages:
* `Address` - Base58-encoded Solana public key string (`string` branded).
* `TransactionSignature` - Base58-encoded transaction signature string.
* `U64` - Branded `bigint` constrained to unsigned 64-bit range.
* `U128` - Branded `bigint` for unsigned 128-bit values.
* `U256` - Branded `bigint` for unsigned 256-bit values.
* `U512` - Branded `bigint` for unsigned 512-bit values.
* `X25519PublicKey` - 32-byte `Uint8Array` Diffie-Hellman public key.
* `RcEncryptionNonce` - Rescue cipher encryption nonce (branded `Uint8Array`).
* `OptionalData32` - 32-byte `Uint8Array` for caller-defined metadata attached to on-chain instructions. Defaults to 32 zero bytes when omitted.
* `TransactionCallbacks` - `{ pre?: (tx: CompiledTransaction) => Promise; post?: (tx: CompiledTransaction, sig: TransactionSignature) => Promise }`. Hooks invoked before signing and after confirmation.
* `IUmbraClient` - The client configuration object produced by `getUmbraClient`. Passed to every factory function as `args.client`.
* `Network` - `"mainnet" | "devnet" | "localnet"`.
***
## Pages in This Reference
* [Client](/reference/client) - `getUmbraClient`, `IUmbraClient`, `IUmbraSigner`
* [Registration](/reference/registration) - User registration, key rotation, seed updates, staged fund recovery
* [Deposit](/reference/deposit) - Direct deposit, self-claimable and receiver-claimable UTXO creation
* [Withdrawal](/reference/withdraw) - Direct withdrawal from encrypted balance to public balance
* [Query](/reference/query) - User account and encrypted balance queries
* [Conversion](/reference/conversion) - Convert to shared encryption, rotate per-mint X25519 key
* [Mixer](/reference/mixer) - Fetch claimable UTXOs, claim into encrypted or public balance
* [Compliance](/reference/compliance) - Compliance grants, re-encryption operations
* [Errors](/reference/errors) - All error classes, stage enums, type guards, and the error hierarchy
# Query
Source: https://sdk.umbraprivacy.com/reference/query
API reference: getEncryptedBalanceQuerierFunction (Rescue cipher decryption for shared-mode) + getUserAccountQuerierFunction (EncryptedUserAccount fields).
## getUserAccountQuerierFunction
**Import:** `@umbra-privacy/sdk`
```typescript theme={null}
function getUserAccountQuerierFunction(
args: GetUserAccountQuerierFunctionArgs,
deps?: GetUserAccountQuerierFunctionDeps,
): UserAccountQuerierFunction
```
Returns a function that fetches and decodes the on-chain user account for any Umbra address.
***
### GetUserAccountQuerierFunctionArgs
* `client: IUmbraClient`
### GetUserAccountQuerierFunctionDeps
* `accountInfoProvider?: AccountInfoProviderFunction`
### Returns
`UserAccountQuerierFunction`
```typescript theme={null}
type UserAccountQuerierFunction = (
userAddress: Address,
options?: { commitment?: Commitment },
) => Promise
```
### QueryUserAccountResult
A discriminated union:
* `{ state: "exists"; data: EncryptedUserAccount }` - The user is registered and the account was decoded.
* `{ state: "non_existent" }` - No user account exists at this address.
### EncryptedUserAccount
Fields of the decoded on-chain account:
* `versionByte: U8` - Schema version.
* `canonicalBump: U8` - PDA canonical bump.
* `isInitialised: boolean` - Whether the account has been initialised.
* `isActiveForAnonymousUsage: boolean` - Whether anonymous (mixer) usage is enabled.
* `isUserCommitmentRegistered: boolean` - Whether the Poseidon commitment has been registered.
* `isUserAccountX25519KeyRegistered: boolean` - Whether the X25519 public key has been registered.
* `x25519PublicKey: X25519PublicKey` - The user's X25519 public key (used for UTXO encryption).
* `userCommitment: PoseidonHash` - The user's on-chain Poseidon commitment.
* `generationIndex: U128` - Current generation index used in key derivation.
* `randomGenerationSeed: U256LeBytes` - Current random generation seed.
***
### Errors
Throws `QueryError`. See [Errors](./errors#queryerror).
### Example
```typescript theme={null}
import { getUserAccountQuerierFunction } from "@umbra-privacy/sdk";
const queryUserAccount = getUserAccountQuerierFunction({ client });
const result = await queryUserAccount(userAddress);
if (result.state === "exists") {
console.log("X25519 key:", result.data.x25519PublicKey);
console.log("Registered for anonymous usage:", result.data.isActiveForAnonymousUsage);
} else {
console.log("User not registered.");
}
```
***
## getEncryptedBalanceQuerierFunction
**Import:** `@umbra-privacy/sdk`
```typescript theme={null}
function getEncryptedBalanceQuerierFunction(
args: GetEncryptedBalanceQuerierFunctionArgs,
deps?: GetEncryptedBalanceQuerierFunctionDeps,
): EncryptedBalanceQuerierFunction
```
Returns a function that fetches and decrypts encrypted token balances for a set of mints. For shared-mode accounts, the balance is decrypted locally using the caller's X25519 private key. For MXE-mode accounts, the ciphertext cannot be decrypted client-side.
***
### GetEncryptedBalanceQuerierFunctionArgs
* `client: IUmbraClient`
### GetEncryptedBalanceQuerierFunctionDeps
* `accountInfoProvider?: AccountInfoProviderFunction`
* `rcDecryptor?: RcDecryptorFunction` - Override the Rescue cipher decryptor used for shared-mode balance decryption.
### Returns
`EncryptedBalanceQuerierFunction`
```typescript theme={null}
type EncryptedBalanceQuerierFunction = (
mints: readonly Address[],
options?: { commitment?: Commitment },
) => Promise