Transaction Flow
End-to-end lifecycle of a private transaction in Senddy.
Overview
A private transaction in Senddy goes through six phases: note selection, proof generation, attestation, relay, on-chain execution, and sync. The diagram below shows the full flow.
Phase Breakdown
1. Note Selection
When the user initiates a send, the app fetches all unspent notes from local storage (IndexedDB). Notes are sorted by value and selected greedily — largest first — to cover the target amount. If the selected notes exceed the amount, a change note is created for the remainder.
For each selected note, the app fetches the current commitment tree from the subgraph and builds a Merkle path proving membership.
2. Proof Generation
The app generates a zero-knowledge proof using a Noir circuit compiled with UltraHonk. The proof attests to:
- Ownership — the sender knows the secret key for each input note
- Existence — each input note's commitment exists in the on-chain Merkle tree
- Conservation — the sum of inputs equals the sum of outputs (no value created or destroyed)
- Correctness — nullifiers and output commitments are correctly derived
The circuit supports up to 3 inputs (spend3) or 9 inputs (spend9) for note consolidation.
3. Attestation
The proof is sent to the attestor — a trusted enclave that verifies compliance requirements and signs the transaction. The attestor's signature is required by the pool contract before accepting any spend.
4. On-chain Execution
The signed transaction is submitted through Gelato Relay, so the user never needs to hold ETH for gas. The relay calls the pool contract, which:
- Verifies the ZK proof
- Checks the attestor signature
- Records each nullifier (preventing double-spend)
- Appends new output commitments to the Merkle tree
5. Sync
After the transaction is confirmed, the sync engine detects the new on-chain events via the subgraph. Both the sender and recipient's clients:
- Fetch new commitments and nullifiers
- Attempt to decrypt attached memos using their view keys
- Store any decrypted notes locally and update balances