Skip to Content
GuidesPrivate Pay

Private Pay

Pay has two tabs: Send, which moves one of your shielded notes to someone else, and Incoming, which lists notes and residual-order proceeds waiting for you to claim. Funds never leave the shielded pool on a send — the recipient gets a note, not a transfer.

Publishing your payment key

Before anyone can pay you, you need to publish your payment key on OwnerKeyRegistry. Click Register on this page. This writes your ownerKey — the public value derived from your spending key — to the registry.

It’s a one-time on-chain transaction per wallet. Until it lands, nobody can send you a private payment, because they have no key to build a note against. Once registered, the page shows Published — ready to receive.

Publishing is safe. An ownerKey lets someone build a note for you. It does not let anyone spend one, and it doesn’t reveal your spending key.

Before you can pay someone else

The recipient must have published their own payment key the same way. A wallet address alone isn’t enough to build a note for someone; deriving their ownerKey requires their own signature.

If the address you enter hasn’t registered, the app refuses rather than inserting a leaf nobody can spend.

Sending

  1. Pick an asset pool.
  2. Select the shielded note to spend.
  3. Enter the recipient’s address.
  4. Confirm.
StepWhat happens
Generate ZK ProofThe pay circuit runs in your browser (Noir / UltraHonk)
Submit Paymentpay() — nullifier spent, output commitment inserted
Confirm On-ChainWait for the Paid event
Notify RecipientAnnounce the note’s private data via StealthAnnouncer
SettlementDone

The whole note moves. Pay is a straight 1-in-1-out passthrough — there’s no change output.

What’s private, what isn’t

ValueVisibility
AmountPrivate
AssetPublic
Which note you spentPrivate (only the nullifier is revealed)
RecipientNot on the pay() call itself

The amount stays private because pay never sums anything — the spent note’s amount moves to the output note unchanged. That meant the circuit only needed a real output-commitment check, not the value-conservation and range-proof machinery a multi-input/multi-output system would require.

The circuit genuinely recomputes the output commitment from the spent note’s own amount and asserts equality. That check matters: without it, anyone could pair a proof for spending a trivial note with an arbitrary, uncapped output commitment. An earlier version of the circuit had exactly that hole — a self-referential assertion that was always true — and it’s covered by a dedicated regression test now.

No compliance gate here

pay is not sanction-screened, on purpose. There is no plaintext recipient to screen — the output is a commitment against an ownerKey, not an address. Screening applies at withdrawal, when funds re-enter the open.

The notify step

After the payment confirms, the app announces the note’s private data — assetId, amount, blinding, commitment — through StealthAnnouncer so the recipient can find and spend it.

That announcement is in the clear. As explained in Note Delivery, it adds no privacy loss beyond what pay() already puts on-chain: the blinding is the only genuinely new value, and on its own it doesn’t let anyone but the key holder spend the note.

Claiming incoming payments

The Incoming tab lists notes someone sent you with pay, discovered through StealthAnnouncer announcements addressed to your ownerKey. Claiming saves one to your shielded notes, after which you can spend it like any other.

The note already exists on-chain the moment the sender’s transaction confirmed — claiming doesn’t create anything or cost gas. It reads the announced private values, verifies they reconstruct a commitment you actually own, and writes the note into your local wallet so it becomes spendable.

Nothing is lost if you don’t claim promptly. The announcement stays in the chain’s event log, and your wallet finds it on the next scan.

A dark-pool match’s proceeds arrive the same way and show up in this same Incoming tab. A partially-filled order’s leftover, though, is a residual order rather than a note — that’s claimed on the Swap page instead, in My Orders.

Last updated on