# Note System (UTXO)

> How Senddy's private note system works — commitments, nullifiers, and Merkle trees.

Source: https://www.senddy.com/docs/technical/note-system
Last modified: 2026-08-01T23:49:54.000Z

---

## Overview

Senddy uses a UTXO (Unspent Transaction Output) model similar to Bitcoin and Zcash. Instead of maintaining account balances, the system tracks individual "notes" that represent discrete amounts of value.

## Notes

A note is a private unit of value. Each note contains:

- **Value** — The amount of USDC the note represents
- **Owner** — The public key of the note's owner
- **Randomness** — A random value that makes each note unique

Notes are never stored in plaintext on-chain. Instead, only their **commitments** (cryptographic hashes) are recorded.

## Commitments

A commitment is a Poseidon2 hash of a note's contents:

```
commitment = Poseidon2(value, ownerPubKey, randomness)
```

Commitments are emitted on-chain and accumulated in an append-only **Merkle tree** maintained by the attestor enclave. The pool stores the recent tip history and requires spends to cite a known root. When you deposit and shield, new commitments are appended; when you spend, prior commitments remain in the tree forever — they're never removed.

## Nullifiers

To prevent double-spending, each note has a unique **nullifier** derived from the note and the owner's secret key:

```
nullifier = Poseidon2(commitment, secretKey)
```

When a note is spent, its nullifier is published on-chain. The system maintains a **nullifier accumulator** — if a nullifier has been seen before, the spend is rejected.

Because nullifiers are derived from the secret key, no one can link a nullifier to its commitment without knowing the key. This is what preserves privacy.

## Merkle Tree

All commitments are stored in a Merkle tree. This data structure allows efficient **membership proofs** — you can prove a commitment exists in the tree without revealing which one it is.

The spend proof includes a Merkle path that proves "I know a commitment in this tree" without revealing the commitment's position or value.

## Transaction Flow

### Shield (Deposit to Private)

```
USDC deposit → Shield proof → New note commitments added to Merkle tree
```

- Proves: value conservation, commitment integrity

### Spend (Private Transfer)

```
Input notes → Spend proof → Nullifiers published + New output commitments added
```

- Input notes are "consumed" by publishing their nullifiers
- Change is returned as a new note to the sender
- Supports optional withdrawal (one output is a public USDC transfer)

## Example

Alice has a note worth $100. She wants to send $30 to Bob.

1. Alice's client selects her $100 note as input
2. Creates two output notes:
   - $30 note owned by Bob
   - $70 note owned by Alice (change)
3. Generates a spend proof proving:
   - The $100 input note exists in the Merkle tree
   - Alice knows the secret key for the input note
   - $30 + $70 = $100 (conservation)
   - The nullifier for the $100 note is correctly derived
4. Submits the proof on-chain
5. The $100 note's nullifier is recorded (can't be spent again)
6. Two new commitments ($30 for Bob, $70 for Alice) are added to the tree
