Skip to main content
All Stealth Pool factories live under @umbra-privacy/sdk/burn.

getBurnableStealthPoolNoteScannerFunction

Import: @umbra-privacy/sdk/burn
function getBurnableStealthPoolNoteScannerFunction(
  args: { client: IUmbraClient },
  deps?: GetBurnableStealthPoolNoteScannerFunctionDeps,
): BurnableStealthPoolNoteScannerFunction;
Returns a zero-arg scanner. When called, it discovers every active Stealth Pool tree, loads scan progress from client.utxoDataStore, fetches missing ranges from the indexer, decrypts ciphertexts addressable by your viewing keys, persists progress, and returns the result grouped by (kind, source). client.indexerApiEndpoint must be set (pass it to getUmbraClient).

Deps

  • fetchStealthPoolNoteData?: StealthPoolNoteDataFetcherFunction — override the default indexer note-data fetcher.
  • fetchMerkleProof?: MerkleProofFetcherFunction — override the single-leaf Merkle proof fetcher used internally for sanity checks. The burner factory uses its own fetchBatchMerkleProof dep at submit time.
  • aesDecryptor?: AesDecryptorFunction — override the AES-GCM decryptor used to decode receiver-burnable note ciphertexts.
  • x25519GetPublicKey? / x25519GetSharedSecret? — override X25519 ECDH primitives.
  • additionalX25519PrivateKeys?: readonly X25519PrivateKey[] — multi-key wallets can scan against extra X25519 keys.

Returns

type BurnableStealthPoolNoteScannerFunction = () => Promise<ScannedStealthPoolNoteResult>;

ScannedStealthPoolNoteResult

interface ScannedStealthPoolNoteResult {
  readonly etaToStealthPoolSelfBurnable: readonly DecryptedStealthPoolNoteData[];
  readonly etaToStealthPoolReceiverBurnable: readonly DecryptedStealthPoolNoteData[];
  readonly ataToStealthPoolSelfBurnable: readonly DecryptedStealthPoolNoteData[];
  readonly ataToStealthPoolReceiverBurnable: readonly DecryptedStealthPoolNoteData[];
  readonly scannedTrees: readonly ScannedTreeProgress[];
}

interface ScannedTreeProgress {
  readonly treeIndex: U128;
  readonly scannedRange: { start: U32; end: U32 } | null;
  readonly totalLeaves: U32;
  readonly fullyScanned: boolean;
}
Notes are not proof-bundled — proofs are fetched per batch at burn time.

Errors

Throws FetchUtxosError (named with the legacy noun; class name retained for backwards compatibility — the error itself covers V18 Stealth Pool Note scanning). See Errors.

Example

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

const scan   = getBurnableStealthPoolNoteScannerFunction({ client });
const result = await scan();

console.log("Self-burnable from ETA:", result.etaToStealthPoolSelfBurnable.length);

getReceiverBurnableStealthPoolNoteIntoETABurnerFunction

Import: @umbra-privacy/sdk/burn
function getReceiverBurnableStealthPoolNoteIntoETABurnerFunction(
  args: { client: IUmbraClient },
  deps: {
    fetchBatchMerkleProof: BatchMerkleProofFetcherFunction;
    zkProver: IZkProverForBurnReceiverBurnableStealthPoolNoteIntoEncryptedTokenAccount;
    relayer: {
      submitBurn: BurnSubmitterFunction;
      pollBurnStatus: BurnStatusPollerFunction;
      getRelayerAddress: () => Promise<Address>;
    };
    hooks?: BurnHooks;
    awaitCompletion?: boolean;        // default true
    pollingIntervalMs?: number;       // default 3000
    timeoutMs?: number;               // default 120_000
    /* additional optional crypto + RPC + key-derivation overrides */
  },
): ReceiverBurnableStealthPoolNoteIntoETABurnerFunction;
Burns receiver-burnable notes into the caller’s ETA. Natively batches: groups by destinationAddress, chunks to ≤5 per proof.

Required deps

  • fetchBatchMerkleProof — typically client.fetchBatchMerkleProof (auto-wired when indexerApiEndpoint is set on the client).
  • zkProvergetClaimReceiverClaimableUtxoIntoEncryptedBalanceProver() from @umbra-privacy/sdk/zk-prover. (The interface type is aliased internally as IZkProverForBurnReceiverBurnableStealthPoolNoteIntoEncryptedTokenAccount — the underlying type is identical.)
  • relayer — adapter built from getUmbraRelayer({ apiEndpoint }). BurnSubmitterFunction and BurnStatusPollerFunction are V18 TypeScript aliases of ClaimSubmitterFunction and ClaimStatusPollerFunction, so the relayer client’s submitClaim / pollClaimStatus plug in directly under the renamed property names.

Returns

type ReceiverBurnableStealthPoolNoteIntoETABurnerFunction = (
  stealthPoolNotes: readonly (DecryptedStealthPoolNoteData & { kind: "receiver-burnable" })[],
  optionalData?: OptionalData32,
  microLamportsPerAcu?: MicroLamportsPerAcu,
) => Promise<BurnStealthPoolNoteIntoETAResult>;

interface BurnStealthPoolNoteIntoETAResult {
  readonly signatures: readonly TransactionSignature[]; // every tx submitted (from OperationOutcome)
  readonly batches: Map<U32, BurnBatchResult>;          // keyed by batch index
}

interface BurnBatchResult {
  readonly requestId: string;                  // relayer-assigned tracking ID
  readonly status: BurnStatus;
  readonly txSignature?: string;               // on-chain burn tx (when landed)
  readonly callbackSignature?: string;         // MPC callback tx (when finalised)
  readonly resolvedVariant?: string;           // e.g. "claim_into_existing_shared_balance_v18"
  readonly failureReason?: string | null;
  readonly stealthPoolNoteIds?: readonly string[]; // "treeIndex:leafIndex" pairs
}

type BurnStatus =
  | "received" | "validating" | "offsets_reserved" | "building_tx" | "tx_built"
  | "submitting" | "submitted" | "awaiting_callback" | "callback_received"
  | "finalizing" | "completed" | "failed" | "timed_out" | "refunded";
completed / callback_received indicate success. The signature to display is callbackSignature ?? txSignature. failureReason containing "NullifierAlreadyBurnt" means the note was already burnt — the SDK treats this as idempotent success.

Errors

Throws ClaimUtxoError. See Errors. The "transaction-validate" stage often indicates a stale Merkle proof — the burner refetches on retry.

getSelfBurnableStealthPoolNoteIntoETABurnerFunction

Import: @umbra-privacy/sdk/burn Same shape as the receiver variant. MAX_NOTES_PER_PROOF = 1 — the SDK loops internally; caller still passes an array. zkProver is IZkProverForClaimSelfClaimableUtxoIntoEncryptedBalance — use getClaimSelfClaimableUtxoIntoEncryptedBalanceProver from @umbra-privacy/sdk/zk-prover.

getSelfBurnableStealthPoolNoteIntoATABurnerFunction

Import: @umbra-privacy/sdk/burn Same shape; tokens released to the destination ATA instead of an ETA. zkProver is IZkProverForClaimSelfClaimableUtxoIntoPublicBalance — use getClaimSelfClaimableUtxoIntoPublicBalanceProver from @umbra-privacy/sdk/zk-prover.
Receiver-burnable → ATA exists on-chain but is not yet shipped in the SDK. Use receiver-burnable → ETA then a regular withdrawal until the SDK adds the direct path.

Full scan + burn example

import {
  getBurnableStealthPoolNoteScannerFunction,
  getReceiverBurnableStealthPoolNoteIntoETABurnerFunction,
  getSelfBurnableStealthPoolNoteIntoETABurnerFunction,
} from "@umbra-privacy/sdk/burn";
import { getUmbraRelayer } from "@umbra-privacy/sdk";
import {
  getClaimReceiverClaimableUtxoIntoEncryptedBalanceProver,
  getClaimSelfClaimableUtxoIntoEncryptedBalanceProver,
} from "@umbra-privacy/sdk/zk-prover";

const r = getUmbraRelayer({ apiEndpoint: "https://relayer.api.umbraprivacy.com" });
const relayerDeps = {
  submitBurn:        r.submitClaim,
  pollBurnStatus:    r.pollClaimStatus,
  getRelayerAddress: r.getRelayerAddress,
};

const scan = getBurnableStealthPoolNoteScannerFunction({ client });

const burnReceiverIntoEta = getReceiverBurnableStealthPoolNoteIntoETABurnerFunction(
  { client },
  {
    fetchBatchMerkleProof: client.fetchBatchMerkleProof!,
    zkProver: getClaimReceiverClaimableUtxoIntoEncryptedBalanceProver(),
    relayer:  relayerDeps,
  },
);

const burnSelfIntoEta = getSelfBurnableStealthPoolNoteIntoETABurnerFunction(
  { client },
  {
    fetchBatchMerkleProof: client.fetchBatchMerkleProof!,
    zkProver: getClaimSelfClaimableUtxoIntoEncryptedBalanceProver(),
    relayer:  relayerDeps,
  },
);

const result = await scan();
if (result.ataToStealthPoolReceiverBurnable.length > 0) {
  const out = await burnReceiverIntoEta(result.ataToStealthPoolReceiverBurnable);
  for (const [batchIndex, batch] of out.batches) {
    console.log("Batch", batchIndex, "status:", batch.status, "id:", batch.requestId);
  }
}
if (result.etaToStealthPoolSelfBurnable.length > 0) {
  await burnSelfIntoEta(result.etaToStealthPoolSelfBurnable);
}

FetchUtxosError

Thrown by getBurnableStealthPoolNoteScannerFunction. Stage values: "initialization" | "validation" | "key-derivation" | "indexer-fetch" | "proof-fetch" | "proof-enrichment". See Errors.

ClaimUtxoError

Thrown by all burner factories. 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. Before retrying after "transaction-send", verify on-chain — the nullifier may have been burnt. The burner itself treats NullifierAlreadyBurnt as idempotent success in BurnBatchOutcome.failureReason.