noircashdocs

Public NOIR and shielding

NOIR outside the vault is an ordinary ERC-20, how shielding and unshielding move it in and out of notes, and what stays visible on-chain.

NOIR (0xDa2E4659dA030AEd0285F9Ed3496ec07058b2319) is a plain ERC-20 created by Pons: OpenZeppelin ERC20 and ERC20Burnable, no owner and no transfer restrictions. Any address can hold, send and receive it, and wallets, exchanges, routers and aggregators handle it like any other token. It has no privacy of its own. Privacy starts when NOIR is shielded into the vault and ends when it is unshielded back out.

Public NOIR

NOIR held by an address outside the vault is public NOIR. Its balance and every transfer are visible, as for any ERC-20. Holding public NOIR earns nothing from the vault: only notes hold shares (see The vault).

Registering privacy keys does not change public NOIR: a registered address receives NOIR like any other address, and its balanceOf is its ordinary ERC-20 balance. NOIR becomes private only when its holder shields it.

Where public NOIR comes fromExample
A purchase anywhere other than PrivateRouter.buyThe Pons site, the curve or pool directly, an aggregator
A transfer from another addressAn exchange withdrawal, a payment
An unshieldtransact with an exit to an address
A sale that could not be completedThe vault sends the NOIR to the sale's ETH recipient

Shielding

function shield(uint256 stub, uint256 amount, bytes calldata ciphertext) external returns (uint32 index);

The holder's app creates a note with random rho and r, computes stub = H(opk, rho, r) and encrypts (rho, r, 0) to the holder's viewing key.

The holder approves the vault for amount on NOIR (approve(vault, amount)).

The holder calls vault.shield(stub, amount, ciphertext). The vault pulls amount with transferFrom, mints convertToShares(amount) shares, inserts cm = H(1, stub, shares) into the tree and emits Shielded(from, index, amount, shares) and NoteAdded(index, cm, ciphertext).

The app's shield sends the approval first, only if the current allowance is short, then the shield, both from the holder's wallet. PrivateRouter.buy does the same in one transaction for ETH: it buys NOIR, approves the vault and shields for the buyer (see The market).

Unshielding

An unshield is a transact whose exit goes to an address: exitShares leave the private side and convertToAssets(exitShares - gasFee) NOIR, rounded as described in The vault, are transferred to ext.recipient. The vault emits Exited(nullifier, recipient, amount, shares). The recipient can be any address, including one that never used noircash. From there it is public NOIR again.

A private sale is the same exit with the router as recipient: the router sells the NOIR and sends ETH instead. See Gas and ERC-4337.

What is visible on-chain

InformationPublic?
NOIR's total supply, public balances and public transfersYes
Trades on the curve and on the pool: amounts, price, time, traderYes
B, S, the gas reserve, the price averageYes
Registered addresses and their public keysYes
A shield's caller, amount, shares and leafYes (Shielded)
A private buy's buyer, ETH amount and NOIR amountYes (router Bought, and the curve or pool trade)
An exit's recipient and amount; a sale's ETH recipientYes (Exited, SaleSettled, router Sold)
Which notes a spend consumedNo
Sender, recipient and amount of a private sendNo
A person's private balanceNo
Whether a shielded note has been spentNo

Entries into and exits from the private side are public; what happens in between is not. Timing and amounts at entry and exit can still link them when few people use the vault. See the threat model.

On this page