> For AI agents: the documentation index is at https://docs.umbraprivacy.com/llms.txt, and every page is available as Markdown at its URL with .md appended.

# Note commitments (/docs/state/commitments)

A note commitment is one Poseidon hash of all thirteen of a note's fields. It is the only form in which a note's contents reach the chain, and the chain never stores even that directly: it wraps the commitment with the slot and position at which it appended it, and keeps the result as a leaf of the note tree.

1. Note fields: 13 values, known to sender and recipient
2. Note commitment: H_off\[4] of all 13, computed by the sender's proof
3. Tree leaf: H_on(36, commitment, slot, position), computed by the chain
4. Note tree root: published on chain

*From a note to the tree. The first step happens on your device, the second on chain.*

## The commitment

The commitment hashes the fields in a fixed order: version, kind, zone, amount, valid after, valid before, blinding factor, nullifier blinding factor, owner commitment, mint identifier, funding user commitment, funding program commitment and memo hash. It uses the circuit form of Poseidon, `H_off[4]`, with the domain 4 and the input count carried in the sponge's capacity. Thirteen inputs is more than the Solana Poseidon syscall accepts, and the chain never needs to compute it, because it never sees the fields. See [hash functions](https://docs.umbraprivacy.com/docs/cryptography/hashing).

The same template that hashes the fields also range-checks the six numeric ones: 16 bits for version and kind, 20 for zone, 64 for amount and 48 for each end of the validity window. Every spent note and every created note goes through it, so no note in the tree can carry an amount of 2⁶⁴ or more.

The commitment has the two properties a commitment needs. It is hiding, because the blinding factor is a random field element that nobody outside the transaction can guess, so seeing the commitment tells an observer nothing about the amount, token or owner. It is binding, because opening it to a different set of fields would mean finding a Poseidon collision. The general case is on the [commitments](https://docs.umbraprivacy.com/docs/cryptography/commitments) page.

## What the chain sees

A value transaction publishes three output commitments, one per output slot, dummies included. They are three of the 53 values folded into the transaction proof's public input, and the proof shows that each one is the commitment of a note satisfying every rule of the circuit.

The program itself checks only that each output commitment is a canonical field element, below the BN254 modulus; otherwise it refuses the transaction with error 65, listed in the reference section below. It cannot check more, because it never learns the fields.

## The leaf

When the transaction succeeds, the program appends the three commitments to the note tree. For each one it computes the leaf

`leaf = H_on(36, commitment, insertion slot, insertion position)`

with the syscall form of Poseidon, where the insertion slot is the slot the append happens in and the insertion position is the leaf's global position: the pool index times 2³² plus its index in that tree. The [note tree](https://docs.umbraprivacy.com/docs/state/note-tree) page explains positions and pool indexes.

Wrapping the commitment gives the chain, not the prover, the last word on where a note sits and when it arrived. A spend has to rebuild this exact leaf inside its proof, from the note's fields and the slot and position the chain chose, and then show the leaf is in the tree. It cannot use a different position, because a different position is a different leaf, and that leaf is not in the tree.

Both stamped values are used again later. The position is an input to the note's [nullifier](https://docs.umbraprivacy.com/docs/state/nullifiers), so each leaf has exactly one nullifier, even if two leaves happened to wrap the same commitment. The slot is one of the per-field values the [disclosure tree](https://docs.umbraprivacy.com/docs/disclosure) records for each spent note.

## What the recipient must know

To spend a note later, its owner has to reproduce the commitment and then the leaf, so they need every field, the slot and the position. The encrypted note carries twelve of the fields; the owner commitment is left out, because the recipient knows it already. The slot and position come from the program's log of the append. Both blinding factors must be non-zero. The transaction circuit refuses an output, real or dummy, with either one at zero, and the escape-hatch circuit refuses to spend such a note, so the rule holds for every note the pool creates; the note-plaintext encoding enforces it off chain as well, since a zero blinding factor would publish a value every observer could predict.

## Reference

**Errors**

| Code | Error                      | Meaning                                                                                     |
| ---- | -------------------------- | ------------------------------------------------------------------------------------------- |
| `65` | `NonCanonicalFieldElement` | A payload value that must be a BN254 scalar-field element is at or above the field modulus. |

Read at build time from `libs/typescript/errors/error-manifest.json`.

## Related

- [Notes](https://docs.umbraprivacy.com/docs/state/notes): The thirteen fields and the three kinds of note.
- [The note tree](https://docs.umbraprivacy.com/docs/state/note-tree): Leaves, positions, roots and pool indexes.
- [Commitments](https://docs.umbraprivacy.com/docs/cryptography/commitments): Hiding, binding and blinding factors in general.
- [Hash functions](https://docs.umbraprivacy.com/docs/cryptography/hashing): The two Poseidon forms and their domains.


Last updated 12 October 2026
