Skip to main content
PolyShield
POLYGON MAINNET · BETA
Docs menu · Core concepts
Getting started
OverviewThe basicsQuickstartFAQ
Core concepts
The privacy modelZero-knowledge proofsSpending notesThe Merkle tree & nullifiers
Architecture
System overviewVault contractZK circuitsOff-chain services
Security
Threat modelTrust assumptionsBackup & recoveryFees
Reference
Glossary
CORE CONCEPTS

The Merkle tree & nullifiers

Two on-chain structures do all the bookkeeping: a Merkle tree that proves a note exists, and a nullifier registry that stops it from being spent twice.

The Merkle tree — "my note is real"

Every note commitment is appended as a leaf to one giant tree (depth 32 — room for billions of notes). The tree hashes pairs of nodes upward until a single root summarizes the whole set. To prove your note exists, you reveal the chain of sibling hashes from your leaf to the root — the inclusion path — without revealing which leaf you are.

rooton-chainsiblingsiblingyour note
Every note commitment is a leaf in one append-only tree. Your inclusion proof is the gold path of hashes from your leaf up to the root — it proves your note is in the set without revealing which leaf it is.

Because the root changes with every deposit, PolyShield accepts a rolling window of the last 1024 roots. A proof you started building a few blocks ago still verifies against the root that was current when you fetched your path — so you're never racing the chain.

Nullifiers — "and I haven't spent it"

Proving a note exists isn't enough — you could try to spend the same note repeatedly. Each spend publishes the note's nullifier, which the Vault records in a registry. Spend the same note again and its nullifier is already present, so the transaction reverts. The nullifier is checked before any state change (checks-effects-interactions), closing double-spend.

old notebalance $100nullifier published — voidedBET_AUTHzk proofbet → Polymarketvia the vault EOAnew note (change)$100 − bet − fee
Spending never edits a note — it destroys the old one (publishing its nullifier so it can never be reused) and creates a brand-new note for the change. Like tearing up a $100 check and writing a fresh one for the leftover balance.

This is why spending always mints a fresh note: you can't edit a note in place, because the old one is permanently nullified. The leftover balance flows into a new note with a new nonce, a new commitment, and — importantly — a new, unlinkable nullifier for next time.