> ## Documentation Index
> Fetch the complete documentation index at: https://sdk.umbraprivacy.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Stealth Pool (Mixer)

> V18 API reference: getBurnableStealthPoolNoteScannerFunction (zero-arg) + 3 burner factories (receiver→ETA, self→ETA, self→ATA) with fetchBatchMerkleProof, ZK prover, and relayer deps.

All Stealth Pool factories live under `@umbra-privacy/sdk/burn`.

***

## getBurnableStealthPoolNoteScannerFunction

**Import:** `@umbra-privacy/sdk/burn`

```typescript theme={null}
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

```typescript theme={null}
type BurnableStealthPoolNoteScannerFunction = () => Promise<ScannedStealthPoolNoteResult>;
```

### ScannedStealthPoolNoteResult

```typescript theme={null}
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](./errors#fetchutxoserror).

### Example

```typescript theme={null}
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`

```typescript theme={null}
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).
* **`zkProver`** — `getClaimReceiverClaimableUtxoIntoEncryptedBalanceProver()` 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

```typescript theme={null}
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](./errors#claimutxoerror). 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`.

<Note>
  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.
</Note>

***

## Full scan + burn example

```typescript theme={null}
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](./errors#fetchutxoserror).

***

## 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](./errors#claimutxoerror). 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`.
