Skip to main content

Overview

The withdraw operation is the reverse of a deposit. It moves tokens from a user’s encrypted balance back to their public Associated Token Account (ATA).
Encrypted Balance (on-chain)
          |
          v
  Umbra Vault (on-chain SPL)  --withdraw-->  Your Public ATA
Arcium MPC verifies the withdrawal authorization. The operation follows the dual-instruction pattern - the SDK waits for both the handler and the callback transactions to confirm before returning.

Usage

import { getEncryptedBalanceToPublicBalanceDirectWithdrawerFunction } from "@umbra-privacy/sdk";

const withdraw = getEncryptedBalanceToPublicBalanceDirectWithdrawerFunction({ client });

const result = await withdraw(
  destinationAddress, // where tokens go (typically client.signer.address)
  mint,               // SPL or Token-2022 mint address
  amount,             // amount in native token units (U64)
  options?,           // optional
);

Parameters

destinationAddress
Address
required
The Solana address to receive the withdrawn tokens. This is typically client.signer.address.
mint
Address
required
The SPL or Token-2022 mint address. Must match a mint for which the user has an existing encrypted balance.
amount
bigint
required
The amount to withdraw, in the token’s native units. Must not exceed the current encrypted balance.
options.priorityFees
bigint
default:"0n"
Additional compute unit price in microlamports. Recommended during high network load.
options.optionalData
Uint8Array
32 bytes of arbitrary metadata stored with the withdrawal. Defaults to all zeros.
options.awaitCallback
boolean
default:"true"
Whether to wait for the Arcium MPC callback transaction to confirm before resolving. When true, the returned WithdrawResult includes callbackSignature and callbackElapsedMs. When false, returns immediately after the handler (queue) transaction confirms.
options.skipPreflight
boolean
default:"false"
Skip Solana transaction preflight simulation.
options.maxRetries
number
Maximum number of times the RPC node should retry sending the transaction.
options.accountInfoCommitment
Commitment
default:"\"confirmed\""
Commitment level used for RPC account reads during withdrawal preparation. Overrides the default on a per-call basis.
options.epochInfoCommitment
Commitment
default:"\"confirmed\""
Commitment level used for fetching epoch info (Token-2022 transfer fee schedule). Overrides the default on a per-call basis.

Return Value

Returns a Promise<WithdrawResult>. The WithdrawResult object contains:
  • queueSignature - The transaction signature of the handler (queue computation) transaction.
  • callbackStatus - The outcome of computation monitoring: "finalized", "pruned", or "timed-out". Present when awaitCallback is true (the default).
  • callbackSignature - The transaction signature of the Arcium MPC callback transaction. Present when callbackStatus is "finalized".
  • callbackElapsedMs - Wall-clock milliseconds spent waiting for the callback. Present when awaitCallback is true.
  • rentClaimSignature - Transaction signature for reclaiming rent from the computation account. Attempted regardless of callback outcome.
  • rentClaimError - Error encountered during rent reclaim, if any. The withdrawal itself still succeeded.

Example

import { getEncryptedBalanceToPublicBalanceDirectWithdrawerFunction } from "@umbra-privacy/sdk";

const withdraw = getEncryptedBalanceToPublicBalanceDirectWithdrawerFunction({ client });

const USDC = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";

const result = await withdraw(client.signer.address, USDC, 50_000_000n); // 50 USDC
console.log("Queue signature:", result.queueSignature);
console.log("Callback signature:", result.callbackSignature);

Withdrawal Destination

The tokens are sent to the ATA associated with the destinationAddress for the given mint. If that ATA does not exist, the transaction will fail with an account-not-found error. Create the ATA first using standard token tooling if needed.

Error Handling

Withdrawal errors are typed with a stage field identifying exactly where in the pipeline the failure occurred:
import { isEncryptedWithdrawalError } from "@umbra-privacy/sdk/errors";

try {
  const result = await withdraw(client.signer.address, mint, amount);
} catch (err) {
  if (isEncryptedWithdrawalError(err)) {
    switch (err.stage) {
      case "validation":
        // Zero amount, or no encrypted balance exists for this mint.
        console.error("Withdrawal validation failed:", err.message);
        break;

      case "mint-fetch":
        // Could not fetch the mint account - check RPC connectivity and mint address.
        break;

      case "pda-derivation":
        // Could not derive required program addresses - unexpected on-chain state.
        break;

      case "instruction-build":
        // Could not construct the instruction - protocol state mismatch.
        break;

      case "transaction-sign":
        // User rejected the transaction in their wallet.
        showNotification("Withdrawal cancelled.");
        break;

      case "transaction-send":
        // Transaction submitted but confirmation timed out.
        // The transaction may have landed - check on-chain before retrying.
        console.warn("Confirmation timeout. Check signature on-chain:", err.cause);
        break;

      default:
        // Other stages: initialization, transaction-build, transaction-compile.
        console.error("Withdrawal failed at stage:", err.stage, err);
    }
  } else {
    throw err;
  }
}
See Error Handling for retry guidance and a full stage reference.

Timing

Withdrawals follow the dual-instruction pattern. The SDK submits the handler transaction, waits for Arcium to complete the MPC computation, then waits for the callback transaction to confirm. Total wall-clock time is typically a few seconds on a well-connected network.