> For AI agents: the documentation index is at https://sdk.nimbusprivacy.xyz/llms.txt, and every page is available as Markdown at its URL with .md appended.

# Deposit (/deposit)

A deposit moves tokens from your user's wallet into a private balance. It is the first payment
every user makes, and the only one their wallet signs.

In this guide, Alice deposits 25 USDC into her own private balance. Then she deposits 5 USDC
straight into Bob's.

## What's public

A deposit looks like an ordinary transfer into the pool. Because Alice registered her wallet, it
can be tied to her. Privacy starts once the tokens are inside.

## Deposit into your user's balance

Create a deposit context from the session, then call `deposit`.

```ts
import { address } from "@solana/kit";
import { createDepositContext, deposit } from "@umbra/nimbus-solana-web";

const usdc = address("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");

const context = await createDepositContext(session);
const result = await deposit(
  context,
  { mint: usdc, amount: 25_000_000n, recipient: session.walletAddress },
  { transactionVersion },
);
```

- **`amount`** is in the token's smallest unit. USDC has 6 decimals, so 25 USDC is `25_000_000n`.
  [Tokens](https://sdk.nimbusprivacy.xyz/helpers/tokens) shows how to read any token's decimals.
- **`recipient`** is the wallet whose private balance receives the deposit. Here it is Alice's own.
- **`transactionVersion`** is what the wallet can sign. `openWalletConnection` tells you; see
  [Connect a wallet](https://sdk.nimbusprivacy.xyz/helpers/wallets).

Alice's wallet asks her to approve one transaction. She pays its network fee from her wallet.

### The protocol fee

The protocol fee comes out of the amount. When Alice deposits 25 USDC, her private balance grows
by 25 USDC less the fee.

To charge the fee on top instead, pass `fees: "added"`. The fee is then paid from the private
balance Alice already holds in that token. If she holds too little, the deposit throws before
anything is signed.

## Deposit into another user's balance

Alice can pay Bob directly from her wallet. Pass Bob's wallet address as `recipient`.

```ts
import { deposit } from "@umbra/nimbus-solana-web";

const result = await deposit(context, {
  mint: usdc,
  amount: 5_000_000n,
  recipient: bob,
});
```

The 5 USDC arrive in Bob's private balance, not Alice's. Bob sees them the next time his device
syncs.

Bob must have [registered](https://sdk.nimbusprivacy.xyz/registration) first. If he has not, `deposit` throws a
`NimbusSdkError` with reason `recipient-unregistered`, and Alice's wallet is never asked to sign.

## Handle the result

`deposit` returns a result instead of throwing for anything your user can cause. Check its `kind`:

| `kind`                                               | What happened                                                | What to do                                |
| ---------------------------------------------------- | ------------------------------------------------------------ | ----------------------------------------- |
| `submitted`                                          | The deposit was sent to Solana.                              | Read where it ended up, below.            |
| `declined`                                           | Alice rejected it in her wallet. Nothing was sent.           | Let her try again.                        |
| `store-refused`, `intent-refused`, `attempt-refused` | This browser could not record the deposit. Nothing was sent. | Ask her to reload the page and try again. |

A `submitted` result carries the transaction's fate in `result.submission.submission.kind`:

| `kind`                 | What it means                                                                                                           |
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `landed`               | Done. The private balance holds the deposit.                                                                            |
| `failed`               | It reached Solana and failed. Only the network fee was charged.                                                         |
| `expired` or `refused` | It never reached Solana. Nothing moved, and it is safe to try again.                                                    |
| `unknown`              | The SDK stopped waiting before Solana decided. See [If the deposit is still pending](#if-the-deposit-is-still-pending). |

Here is the whole check in one function. TypeScript checks that it covers every case.

```ts
function describeDeposit(result: DepositResult): string {
  switch (result.kind) {
    case "submitted":
      switch (result.submission.submission.kind) {
        case "landed":
          return "Deposited.";
        case "failed":
          return "The deposit failed on Solana. Only the network fee was charged.";
        case "expired":
        case "refused":
          return "The deposit never reached Solana. Try again.";
        case "unknown":
          return "The deposit is still pending.";
      }
    case "declined":
      return "You declined the deposit in your wallet.";
    case "store-refused":
    case "intent-refused":
    case "attempt-refused":
      return "This browser could not record the deposit. Reload and try again.";
    case "funding-failed":
    case "funding-unknown":
    case "deposit-unsendable":
      return "See Wallets that sign only version 0.";
  }
}
```

The last three kinds happen only with older wallets. They are covered in
[Wallets that sign only version 0](#wallets-that-sign-only-version-0).

## Show progress

A deposit takes a few seconds, most of it building the proof. Pass `onProgress` to tell your user
what is happening:

```ts
import { deposit } from "@umbra/nimbus-solana-web";

await deposit(
  context,
  { mint: usdc, amount: 25_000_000n, recipient },
  { onProgress: (event) => showStatus(event.stage) },
);
```

[Show progress](https://sdk.nimbusprivacy.xyz/api-design/progress) lists every stage with a message you can show as it is.

## If the deposit is still pending

Sometimes the SDK stops waiting before Solana settles a deposit. The result is `submitted`, and its
submission's kind is `unknown`. Do not deposit the same funds again yet: call
`resolvePendingDeposit` to learn how it ended. [Pending and stranded deposits](https://sdk.nimbusprivacy.xyz/deposit/pending)
shows how, including across a page reload.

## Wallets that sign only version 0

A wallet that signs only version 0 needs two transactions, and Alice approves both. The first moves
the funds to her platform wallet, a second address the SDK derives for her, and the second deposits
them. When they do not both go through, the result is `funding-failed`, `funding-unknown` or
`deposit-unsendable`. [Pending and stranded deposits](https://sdk.nimbusprivacy.xyz/deposit/pending#wallets-that-sign-only-version-0)
says when to try again and when to refund.

## Common errors

These are thrown, not returned, because your app usually cannot fix them by asking the user.

| Error                   | Reason                   | Cause                                                                                                                            |
| ----------------------- | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------- |
| `NimbusSdkError`        | `recipient-unregistered` | The recipient has not registered.                                                                                                |
| `NimbusSdkError`        | `account-unregistered`   | Alice has not registered. Run `register` first.                                                                                  |
| `NimbusSolanaFlowError` | `context-mismatched`     | A Nimbus service answered with data that does not fit the deposit. Try again.                                                    |
| `NimbusSolanaFlowError` | `mint-unsupported`       | The address is not an SPL or Token-2022 mint.                                                                                    |
| `NimbusSolanaFlowError` | `amount-unfundable`      | The amount is too small to cover the fee, or the private balance cannot pay an added fee.                                        |
| `NimbusSolanaFlowError` | `fee-changing`           | The token's transfer fee changes at the next epoch. Try again after it turns.                                                    |
| `NimbusSolanaFlowError` | `fee-payer-unfunded`     | Alice's wallet holds too little SOL for the fee, the SOL it deposits, or a new token account's rent. Nothing was proved or sent. |
| `NimbusSdkError`        | `service-unavailable`    | Solana or a Nimbus service could not be reached. Try again.                                                                      |

[Handle errors](https://sdk.nimbusprivacy.xyz/api-design/results-and-errors) shows one function that covers every error the SDK throws.

## Options

| Option                | Default       | What it does                                                         |
| --------------------- | ------------- | -------------------------------------------------------------------- |
| `transactionVersion`  | `1`           | The transaction version the wallet signs.                            |
| `fees`                | `"deducted"`  | `"added"` charges the protocol fee on top of `amount`.               |
| `onProgress`          | none          | Called at each stage of the deposit.                                 |
| `sync`                | `true`        | Brings the device up to date first. Pass `false` if you just synced. |
| `commitment`          | `"confirmed"` | `"finalized"` waits until the deposit cannot be rolled back.         |
| `priorityFeeLamports` | `0n`          | A priority fee, in lamports, to land faster when Solana is busy.     |

The [API reference](https://sdk.nimbusprivacy.xyz/reference/deposit#deposit) lists every option.

## Next

- [Private Send](https://sdk.nimbusprivacy.xyz/transfer): Pay another user from the private balance.
- [Show balances](https://sdk.nimbusprivacy.xyz/helpers/balances): Read what the private balance holds.
- [Handle errors](https://sdk.nimbusprivacy.xyz/api-design/results-and-errors): Cover every result and error in one place.


Last updated 21 October 2026
