Zcash Flutter Wallet (Sapling/Orchard): Shielded Transaction Tracking, Merkle State Sync, Web Performance Constraints, and Nullifier Indexing

I’m building a Zcash wallet in Flutter using a pure Dart implementation, supporting Sapling, Orchard, and transparent transactions. Most of the core cryptographic and transaction logic is already working correctly.

Now I’m trying to integrate Web3-style behavior and light-client synchronization, but I’m facing several architectural challenges—especially in a web environment.


  1. Tracking shielded transactions efficiently

What is the recommended approach for tracking incoming and outgoing shielded transactions in a light wallet context?

Is there a preferred pattern beyond scanning all blocks / notes, especially for scalable sync?


  1. Merkle tree state management (Sapling/Orchard) — Web performance constraints

My biggest challenge is maintaining Merkle state for proof generation, especially in a web-based Flutter (Dart) environment where CPU and memory resources are limited.

In native builds I can handle incremental updates more comfortably, but in the web implementation I’m experiencing significant performance issues when trying to keep the full Merkle state synchronized.

So I’m trying to understand best practices:

  • Do light clients typically update Merkle trees incrementally per block, or is there a recommended alternative approach for constrained environments like web?
  • Is it acceptable (or recommended) to:
    • maintain only recent checkpoints or a rolling window of blocks
    • reconstruct Merkle paths on-demand instead of continuous full-tree updates
    • store only partial subtrees relevant to owned notes
  • Are there known optimizations to reduce Merkle computation frequency while still guaranteeing correct proof generation?

In particular, I’m trying to avoid:

  • full continuous re-indexing of the entire tree in the browser
  • high-frequency state mutations that degrade UI performance
  • memory-heavy storage of full historical tree state

Is there any accepted pattern for lazy Merkle path reconstruction or checkpoint-based sync used in Zcash light clients?


  1. Nullifier / spent-note indexing

Why doesn’t lightwalletd provide a direct mechanism to index spent nullifiers (similar to Monero’s key_image_status spent tracking)?

Is this intentional due to privacy constraints, or is it expected that clients maintain their own nullifier state locally?


  1. General design guidance

If there are recommended architectural patterns for:

  • minimizing sync cost
  • reducing Merkle updates
  • improving multi-threaded wallet synchronization
  • handling Sapling + Orchard simultaneously

I would really appreciate any guidance or references.


Thanks in advance — I’m trying to align my implementation with best practices in the Zcash ecosystem rather than inventing something incompatible.

I wrote this post with the help of AI.

3 Likes

This is really interesting, Is it open source?

I don’t even care that you used AI :joy: , Everyone uses AI to write

1 Like