Skip to main content
Non-interactive privacy protocol enabling senders to generate one-time addresses that only recipients can detect and spend from.
Implements ERC-5564 stealth address specification for SECP256k1.

Overview

Stealth addresses protect recipient privacy by generating unique one-time addresses for each transaction. The sender can generate these addresses without interacting with the recipient, and only the recipient can:
  • Detect payments to their stealth addresses (using viewing key)
  • Spend funds from stealth addresses (using spending key)
Key Properties:
  • Non-interactive: No communication between sender and recipient required
  • Forward secrecy: Past transactions remain private if viewing key compromised
  • Unlinkability: Stealth addresses appear unrelated to each other and recipient

Quick Start

import * as StealthAddress from '@tevm/voltaire/StealthAddress';
import * as Secp256k1 from '@tevm/voltaire/Secp256k1';

// Generate key pairs
const spendingPrivKey = crypto.getRandomValues(new Uint8Array(32));
const viewingPrivKey = crypto.getRandomValues(new Uint8Array(32));

// Derive public keys
const spendingPubKey = StealthAddress.compressPublicKey(
  Secp256k1.derivePublicKey(spendingPrivKey)
);
const viewingPubKey = StealthAddress.compressPublicKey(
  Secp256k1.derivePublicKey(viewingPrivKey)
);

// Generate meta-address (publish this)
const metaAddress = StealthAddress.generateMetaAddress(
  spendingPubKey,
  viewingPubKey
);

console.log('Meta-address:', Buffer.from(metaAddress).toString('hex'));
// Share meta-address publicly (66 bytes)

How It Works

1. Setup (Recipient)

Recipient generates two key pairs:
  • Spending keys: For spending funds (keep spending private key secret)
  • Viewing keys: For scanning blockchain (can be delegated to scanner)
Combines compressed public keys into 66-byte meta-address and publishes it.

2. Payment (Sender)

Sender uses recipient’s meta-address to:
  1. Generate ephemeral key pair
  2. Perform ECDH with recipient’s spending public key
  3. Derive stealth address using keccak256
  4. Generate 1-byte view tag for efficient scanning
  5. Publish ephemeral public key and view tag (announcement)
  6. Send payment to stealth address

3. Detection (Recipient)

Recipient scans announcements:
  1. Check view tag first (cheap 1-byte comparison)
  2. If view tag matches, perform full check (ECDH + address derivation)
  3. If match confirmed, compute stealth private key for spending

API Documentation

View Tags Optimization

View tags reduce scanning cost by ~99%:
// Without view tag: Check every announcement (expensive)
for (const announcement of announcements) {
  // Perform full ECDH + derivation (~1000 gas equivalent)
  const result = checkStealthAddress(...);
}

// With view tag: Filter first (cheap)
for (const { viewTag, ...announcement } of announcements) {
  const expected = computeViewTag(viewingPrivKey, ephemeralPubKey);
  if (viewTag !== expected) continue; // Skip 99% of announcements

  // Only check 1% that match view tag
  const result = checkStealthAddress(...);
}

Privacy Considerations

Forward Secrecy:
  • Viewing key compromise: Past stealth addresses remain private
  • Spending key compromise: Catastrophic (all funds at risk)
  • Best practice: Use hardware wallet for spending key
Metadata Leakage:
  • Transaction amounts visible on-chain
  • Timing correlation possible
  • Consider mixing with other privacy techniques
Key Management:
  • Spending key: Never expose (offline/hardware wallet)
  • Viewing key: Can delegate to scanner service
  • Ephemeral keys: Generate fresh for each payment

Specification References