What are Stealth Addresses?
A stealth address lets you receive funds or contract calls without linking transactions to your identity. Every sender generates a unique one-time address just for you — nobody watching the chain can tell which transactions belong to the same wallet.
NEXUS implements the ERC-5564 Dual-Key Stealth Address Protocol using secp256k1 ECDH.
Stealth addresses are live in V1. They are separate from Zcash shielded pools (z-addr), which are planned for V2.
How it Works (Plain English)
- You publish a stealth meta-address — two public keys (scan key + spend key)
- Sender looks up your meta-address, generates a random one-time key, and computes a unique address just for this transaction
- Transaction goes to that one-time address — no link to your real identity on-chain
- You scan new transactions using your private scan key to find yours
- You spend using a derived private key only you can compute
You publish: scan_pubkey + spend_pubkey (66 bytes, your stealth meta-address)
Sender:
1. Generate random ephemeral key R
2. ECDH: shared_secret = HKDF(R × scan_pubkey)
3. one_time_address = spend_pubkey + shared_secret × G
4. Attach (R, view_tag) to transaction
You (scanning):
1. For each tx: shared_secret = HKDF(scan_privkey × R)
2. View tag matches? → Compute full address to confirm
3. Match → Spend with: spend_privkey + shared_secret_scalar
Every transaction carries a 1-byte view tag (the first byte of the HKDF output). Before doing any expensive elliptic curve math, the scanner checks the view tag — this filters out 99.6% of non-matching transactions instantly.
Privacy Properties
| Property | Status |
|---|
| Sender cannot be linked to recipient | ✅ |
| Multiple receipts unlinkable | ✅ |
| Works for transfers | ✅ |
| Works for contract calls | ✅ |
| Scan key can be delegated (watch-only) | ✅ |
| Spend key never revealed during scan | ✅ |
Stealth Contract Calls
Stealth addresses work for smart contract interactions too, not just transfers. The NEXUS miner resolves the stealth address to your real address before execution, so contracts see your real msg.sender — approvals, ownership checks, and balances all work correctly. The public block record only shows the unlinkable one-time address.
Using Stealth Addresses (TypeScript SDK)
import {
deriveStealthKeyPair,
encodeStealthMetaAddress,
generateStealthAddress,
getMyStealthTransactions,
deriveStealthPrivkey,
} from '@yattacorp/nexus-sdk';
// ── Recipient: set up your stealth keypair ──────────────────────────────────
const stealthKeys = deriveStealthKeyPair(myMasterPrivkey);
// Publish this — it's your stealth meta-address (safe to share)
const metaAddress = encodeStealthMetaAddress(stealthKeys);
// e.g. "02abc...def02ghi...jkl" (66 bytes hex)
// ── Sender: generate a one-time address for the recipient ───────────────────
const { stealthAddress, ephemeralPubkey, viewTag } = generateStealthAddress(
recipientScanPubkeyHex,
recipientSpendPubkeyHex,
);
// Use stealthAddress as tx.from, attach ephemeralPubkey + viewTag to tx
// ── Recipient: scan for incoming transactions ───────────────────────────────
const myTxs = getMyStealthTransactions(
allStealthTxsFromSnapshot, // fetched from node
stealthKeys.scanPrivkey,
stealthKeys.spendPubkey,
);
// ── Recipient: derive private key to spend ──────────────────────────────────
for (const tx of myTxs) {
const privkey = deriveStealthPrivkey(stealthKeys.spendPrivkey, tx.sharedSecretScalar);
// sign transactions with privkey
}
Key Derivation
Stealth scan and spend keys are derived from your master key using HKDF-SHA256 with domain separation:
scan_privkey = HKDF(master, salt="nexus-stealth-scan", info="scan-key-v2", 32 bytes)
spend_privkey = HKDF(master, salt="nexus-stealth-spend", info="spend-key-v2", 32 bytes)
This means a single master key controls your entire identity, while scan and spend keys are separate — you can share your scan key with a watchtower service without giving it spending ability.
Scanning Privacy
Scanning is fully client-side — your scan private key never leaves your device. You fetch the global stealth transaction snapshot from a NEXUS node, then compute locally which transactions are yours. The node never learns which transactions belong to you.
Cryptographic Details
| Component | Specification |
|---|
| Key agreement | ECDH on secp256k1 |
| Key derivation | HKDF-SHA256 (RFC 5869) |
| Domain | nexus-stealth-address-v2 |
| View tag | 1 byte (first HKDF output byte) |
| Address format | 32-byte x-only pubkey (Taproot-compatible) |
| Comparison | Constant-time to prevent timing attacks |