The browser wallet
librustzcash compiled to WebAssembly scans, proves and signs in a Web Worker, and nothing about your notes leaves the device.


Inside the worker
| Step | What happens | Code |
|---|---|---|
| Start | The 7.9 MB zwallet.wasm loads in a Web Worker; the wallet database lives in an in-memory WASI directory | packages/zwallet/src/core.ts |
| Create | 24 words (BIP 39, 256 bits) make one Zcash account with ZIP 32 keys; a new wallet's scan starts 20 blocks below the tip | wallet.rs, zwallet/src/constants.ts |
| Sync | Every 30 seconds, 100 blocks per batch, single-threaded (WASI has no threads), up to 50 full transactions fetched per sync | sync.rs, sync.ts |
| Rest | Notes that came from a public address are locked 2–6 h by the wallet's own output locks | notes.rs, packages/engine/src/rest.ts |
| Send | A ZIP 321 payment request, ZIP 317 fee, no memo, the outgoing viewing key discarded, change to Ironwood | send.rs |
| Prove | The Orchard proving key warms up in the background after the wallet opens; proofs run in the browser | prover.rs |
| Save | The database is encrypted (AES-GCM 256, a fresh 12-byte IV per write) into IndexedDB after every change | apps/web/src/features/vault/vault.ts |
Rules it keeps
- No sync right after a send. After a broadcast the wallet waits a random 1 to 10 minutes before its next sync, so the gateway never sees a send followed at once by the same wallet's block reads.
- Sapling is never spent. Its provers are stubs, which keeps about 50 MB of parameters out of the browser.
- Restores scan from Ironwood. A wallet restored from words scans from block 3,428,143, the Ironwood activation height on mainnet.
- Reorgs. The wallet rewinds 10 blocks on a broken chain and gives up after 5 tries.
The light client
The wallet reads the chain through six lightwalletd methods: GetLatestBlock, GetTreeState, GetSubtreeRoots,
GetBlockRange, GetTransaction and SendTransaction. Every one is an Oblivious HTTP
message, because even a block range says something about a wallet (where its scan starts is its birthday). The
compact blocks come from ChainSafe's public gRPC-web servers, which only ever see Umbra Trade's gateway.
One pool per transaction
A payment spends Ironwood notes only, with change to Ironwood, so no payment publishes a pool crossing's value beside it. ZEC in the old Orchard pool (a note from before Ironwood, or from a wallet that still builds older transactions) shows on Portfolio as in the old Orchard pool. Move to Ironwood moves it the ZIP 318 way, after you agree to the schedule.