Skip to content

Keeping the device in sync

Why the device's copy of the private balance needs syncing, what a sync does, and when to run one.

A wallet app reads a token balance straight from an RPC node. A private balance cannot be read that way. Only your user's keys can find their notes, so the device finds them and keeps a copy.

A sync brings that copy up to date.

What a sync does

syncNoteStore runs in rounds until it has read everything new:

sequenceDiagram
participant App as Your app
participant SDK
participant Nimbus as Nimbus indexer and Solana
participant Store as This device
App->>SDK: syncNoteStore
loop until caught up
  SDK->>Nimbus: the next page: a sealed file, or the newest rows
  SDK->>SDK: try your user's keys on each new note
  SDK->>Store: save their notes and spends
end
SDK->>Store: release payments a closed tab left
SDK-->>App: ingested

In each round, the SDK reads one page of new notes, spends and records for each pool. It keeps the notes your user's keys open. It marks their notes as spent once Solana shows them spent.

Last, it frees the notes of any payment whose tab closed before sending it.

A sync downloads every new note, not just your user's. So it never tells the indexer which notes are theirs.

Where the pages come from

The indexer seals older pool activity into files of 1000 rows each. A sealed file never changes, so a CDN serves it from the edge.

The SDK reads each stream from those files first. It reads only the newest rows, past the last sealed file, from the indexer's data API. A file is the same for every user, so fetching it shows nothing about yours.

The SDK checks each file against the indexer's manifest before it takes a row. It reads a missing or faulty file from the data API instead. Either way, the device ends up with the same copy.

When the SDK syncs for you

Every deposit, Private Send and withdrawal syncs first, by default. A followed Private Send or withdrawal syncs again once it lands. Pass sync: false to a payment only if you synced just before.

When your app should sync

  • On load, once the session is open, before you show a balance.
  • When the tab regains focus, since your user may have been paid meanwhile.
  • On an interval while the page is visible, to pick up incoming payments.

Every tab shares one copy. A sync in one tab updates the balance for all of them.

Long syncs

A device that has been away a while, or a new device, has a lot to read. Most of it comes from sealed files on the CDN, which is the fast part. Three options keep a sync in hand:

  • maxRounds stops after that many rounds. The result's caughtUp says whether it finished.
  • signal stops it between rounds when an AbortSignal fires.
  • onProgress hears about each round, with the slot reached.

The next sync picks up where the last one stopped.

What this means for your app

  • Show "updating" while a sync runs. Show the last balance meanwhile, not a spinner in its place.
  • Expect unregistered before registration. Until your user registers, there is nothing to read.

Last updated

On this page