notes
Interfaces
TokenBalance
A token-grouped balance line as the SDK reports it.
Properties
AvailableBalanceOpts
Properties
ClaimPackage
Per-recipient claim payload sent from the operator who settled
the run to each recipient. Carries the per-claim secret + the
inclusion proof needed to claim against PrivateSettlement.
Wire format: JSON-serializable strings (decimal-encoded bigints) so the package can travel through URLs, QR codes, email bodies, etc. without per-consumer BigInt-awareness. Decoding into native bigints happens once at the recipient before proving.
Properties
PendingDepositNote
The fields assessDepositRetry reads off a vault note. A
StoredNote satisfies this structurally, so callers pass notes
straight through.
Properties
MinimalReceipt
A tx receipt, reduced to the one field the guard needs.
Properties
| Property | Type | Description |
|---|---|---|
status | number | null | 1 mined-ok, 0 reverted, null for ethers’ pre-confirmation shape. |
RetryGuardDeps
Properties
| Property | Type | Description |
|---|---|---|
refreshTree | () => void | Force one commitment-tree re-hydrate. Fire-and-forget (the SDK’s tree.refresh returns void); the tree mutates its shared index in place, so we poll RetryGuardDeps.findIndex afterwards. |
findIndex | (commitment) => number | Synchronous lookup against the in-memory tree index. >= 0 means the commitment has landed on-chain. The tree verifies its own hydration (rejecting forked / out-of-sync leaf sets), so a hit is trustworthy positive evidence. |
getReceipt? | (txHash) => Promise<MinimalReceipt | null> | Fetch a tx receipt. null means either still-pending or dropped/unknown — ethers can’t tell them apart from the receipt alone, so RetryGuardDeps.getTransaction disambiguates. Only called for notes that carry a txHash. Optional: when absent (no wallet provider), the guard falls back to tree-only evidence. |
getTransaction? | (txHash) => Promise<unknown> | Fetch the tx itself. null = the node doesn’t know this hash — i.e. it was dropped from the mempool or never broadcast, so a retry must be allowed rather than blocked forever. A non-null result with a null receipt is a genuine mempool-pending tx → block. Optional; without it a null receipt is treated as ambiguous. |
signal? | AbortSignal | Abort the recheck early (e.g. the user hit Cancel). When aborted the guard stops polling and returns block: false; the caller is responsible for noticing the abort and abandoning the deposit rather than proceeding. |
sleep? | (ms) => Promise<void> | Injectable sleep — overridden in tests to avoid real timers. |
RetryGuardResult
Properties
IndexedDbAdapterOpts
Properties
NoteCipher
encrypt/decrypt pair consumable by
IndexedDbAdapterOpts (and any other storage adapter that
takes the same hooks).
Properties
| Property | Type |
|---|---|
encrypt | (plaintext) => Promise<string> |
decrypt | (ciphertext) => Promise<string> |
StoredNote
Persistent note record. Carries everything an app needs to spend
(note preimage), display (symbol, amount, label), and
reconcile against chain state (leafIndex, txHash, chainId).
BigInts are kept native here; adapters handle wire-format serialization (hex strings) at the storage boundary.
Properties
| Property | Type | Description |
|---|---|---|
id | string | Stable per-record id (uuid). |
label | string | Display label, e.g. lot-1. |
symbol | string | Token symbol shown in the UI. |
amount | string | Display amount string (already formatted for the UI). Not used for math — note.amount is the canonical raw value. |
note | CommitmentNote | Full commitment-note preimage. The secret material that lets the holder spend the deposit. |
commitment | bigint | Poseidon commitment derived from note. Cached so callers don’t recompute on every render. |
leafIndex | number | On-chain leaf index. -1 when the deposit’s CommitmentInserted event hasn’t been reconciled yet. |
status? | "failed" | Set to "failed" once the deposit transaction is proven to have NOT landed on-chain — i.e. its receipt reverted (status === 0). Such a note’s commitment was never inserted, so it can never reconcile to a leaf and no funds were escrowed for it; the UI filters it out instead of showing it as Pending forever. Only set on a reverted receipt — a merely-not-yet-mined or successfully- mined-but-unindexed deposit is left untouched (deleting those could strand a real, recoverable note). |
failedAt? | number | When status was set (ms epoch). |
txHash? | string | Deposit transaction hash, when known. |
chainId? | number | Chain id this note belongs to — apps should only show notes for the active network. |
account? | string | Wallet address (lowercased, 0x-prefixed) that deposited this note. Used so the escrow / vault UI only shows notes whose spendable secret the connected wallet plausibly holds — without it, every wallet sharing a workspace folder sees every other wallet’s notes and gets misleadingly Pending / Available chips for funds it can’t actually claim. Optional: notes written before this field existed (and notes the user explicitly wants visible across wallets) leave it undefined and pass through any accountKey filter. |
createdAt | number | When the note was added (ms epoch). |
NoteStorageAdapter
Storage adapter contract. Implementations: in-memory (tests / SSR), IndexedDB (browser), and (future) AsyncStorage / SQLite for RN.
Methods
ready()
ready(): Promise<void>;Resolve once the adapter is ready (e.g. IDB open completed). Idempotent: subsequent calls return the same promise.
Returns
Promise<void>
loadAll()
loadAll(): Promise<StoredNote[]>;Load all notes, ordered oldest → newest by createdAt.
Returns
Promise<StoredNote[]>
put()
put(note): Promise<void>;Insert or update a note by id.
Parameters
| Parameter | Type |
|---|---|
note | StoredNote |
Returns
Promise<void>
remove()
remove(id): Promise<void>;Remove by id. Idempotent — a missing id is a no-op.
Parameters
| Parameter | Type |
|---|---|
id | string |
Returns
Promise<void>
clear()
clear(): Promise<void>;Remove every note. Used for “reset wallet” / “switch account”.
Returns
Promise<void>
lockedCount()?
optional lockedCount(): number;Number of records present in the backing store that the last
load could NOT recover — e.g. encrypted rows read by an adapter
whose decryption key isn’t available yet. Meaningful only after
loadAll() resolves; adapters without a locked concept omit it
(read as 0). The vault provider surfaces this so apps can raise
an “unlock” affordance instead of silently under-reporting.
Returns
number
Variables
DEPOSIT_CONFIRMING_WINDOW_MS
const DEPOSIT_CONFIRMING_WINDOW_MS: number;Window (ms) after a deposit note is persisted during which we treat
it as “still confirming” and block a second deposit. Past this, a note
stuck at leafIndex < 0 is almost certainly a dropped tx or a discarded
cross-app change note (the vault is shared across products), so it must
NOT keep funding locked forever.
Functions
getAvailableBalance()
function getAvailableBalance(adapter, opts?): Promise<TokenBalance[]>;Compute available balance per token from a notes adapter.
This is a local read. It sums what the wallet sees in storage.
Notes that have been spent on-chain but not yet reconciled by the
adapter (e.g. used in another tab) will count until spentIds
marks them, so callers that need authoritative chain state should
pair this with their nullifier-watch loop.
Returns one entry per distinct token, in descending balance order so UIs can render the largest line first without sorting.
Parameters
| Parameter | Type |
|---|---|
adapter | NoteStorageAdapter |
opts | AvailableBalanceOpts |
Returns
Promise<TokenBalance[]>
encodeClaimPackage()
function encodeClaimPackage(pkg): string;Base64url-encode a ClaimPackage. The result is URL-fragment safe
(no +, /, or = chars) so callers can drop it into the hash
segment of a claim link without further escaping.
Parameters
| Parameter | Type |
|---|---|
pkg | ClaimPackage |
Returns
string
decodeClaimPackage()
function decodeClaimPackage(encoded): ClaimPackage;Decode a base64url-encoded ClaimPackage. Throws with a clear
message when the payload is malformed, the JSON is invalid, or
the version / shape doesn’t match — so a caller doesn’t have to
unwrap a chain of unknown checks before showing the user.
Parameters
| Parameter | Type |
|---|---|
encoded | string |
Returns
isClaimPackage()
function isClaimPackage(v): v is ClaimPackage;Parameters
| Parameter | Type |
|---|---|
v | unknown |
Returns
v is ClaimPackage
isCompressedPubkeyHex()
function isCompressedPubkeyHex(v): v is string;Shared shape check for an EIP-5564 compressed secp256k1 pubkey
(the 0x + 33 hex bytes that goes into ephemeralPubKey and
similar fields). Exported so storage modules and ABI callers
share one definition.
Parameters
| Parameter | Type |
|---|---|
v | unknown |
Returns
v is string
isLiveNote()
function isLiveNote(n): boolean;A note that hasn’t been flagged a phantom/failed deposit (its tx reverted → never inserted). Centralizes the “exclude failed” filter shared across balance summaries, the confirming-deposit guard, and the note lists so the rule stays in one place.
Parameters
| Parameter | Type |
|---|---|
n | { status?: "failed"; } |
n.status? | "failed" |
Returns
boolean
isPendingDeposit()
function isPendingDeposit(n): boolean;A deposit that has been broadcast but not yet reconciled to an on-chain leaf — i.e. still “in flight”. Excludes phantom (failed) notes. The single predicate behind the confirming-deposit guard, the on-chain retry recheck, and the wizard’s pending filter, so they can’t drift on what “pending” means.
Parameters
| Parameter | Type |
|---|---|
n | { leafIndex: number; status?: "failed"; } |
n.leafIndex | number |
n.status? | "failed" |
Returns
boolean
hasConfirmingDeposit()
function hasConfirmingDeposit(
tokenNotes,
nowMs,
windowMs?): boolean;True when a recently-created note for the run’s token is still
pending on-chain (leafIndex < 0) within the confirming window — i.e. a
deposit we just broadcast is most likely mid-confirmation, so a second
deposit would duplicate it. Time-bounded on createdAt so a
phantom/never-reconciling pending note can’t permanently block
deposits (the bug a naive pendingRaw > 0 check would introduce).
Callers pass the token-filtered notes + Date.now().
Parameters
| Parameter | Type | Default value |
|---|---|---|
tokenNotes | readonly { leafIndex: number; createdAt: number; status?: "failed"; }[] | undefined |
nowMs | number | undefined |
windowMs | number | DEPOSIT_CONFIRMING_WINDOW_MS |
Returns
boolean
assessDepositRetry()
function assessDepositRetry(pending, deps): Promise<RetryGuardResult>;On-chain recheck before allowing a deposit retry. Call this only for pending notes that have already aged past the wall-clock confirming window — the in-window block stays enforced separately and unconditionally (see hasConfirmingDeposit). This closes the gap where a deposit that genuinely landed (but whose confirmation/reconcile lagged past the window) would otherwise let a confused user re-deposit and lock 2× the funds.
Conservative by construction: it only adds a block — it never trusts
a bare findIndex < 0 to permit a retry. Verdicts:
- a commitment in the tree → block (landed)
- a
status === 1receipt → block (landed) - a known tx with a
nullreceipt → block (mempool) - a
status === 0receipt → allow (reverted) - a dropped tx (unknown to the node) → allow (safe)
- no txHash / unreadable receipt / unknown status / no reader (can’t classify) → confirm (ambiguous)
Parameters
| Parameter | Type |
|---|---|
pending | readonly PendingDepositNote[] |
deps | RetryGuardDeps |
Returns
Promise<RetryGuardResult>
createFolderNoteAdapter()
function createFolderNoteAdapter(opts?): NoteStorageAdapter;Build a folder-backed NoteStorageAdapter. Throws on put,
remove, or clear when no folder is selected — callers should
guard with hasFolder() or wait for useFolderStorage().ready in
React contexts. loadAll returns [] instead of throwing so a
vault that mounts before the folder is picked still gets an empty
list rather than a render-time error.
Identity model: id is content-addressed from commitment
(c-<hex>), so a record written by Pay and a record written by
frontend with the same commitment have the same id. This makes
remove(id) work across apps and keeps any caller-side dedup-by-
id consistent regardless of which app produced the file.
Parameters
| Parameter | Type |
|---|---|
opts | FolderAdapterOpts |
Returns
idForCommitment()
function idForCommitment(commitment): string;Content-addressed id for a commitment. Hex of the commitment is
unique per note (Poseidon collision resistance) and matches the
same record across reads from Pay or frontend. Exported so vault
providers stamp the same id at put time, which keeps the
in-memory vault and the on-disk record consistent.
Parameters
| Parameter | Type |
|---|---|
commitment | bigint |
Returns
string
createIndexedDbNoteAdapter()
function createIndexedDbNoteAdapter(opts?): NoteStorageAdapter;IndexedDB-backed note storage. Uses a single object store keyed
by note.id. Survives page reload + browser restart. Falls back
to in-memory state on platforms without IDB (private-mode quotas,
SSR, broken handles) — failures don’t surface to the caller, but
are logged once per session.
Security model (read before relying on this in production)
By default this adapter persists note.ownerSecret, note.salt, and
note.amount as plaintext hex in the browser’s IndexedDB. Any
JavaScript on the same origin (stored XSS, malicious extensions, dev-tools
by anyone with device access) can then read this material. It does not
move funds on its own — spending also requires the EdDSA private key bound
into the v2 commitment (which apps store encrypted) — but it DOES leak the
link between the note and the user’s other on-chain activity, defeating
the privacy goal.
Pass encrypt / decrypt in IndexedDbAdapterOpts to opt into
encryption-at-rest: the sensitive payload is stored as ciphertext and only
the record id remains in clear. The app owns the crypto + key (e.g.
WebCrypto AES-GCM under a wallet-signature-derived key), matching the
EdDSA-key encryption flow. With encryption enabled, an IDB read yields no
preimage/amount. Without it, treat IDB as semi-trusted storage.
Parameters
| Parameter | Type |
|---|---|
opts | IndexedDbAdapterOpts |
Returns
createMemoryNoteAdapter()
function createMemoryNoteAdapter(): NoteStorageAdapter;In-memory note adapter — no persistence. Useful for tests, SSR, and storybook stories. Each instance has its own state; callers should share a single instance per “session” if they want cross-component visibility.
Returns
createSignatureNoteCipher()
function createSignatureNoteCipher(signature): NoteCipher;Derive an AES-GCM-256 note cipher from a wallet ECDSA signature —
the same 65-byte personal_sign output deriveEdDSAKey returns, so
an app that already derived its trading key can enable note
encryption-at-rest without a second wallet prompt.
Key path: HKDF-SHA256(ikm = signature bytes, salt/info = fixed domain-separation labels) → non-extractable AES-GCM-256 CryptoKey. The key lives only in memory (like the signature itself); losing it is fine — it’s re-derivable from the wallet by re-signing.
Each encrypt call uses a fresh random 96-bit IV, so identical
plaintexts produce unlinkable ciphertexts. decrypt throws on a
tampered envelope (GCM auth failure) — storage adapters treat that
as a skip, not a crash.
Parameters
| Parameter | Type |
|---|---|
signature | string |