Skip to main content

Private Transfer

A private transfer moves value between two vaults. On-chain it publishes one nullifier and two commitments, and nothing else: not the sender, not the recipient, not the amount.


What happens

  1. Your wallet picks input notes covering the amount, from the notes it holds.
  2. It builds a proof that you own them, that they exist in the Merkle forest, and that value balances — inputs equal outputs plus fee. This takes a few seconds in the browser.
  3. Two new notes are created: one for the recipient, one back to you as change. Each is encrypted to its owner's viewing key and attached to the transaction.
  4. The chain verifies the proof and records the nullifier and the two commitments. Your input note is now spent and cannot be spent again.

The recipient's wallet finds its note by scanning, or immediately if you hand them a payment slip.


What stays private

HiddenPublic
Who sent itThat a transfer happened, in this block
Who received itOne nullifier (unlinkable to any commitment)
The amountTwo new commitments (opaque hashes)
Which note was spentThe fee amount

The nullifier cannot be traced back to the commitment it spends — that link needs your spending key. See Notes & the Merkle Forest.


The fee

A small fee is deducted from the input note at proof-generation time and held as a pending balance inside the shielded pool. You never pay gas from a public balance, which is what lets an account with no public tokens spend at all.

Who receives the fee

Nothing in the calldata names the fee recipient. The chain reads it from the dispatch origin — how the call arrived, which the caller cannot forge:

Submitted viaCredited to
ShieldedPool precompilewhoever signed that EVM transaction and paid its gas
Signed extrinsicthe signer's registered EVM address
Unsigned extrinsicthe block author

The recipient is read from the origin rather than the calldata because a calldata field would be an unauthenticated claim. A private transfer is broadcast before it is included, so anyone could take a propagated proof, resubmit it naming themselves, and collect a fee they never paid for. An origin cannot be rewritten that way: the party credited is the one who actually bore the cost of submitting.

The fee is a public input to the proof, so it cannot be altered without regenerating the proof. See Gasless Fees.


Choosing a submit route

Which origin a transfer arrives under is the sender's choice, and it is the difference between leaving a public trace and leaving none.

An unsigned submit needs no wallet at all: the proof authorises the spend, so there is no signature to check and nothing to charge gas to. What lands on chain is a nullifier and two commitments — the same as any other transfer, with nothing tying them to a submitter.

Signing through the precompile publishes an Ethereum transaction alongside it. That transaction is public and permanent, and it names the sender's address, the precompile it called, and the privateTransfer selector. Anyone reading the chain learns this address made a private transfer in this block. The amount, the recipient and the spent note stay hidden — but participation does not.

UnsignedPrecompile
Wallet signaturenonerequired
Gasnonepaid by the signer
Relay feeto the block authorback to the signer
Public tracenonean EVM transaction naming the sender
Time to inclusion~4.5 s~8.2 s

Measured against a development node; treat the timings as relative, not absolute.

For a wallet spending its own notes, unsigned is better on every axis a user cares about. The one thing signing buys back is the relay fee — and the gas it costs to do so exceeds it, so a sender who signs to recover their own fee ends up behind.

Signing is for the case it was built for: relaying somebody else's transfer. There the submitter pays the gas and is credited the fee, which is the trade that makes third-party relaying worth doing.

What an unshield reveals regardless

The same choice applies to unshield, but the stakes differ: an unshield publishes its recipient and amount by design. Submitting it unsigned withholds who withdrew, not what was withdrawn.