Notes and Account Model
UPP uses a UTXO-style note model instead of account balances — encrypted UTXOs that prove ownership without revealing contents.
Notes and Account Model
Notes, Not Balances
UPP uses notes (like Bitcoin UTXOs) instead of account balances:
Ethereum: Address → Balance (1000 tokens)
UPP: Note A (500 UPD) + Note B (300 UPD) + Note C (200 UPD) = 1000 UPDTransactions consume existing notes and create new ones. Notes are never modified — only created and nullified.
Note Structure
interface Note {
amount: bigint // Token amount
blinding: bigint // Random value (ensures hiding — prevents commitment duplication)
origin: Address // Original depositor (for ASP compliance)
sender: Address // Who sent this specific note
token: Address // ERC20 contract address
}Origin vs Sender:
origin— who first deposited. Preserved through all transfers. Used for ASP compliance. Only changes via merge.sender— who sent this specific note. Changes each transfer.
Commitments
Notes are stored on-chain as Poseidon hashes (never the note itself):
commitment = Poseidon(amount, blinding, origin, token)Properties:
- Hiding: Cannot recover note contents from commitment
- Binding: Cannot create two notes with the same commitment (blinding prevents this)
- Token-locked: Token is bound in the commitment — no transmutation attacks
Nullifiers
Prevent double-spending:
nullifier = Poseidon(commitment, spendingKey, leafIndex)When a note is spent:
- The circuit computes the nullifier privately
- The nullifier is published on-chain (not the note contents)
- The contract records it as spent
- The same note can never be spent again
Because the nullifier depends on spendingKey, only the note owner can produce it.
Merkle Tree
All commitments are stored in a LeanIMT (Lean Incremental Merkle Tree):
| Property | Value |
|---|---|
| Depth | 32 levels |
| Hash function | Poseidon |
| Capacity | ~4.3 billion notes |
| On-chain | Commitment appended at each shield/transfer |
ZK proofs include a Merkle membership proof — proving a commitment exists in the tree without revealing which one.
Note Lifecycle
Transfer Example
Alice sends 600 of her 1000 UPD note to Bob:
The original 1000 UPD note is nullified. Two new notes are created. Origin remains Alice in both.
Merge Example
Bob merges two received notes:
After merge, origin becomes Bob — Bob vouches for the funds (bona fide purchaser).
Merge Changes Origin
Merge is the only operation that changes a note's origin. The merger takes on compliance responsibility for the combined funds.