Stealth Transfers
Send ETH privately to any .dust name. Each payment lands at a unique one-time address that only the recipient can detect.
The Problem
On any public EVM chain, every transaction is permanently visible. When someone pays you at your regular wallet address, anyone can: see your total balance, trace every prior payment, learn your income, and profile your spending habits. Sharing your address is a privacy risk.
Stealth transfers solve this by making each payment land at a fresh, unlinkable, one-time address — while still being discoverable by the recipient through a secret only they hold.
How Stealth Transfers Work
Sender has
Recipient's stealth meta-address — their two public keys (spendKey, viewKey) fetched from StealthNameRegistry.
Recipient has
Their stealth private keys — derived from their wallet signature + PIN via PBKDF2. Never stored, always recomputed locally.
- 01
Sender picks a random scalar r
A fresh random numberris generated in the browser for every payment. This is the ephemeral private key — it produces a unique payment every time, even if the same sender pays the same recipient repeatedly. - 02
Compute shared secret via ECDH
Using elliptic curve Diffie-Hellman on secp256k1:sharedSecret = r × viewKey. Only someone who knows eitherr(the sender's ephemeral secret) orviewKey(the recipient's private view key) can compute this value. - 03
Derive the stealth address
stealthAddress = spendKey + hash(sharedSecret) × G. This is a normal Ethereum address. Nothing on-chain identifies it as belonging to any particular person. The sender computesR = r × G(the ephemeral public key) to publish as a hint. - 04
Send ETH + publish announcement
ETH is transferred directly tostealthAddress. Simultaneously, the sender callsERC5564Announcer.announce(schemeId, stealthAddress, R, metadata). This emits a public event — it's the broadcast hint that all recipient scanners read. - 05
Recipient's scanner detects the payment
The recipient's browser scanner fetches all recent announcements. For each one, it computessharedSecret = viewKey × Rand checks if the derived stealth address matches. When it does, the payment is detected and shown in Activities. - 06
Gasless claim via ERC-4337
The recipient clicks Claim. The browser derives the stealth private key(spendKey + hash(sharedSecret))and signs a UserOperation. A DustPaymaster-sponsored relayer submits it — aStealthAccountis deployed at the stealth address and immediately drains its balance to the recipient's chosen claim address. Gas is zero for the recipient.
.dust Names
Instead of sharing two raw public keys, users register a readable name on the StealthNameRegistry contract. Names are up to 32 characters and end with .dust.
| Name type | Example | Use case |
|---|---|---|
| Primary name | alice.dust | Main identity |
| Sub-account | work.alice.dust | Segment payment streams |
| Custom link | /pay/alice/freelance | Track per-campaign analytics |
Sub-accounts
work.alice.dust) use the same stealth key pair but register separately — allowing different payment streams to flow to the same recipient without any on-chain connection.Security Model
spendKeyThe private key that controls stealth funds. Derived from wallet signature + PIN. Never leaves the browser.
viewKeyUsed only for scanning. Allows detecting incoming payments without spending authority. The public part (viewKey × G) is on-chain.
Ephemeral key rGenerated fresh per payment by the sender. Discarded after the announcement. Creates unlinkability.
AnnouncementsPublished on-chain but contain no decryptable private information. Only viewKey holders can match them to stealth addresses.
V2 Keys (BN254)DustPool V2 uses separate keys on the BN254 curve for FFLONK proofs. The spending key and nullifier key are derived from the same wallet signature + PIN but reduced modulo the BN254 field order.
Works with DustPool V2