03

V0.5 Testnet · The note model

The formulas

V0.5 keeps no balances. Value lives as notes, in the style of Bitcoin’s UTXOs: an amount and a random number, owned by a userCommitment and spent whole. Each pool holds one coin — its chain’s native one.

Example: a note is spent whole

You hold one 10 MON note and send 4 MON with a 0.01 MON fee. The note is consumed entirely: 4 MON to the receiver, 5.99 MON change back to you, 0.01 MON to the relayer — 10 in, 10 out. Your balance is simply the sum of your unspent notes, and only your wallet can compute it.
ucuserCommitment — your Noid identity
Formula
userCommitment = Poseidon(address, spendPk.x, spendPk.y)
What it is
One field element binding your real address to your spending key.
Why
It contains your address, so a sender just types your normal address and reads your commitment from the pool. It contains spendPk, so knowing your address isn’t enough to spend — that takes sk.
Used in
Registration, every note you own, and every proof — the circuit rebuilds it from sk and your address.

Example

Bob pays Alice by typing her ordinary address. His wallet reads her registration on-chain and locks the new note to her userCommitment. No special private address is ever exchanged.
cmcommitment — the note on-chain
Formula
r          = a fresh random number   // never reused
commitment = Poseidon(1, amount, r, userCommitment)
What it is
A note’s public fingerprint: one leaf of the pool’s Merkle tree. It reveals neither the amount nor the owner.
Why
r makes it hiding: without it, every 1 MON note to the same person would hash to the same value and could be guessed by trying amounts. The leading 1 is a domain separator.
Used in
Created by every mask, send and unmask; proved to exist when the note is spent.

Example

You mask 4 MON: C = Poseidon(1, 4·10¹⁸, r, uc). The chain stores C — nobody can tell it holds 4 MON, or that it is yours.
nfnullifier — the note’s spent marker
Formula
nullifier = Poseidon(2, commitment, r, sk)
What it is
A number published when a note is spent.
Why
The pool refuses a nullifier it has already seen, so no note is spent twice. It needs sk, so only the owner can make one — and nobody can link it back to its commitment.
Used in
Every send and unmask: one per input note.

Example

You spend the 4 MON note and the proof publishes N. A second spend with N fails: the nullifier is already spent.
rootMerkle root — proof that a note exists
Formula
root = top of a depth-20 incremental Merkle tree   // 1,048,576 leaves per pool
// a full pool opens a new one; each pool keeps its last 10 roots
What it is
One number summarising every commitment in a pool.
Why
Proving a note sits under the root proves it exists without saying which leaf it is. Ten remembered roots let a proof made a moment ago survive other people’s transactions.
Used in
Every send and unmask.

Example

You prove against root R and two deposits land before your transaction. R is still among the last 10 roots, so it is accepted. Wait too long and you simply prove again.
encencrypted note — how the owner finds it
Formula
encryptedNote = ECIES(owner’s encPub, { amount, r })
What it is
A note’s contents sealed to its owner, emitted in the NoteCreated event.
Why
Nothing on-chain says whose a note is. Only the owner’s encPriv opens it.
Used in
Every note created — the relayer’s fee notes included.

Example

Alice’s wallet tries every new note with her encPriv. Most fail (not hers); one opens to {2 MON, r}. She rebuilds Poseidon(1, 2·10¹⁸, r, uc_A), it matches — +2 MON.