Trust model
What you have to trust when you use noircash, what you do not, and what each party, Pons included, could and could not do to you.
noircash has no operator: nobody can freeze, move or reveal your notes for you. You still rely on mathematics, on code, on Pons for trading and fees, on your wallet and on your device. This page lists each party, what it can do, and what stops it.
The noircash contracts
The vault (PrivateVault), the router (PrivateRouter), the fee harvester (FeeHarvester), the verifier and the
Poseidon2 hasher are immutable:
- No owner, no admin role, no proxy, no upgrade path, no pause, no freeze, no blocklist.
- No initializer: every address a contract works with is set in its constructor. The vault's harvester and router are fixed CREATE3 addresses known before any of them is deployed.
- Every limit is a constant or set once in the constructor: the 20% gas margin, the ±10% price clamp, the 3% bound on harvester trades, the 0.5% harvest tip, the harvester's target deposit.
- The EntryPoint deposit that pays for gas can be topped up by anyone and withdrawn by nobody. The vault is not
staked; stake added with
addStakecould never be unlocked.
The deployer has no power after the deploy. It deployed the noircash contracts, bought and locked the seed note, and made the first gas deposit. The address that made NOIR's launch on Pons has no special rights over NOIR: it cannot sweep or redirect NOIR's fees, which Pons pays to the harvester. See NOIR on Pons. The other side of immutability: a bug cannot be patched (see Stated limits).
NOIR and Pons
NOIR is a Pons V2 token: OpenZeppelin ERC20 plus ERC20Burnable, with no owner and no transfer restrictions.
Nobody, Pons included, can mint it, freeze it or move it from an address, and that includes the vault's balance. A
note's NOIR stays in the vault until its owner spends the note.
NOIR trades on Pons contracts: its bonding curve, then the graduated Uniswap v4 pool with the Pons hook. Pons also collects the trade fees and pays the creator share to the harvester. So Pons decides:
| Pons can | Effect on noircash | What it cannot do |
|---|---|---|
Take its share of the fees: 30% of the 1% standard fee (protocolFeeShareBps 3000), frozen at launch | Holders get the rest: 0.7% of every trade | Change NOIR's fee terms after launch |
Redirect NOIR's creator fee recipient with setCreatorFeeRecipient, after a 3-day timelock (visible in pendingCreatorFeeRecipient) | The harvester stops receiving fees: holder yield and the ETH that tops up the gas deposit stop | Touch the vault, the notes or anyone's NOIR |
Use its owner's rescue functions on curves and graduations (rescueCurveFees, forceSweptGraduation, rescueSweptGraduation) | Can affect the curve's fees and how and when NOIR moves to the pool, hence trading, the price the vault reads, and harvests | Move NOIR out of the vault |
| Stop serving the curve or the pool (for example a failed graduation) | Private buys and router sales fail; an ERC-4337 sale whose swap fails delivers the NOIR to the ETH recipient instead | Stop private sends or unshields, which never touch a Pons contract |
The Pons owner is a contract, 0x263ed295dAFaE1d9AAdD6E56c4B6F9f38eE019Dd (read on-chain). This dependency is part of the design: it is what lets NOIR trade on every venue and
aggregator that supports Pons.
The graduated pool is the one Pons creates: only the Pons factory can initialize a pool with its hook, and the router, the harvester and the vault use only that pool's id. Anyone can open another ETH/NOIR pool, but it has another id and noircash never trades or prices on it.
The circuit and the verifier
Every spend is a zero-knowledge proof for the transact circuit, written in Noir and proved with UltraHonk. The
verifier checks it against public inputs the vault computes itself: the root, two nullifiers, two new commitments,
the exiting shares, a hash of the external data and the vault's EIP-712 domain separator.
UltraHonk needs no ceremony for this circuit. It uses a universal reference string (CRS) shared by all circuits; the app serves pinned copies checked against fixed SHA-256 hashes. Soundness rests on:
- the circuit enforcing membership, ownership through the nullifier key, correct nullifiers, conservation of shares (inputs = outputs + exit), 120-bit range checks and a valid wallet signature (see The circuit);
- the generated verifier matching the circuit, and no soundness bug in Noir or Barretenberg;
- Poseidon2 and keccak256 being collision resistant and secp256k1 ECDSA being unforgeable;
- the universal CRS having been generated honestly.
If one fails, an attacker could create shares from nothing and take the vault's NOIR. This is the largest risk in the system.
Your wallet
Your wallet signs two kinds of messages.
The activation signature. A Sign-In with Ethereum message that names the app's domain and the vault. Its hash is the seed for your viewing key and your nullifier key. Anyone who obtains this signature can see all your notes, past and future. They cannot move them. See Keys.
One signature per spend. Every send, sale or unshield needs an EIP-712 Spend(bytes32 transaction) signature
from the same wallet. transaction is the Poseidon2 hash of the proof's public inputs, so the signature covers the
root, nullifiers, new notes, exit amount and the hash that binds recipient, fee and sale terms. The circuit checks
the signature against the wallet key committed in your registered owner key. The domain separator binds it to this
vault on this chain.
Public actions (buying, approving the vault, shielding, registering) are ordinary transactions your wallet sends and shows in full.
You trust your wallet to keep its key secret and to sign deterministically: the app signs the activation message twice on first use and refuses wallets whose signatures differ. Only EOA-key wallets are supported (including EIP-7702 accounts and MPC wallets that produce one ECDSA signature), not contract-based multisigs.
The app (frontend)
The app derives your keys, scans for notes and builds the proof on your device. Keys live only in the page's
memory and are not sent to a server. Its Content-Security-Policy allows only the app's own scripts and limits
connect-src to the app, the chain RPC, the bundler and the indexer, so injected code cannot fetch your keys to
an arbitrary server. The proving CRS is served by the app, so proving contacts no third party.
The policy does not protect you from the app's own code. A compromised frontend (a malicious build on the real domain, or a fake site) could:
- read your notes and balance once you activate privacy there;
- ask you to sign a
Spendfor a transaction that pays someone else; - ask you to approve NOIR to an address other than the vault.
What the spend signature protects, and what it does not
The per-spend signature stops anyone who only has your activation signature or your keys. It does not stop an app
you are actively using: your wallet shows the Spend request as a 32-byte hash, which you cannot check against
the recipient or amount. Use only a frontend you trust, and check the domain in the activation message.
The harvester's callers
Anyone can call harvest(), and the caller earns 0.5% of the ETH it handles. The caller chooses only when a
harvest happens. The harvester's buys and reserve sales must land within 3% of the vault's price average after Pons
fees, and a buy that does not fit is made smaller or left for the next harvest. See Gas.
The indexer
The indexer is optional. One endpoint serves every vault event from a block onwards, the same for everyone, with
no per-user queries, so it does not learn which notes are yours. The app checks the rebuilt tree's root with the
vault's isKnownRoot and reads the chain directly on a mismatch or when the indexer is down. The indexer can see
your IP address and when you sync. See The Merkle tree.
The bundler
Private transactions are ERC-4337 UserOperations whose sender is the vault. Any bundler can submit them.
- It cannot change the recipient, gas fee, amounts or sale terms: their hash is a public input of the proof.
- It cannot starve execution: the proof fixes a minimum
callGasLimitthat validation enforces. - It cannot make the gas deposit pay more than the fee covers: the fee must be worth the operation's maximum gas cost plus 20%, at the price average.
- It can refuse to submit. Any other bundler works, or a direct call paid by an address you choose, which reveals that address.
See Gas.
The RPC node
The node answers the app's reads and relays transactions, and sees your IP address and calls. A lying node could hide events or give wrong reads, but the vault rejects any proof on a root it does not know: it can make a transaction fail, not succeed wrongly.
Robinhood Chain's sequencer
The sequencer orders transactions and decides which ones enter a block. It filters transactions tied to sanctioned addresses. It could censor transactions to the vault, or delay them. It cannot forge a proof, spend a note or change a transaction's contents. This dependency cannot be removed.
Summary
| Party | Can | Cannot |
|---|---|---|
| Deployer | Nothing after the deploy | Change a parameter, move notes, pause, upgrade, redirect fees |
| Pons (its owner) | Redirect the creator fees after a 3-day timelock, use its rescue functions, take its 30% share | Move NOIR from the vault or any address, touch notes, change fee terms after launch |
| Circuit and verifier | Everything, if unsound | Nothing extra, if sound |
| Holder of your activation signature | See all your notes and history | Spend them |
| Whoever controls your wallet key | Everything: re-derive your keys and sign spends | — |
| Compromised frontend | Read your notes; get you to sign a harmful spend or approval | Spend without your wallet's signature |
| Harvest caller | Choose when fees are harvested; earn the 0.5% tip | Buy or sell more than 3% away from the price average |
| Indexer | Withhold data, see IP and timing | Learn which notes are yours; alter the note tree without detection |
| Bundler | Censor, see IP and timing | Change recipient, fee or amounts; starve execution |
| RPC node | Censor, lie about reads, see IP and calls | Make an invalid transaction succeed |
| Sequencer | Censor or delay | Forge, steal or change a transaction |