Discover your notes from chain
What this lets you do
Sync your local view of shielded balances with what's actually on-chain. The SDK's NoteManager keeps a per-wallet local index of notes (commitment, value, status, asset, label), and discoverNotes() scans the chain for events that affect your account and reconciles the two.
This is the entry point to every read-side flow: displaying balances and picking which notes to spend.
Constraints & limits
- Cold sync can be slow if the ASP is unavailable: the first
discoverNotes()starts from the sync cursor, which is0x0on a fresh NoteManager. On the ASP fast-path (next bullet) that's cheap, because the ASP serves its indexed event history and only the tail past its last-synced block is fetched over RPC. But on the pure-RPC fallback (noaspUrl, or ASP unavailable) the SDK paginateseth_getLogsfrom block 0 at 10K blocks per page, which can hit free-tier rate limits. Bound the scan by passingfromBlock(e.g. the pool's deploy block) todiscoverNotes(). There is no deployment-block config, and the SDK never learns it on its own. - ASP note-events fast-path: when
aspUrlis configured, the SDK pulls encrypted Note events from the ASP's/{chainId}/public/note-eventsendpoint instead of paginatingeth_getLogsitself, then gap-fills any range past the ASP's last-synced block over RPC. (The separate/{chainId}/public/event-snapshot/payloadendpoint backs status reconciliation, the spent/ragequit/phantom-purge checks, not the primary Note-event scan.) - Cursor and notes live in the persistent-storage adapter you configure, typically the browser's
localStorage(the SDK'sLocalStorageService). The stored state includes the secrets needed to spend, so protect it like any wallet data. Storage is not configured by default, so without an adapter the cursor and notes stay in memory only and are lost on reload, forcing a full rescan next time. Configure a persistent adapter to keep subsequent syncs fast. - Local state can lag behind chain state: a spent note may still look active locally after a failed or partial flow. Before picking inputs to spend, always verify spent status on-chain.
What it unlocks next
- List notes: filter the result by status / asset / label.
- Private transfer: pick freshly-discovered
ACTIVEnotes as inputs. - Check note attestation: see which
PENDINGnotes have transitioned toACTIVE.
How to use it
// Reconcile from chain. Returns immediately with the up-to-date set.
await session.discoverNotes();
// Read the current view.
const account = await session.exportAccount();
const active = account.notes.filter((n) => n.status === "ACTIVE");
console.log(`${active.length} spendable notes`);
Avoid tight polling: calling discoverNotes() in a tight loop (e.g. from a polling hook with no debounce) reruns chain scans and can starve out RPC quota. Sync on explicit user action or on a long stale-time.
Behind the scenes
SDK call
session.discoverNotes()Data sources ASP
/{chainId}/public/note-events for the encrypted Note stream (with RPC gap-fill past its last-synced block), falling back to paginated eth_getLogs when no aspUrl is set. Status reconciliation additionally reads the ASP event-snapshot payload (transacts, ragequits, snapshot block)Decryption Per-note ECDH with your
viewingPrivateKey against the event's ephemeral pubkeyState update NoteManager flushes new notes and status transitions to the persistent-storage adapter