// one-time stealth addresses

Every payment,
a different door.

Status: Ticker (placeholder, no token exists):
// the working demo

Send. Scan. Spend. Nothing links.

Everything below is computed live with the Web Crypto API (ECDH over P-256, SHA-256) plus a small in-page elliptic-curve implementation. Click through the three steps.

1 Recipient publishes a meta-address

Two keypairs: a scan key (finds payments) and a spend key (moves funds). Only the two public halves are ever published — and that same meta-address is reused for every payment.

Demo keys only — never paste a real seed phrase or private key. These keys exist only in this tab's memory and are discarded on reload.

2 Sender pays — a fresh address each time

The sender makes a one-time ephemeral keypair, does ECDH with your scan public key, hashes the shared secret to a scalar h, and computes stealth = spendPub + h·G. Click repeatedly — every address is different.

No payments yet. Press “Send payment”.

The ledger above is what a public chain would show: an ephemeral key and a one-time address per payment. Nothing in it points back to the recipient's meta-address.

3 Recipient scans — only they can recognise payments

The recipient re-derives the shared secret from each ephemeral key using their scan private key, recomputes the address, and compares. A stranger with the wrong key finds nothing.

// how it works

What's hidden vs. what's public

Public (visible to everyone)

  • The recipient's meta-address (scan pub + spend pub), if they choose to publish it
  • Each payment's ephemeral public key R
  • Each payment's one-time address spendPub + h·G
  • Amounts and timing (in a real chain — stealth addresses do not hide these)

Hidden (only sender + recipient)

  • The ECDH shared secret between ephemeral key and scan key
  • The scalar h derived from it
  • Which one-time addresses belong to which meta-address
  • The spend private key spendPriv + h for each address
  1. The sender computes S = ECDH(r, scanPub). The recipient can compute the identical S = ECDH(scanPriv, R) — Diffie–Hellman symmetry.
  2. Both hash it: h = SHA-256(S ‖ "stealthbox/v1") mod n. Because r is fresh each time, h is different each time.
  3. The one-time public key is spendPub + h·G. Only someone who knows spendPriv can derive the matching private key spendPriv + h — the “Prove spend” button signs a message with it and verifies against the stealth address.
  4. Splitting scan and spend keys means a scanning service can find your payments with just the scan key, without ever being able to spend them.

Curve note: this demo uses P-256 because it is what the browser's Web Crypto API provides. A Solana build would use ed25519 (and x25519 for the Diffie–Hellman step). The displayed “address” is the base58 encoding of the stealth point's x-coordinate — address-shaped for illustration, not a valid Solana account.

Known limitations — please read

  • Reduces linkability, not anonymity. Stealth addresses stop observers from trivially grouping payments to one recipient. They do not hide amounts, timing, or the sender, and they do not make anyone anonymous.
  • Spending leaks. When you move funds out of several stealth addresses into one wallet, or fund their gas from a known wallet, you re-link them on-chain. Careful spending hygiene is required and not solved by this primitive.
  • Scanning is the cost. The recipient must examine every candidate payment. Real systems add view tags and indexers to make that practical.
  • Compromised scan key = lost privacy. Whoever gets your scan key can see all your incoming payments (though not spend them). Compromised spend key = lost funds.
  • This is a teaching demo, not a wallet. It uses P-256 via the browser, a hand-written curve routine that is not constant-time, and has had no security review. Do not reuse its code or keys for real value.
  • No on-chain component exists yet. There is no program, token, or deployed service behind this page.