Skip to content
Deposit

Deposit

Move tokens from your user's wallet into a private balance, theirs or another user's.

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

Anyone can see
  • Alice's wallet
  • The token: USDC
  • The amount: 25 USDC
Stays private
  • Whose private balance receives it

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.

import {  } from "@solana/kit";
import { ,  } from "@umbra/nimbus-solana-web";

const  = ("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");

const  = await ();
const  = await (
  ,
  { : , : 25_000_000n, : . },
  {  },
);
  • amount is in the token's smallest unit. USDC has 6 decimals, so 25 USDC is 25_000_000n. 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.

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.

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

const  = await (, {
  : ,
  : 5_000_000n,
  : ,
});

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

kindWhat happenedWhat to do
submittedThe deposit was sent to Solana.Read where it ended up, below.
declinedAlice rejected it in her wallet. Nothing was sent.Let her try again.
store-refused, intent-refused, attempt-refusedThis 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:

kindWhat it means
landedDone. The private balance holds the deposit.
failedIt reached Solana and failed. Only the network fee was charged.
expired or refusedIt never reached Solana. Nothing moved, and it is safe to try again.
unknownThe SDK stopped waiting before Solana decided. See If the deposit is still pending.

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

function (: ): string {
  switch (.) {
    case "submitted":
      switch (...) {
        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.

Show progress

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

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

await (
  ,
  { : , : 25_000_000n,  },
  { : () => (.) },
);

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

ErrorReasonCause
NimbusSdkErrorrecipient-unregisteredThe recipient has not registered.
NimbusSdkErroraccount-unregisteredAlice has not registered. Run register first.
NimbusSolanaFlowErrorcontext-mismatchedA Nimbus service answered with data that does not fit the deposit. Try again.
NimbusSolanaFlowErrormint-unsupportedThe address is not an SPL or Token-2022 mint.
NimbusSolanaFlowErroramount-unfundableThe amount is too small to cover the fee, or the private balance cannot pay an added fee.
NimbusSolanaFlowErrorfee-changingThe token's transfer fee changes at the next epoch. Try again after it turns.
NimbusSolanaFlowErrorfee-payer-unfundedAlice'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.
NimbusSdkErrorservice-unavailableSolana or a Nimbus service could not be reached. Try again.

Handle errors shows one function that covers every error the SDK throws.

Options

OptionDefaultWhat it does
transactionVersion1The transaction version the wallet signs.
fees"deducted""added" charges the protocol fee on top of amount.
onProgressnoneCalled at each stage of the deposit.
synctrueBrings the device up to date first. Pass false if you just synced.
commitment"confirmed""finalized" waits until the deposit cannot be rolled back.
priorityFeeLamports0nA priority fee, in lamports, to land faster when Solana is busy.

The API reference lists every option.

Next

Last updated

On this page