> ## Documentation Index
> Fetch the complete documentation index at: https://voltaire.tevm.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# StealthAddress

> EIP-5564 stealth addresses for privacy-preserving transactions using secp256k1 cryptography

## Overview

StealthAddress implements [EIP-5564](https://eips.ethereum.org/EIPS/eip-5564) stealth addresses for privacy-preserving, non-interactive address generation. Senders can generate one-time addresses that only the intended recipient can detect and spend from, without requiring any prior communication.

```typescript theme={null}
import * as StealthAddress from '@tevm/voltaire/StealthAddress'
import * as Secp256k1 from '@tevm/voltaire/Secp256k1'

// Recipient: Generate key pairs and meta-address
const spendingPrivKey = crypto.getRandomValues(new Uint8Array(32))
const viewingPrivKey = crypto.getRandomValues(new Uint8Array(32))

const spendingPubKey = StealthAddress.compressPublicKey(
  Secp256k1.derivePublicKey(spendingPrivKey)
)
const viewingPubKey = StealthAddress.compressPublicKey(
  Secp256k1.derivePublicKey(viewingPrivKey)
)
const metaAddress = StealthAddress.generateMetaAddress(spendingPubKey, viewingPubKey)

// Sender: Generate stealth address from recipient's meta-address
const ephemeralPrivKey = crypto.getRandomValues(new Uint8Array(32))
const { stealthAddress, ephemeralPublicKey, viewTag } =
  StealthAddress.generateStealthAddress(metaAddress, ephemeralPrivKey)

// Sender publishes: stealthAddress, ephemeralPublicKey, viewTag on-chain

// Recipient: Scan announcements to detect payments
const result = StealthAddress.checkStealthAddress(
  viewingPrivKey,
  ephemeralPublicKey,
  viewTag,
  spendingPubKey,
  stealthAddress
)

if (result.isForRecipient) {
  const stealthPrivKey = StealthAddress.computeStealthPrivateKey(
    spendingPrivKey,
    result.stealthPrivateKey!
  )
  // Use stealthPrivKey to spend from stealthAddress
}
```

## How It Works

EIP-5564 stealth addresses use ECDH (Elliptic Curve Diffie-Hellman) key exchange:

1. **Recipient publishes meta-address** - Concatenation of spending and viewing public keys (66 bytes)
2. **Sender generates ephemeral key pair** - One-time keys for this payment
3. **Sender computes shared secret** - ECDH between ephemeral private key and recipient's viewing public key
4. **Sender derives stealth address** - Combines shared secret with recipient's spending public key
5. **Sender announces on-chain** - Publishes ephemeral public key and view tag
6. **Recipient scans announcements** - Uses view tag for fast filtering (\~6x faster), verifies matches
7. **Recipient computes stealth private key** - Combines spending private key with shared secret to spend

## API

### generateMetaAddress

Creates a 66-byte stealth meta-address from spending and viewing public keys.

```typescript theme={null}
function generateMetaAddress(
  spendingPubKey: SpendingPublicKey,
  viewingPubKey: ViewingPublicKey
): StealthMetaAddress
```

```typescript theme={null}
const spendingPubKey = StealthAddress.compressPublicKey(
  Secp256k1.derivePublicKey(spendingPrivKey)
)
const viewingPubKey = StealthAddress.compressPublicKey(
  Secp256k1.derivePublicKey(viewingPrivKey)
)

const metaAddress = StealthAddress.generateMetaAddress(spendingPubKey, viewingPubKey)
console.log(metaAddress.length) // 66
```

Throws `InvalidPublicKeyError` if either public key is not 33 bytes.

### parseMetaAddress

Parses a 66-byte meta-address into its component public keys.

```typescript theme={null}
function parseMetaAddress(metaAddress: StealthMetaAddress): {
  spendingPubKey: SpendingPublicKey
  viewingPubKey: ViewingPublicKey
}
```

```typescript theme={null}
const { spendingPubKey, viewingPubKey } = StealthAddress.parseMetaAddress(metaAddress)
console.log(spendingPubKey.length) // 33
console.log(viewingPubKey.length) // 33
```

Throws `InvalidStealthMetaAddressError` if meta-address is not 66 bytes.

### generateStealthAddress

Generates a stealth address from a meta-address using an ephemeral private key.

```typescript theme={null}
function generateStealthAddress(
  metaAddress: StealthMetaAddress,
  ephemeralPrivateKey: Uint8Array
): GenerateStealthAddressResult
```

**Returns:**

* `stealthAddress` - 20-byte Ethereum address
* `ephemeralPublicKey` - 33-byte compressed public key (publish on-chain)
* `viewTag` - 1-byte view tag for fast scanning (publish on-chain)

```typescript theme={null}
const ephemeralPrivKey = crypto.getRandomValues(new Uint8Array(32))

const result = StealthAddress.generateStealthAddress(metaAddress, ephemeralPrivKey)

console.log(result.stealthAddress)      // AddressType (20 bytes)
console.log(result.ephemeralPublicKey)  // 33 bytes
console.log(result.viewTag)             // 0-255
```

Throws `StealthAddressGenerationError` if:

* Ephemeral private key is not 32 bytes
* Meta-address is invalid
* Cryptographic operation fails

### checkStealthAddress

Checks if a stealth address announcement belongs to the recipient.

```typescript theme={null}
function checkStealthAddress(
  viewingPrivateKey: Uint8Array,
  ephemeralPublicKey: EphemeralPublicKey,
  viewTag: ViewTag,
  spendingPublicKey: SpendingPublicKey,
  stealthAddress: AddressType
): CheckStealthAddressResult
```

**Returns:**

* `isForRecipient` - `true` if the stealth address belongs to this recipient
* `stealthPrivateKey` - 32-byte shared secret hash (use with `computeStealthPrivateKey`)

```typescript theme={null}
const result = StealthAddress.checkStealthAddress(
  viewingPrivKey,
  announcement.ephemeralPublicKey,
  announcement.viewTag,
  spendingPubKey,
  announcement.stealthAddress
)

if (result.isForRecipient) {
  console.log('Found stealth payment!')
  // result.stealthPrivateKey contains the shared secret hash
}
```

The view tag enables fast rejection: \~255/256 non-matching addresses are rejected after a single byte comparison, before expensive EC operations.

### computeStealthPrivateKey

Computes the full stealth private key for spending.

```typescript theme={null}
function computeStealthPrivateKey(
  spendingPrivateKey: Uint8Array,
  hashedSharedSecret: Uint8Array
): Uint8Array
```

Implements: `stealthPrivateKey = (spendingPrivateKey + hashedSharedSecret) mod n`

```typescript theme={null}
if (result.isForRecipient) {
  const stealthPrivKey = StealthAddress.computeStealthPrivateKey(
    spendingPrivKey,
    result.stealthPrivateKey!
  )
  // Use stealthPrivKey to sign transactions from stealthAddress
}
```

### compressPublicKey

Compresses a 64-byte uncompressed public key to 33-byte compressed format.

```typescript theme={null}
function compressPublicKey(uncompressed: Uint8Array): Uint8Array
```

```typescript theme={null}
const uncompressed = Secp256k1.derivePublicKey(privateKey) // 64 bytes
const compressed = StealthAddress.compressPublicKey(uncompressed) // 33 bytes

// First byte is 0x02 (even y) or 0x03 (odd y)
console.log(compressed[0]) // 2 or 3
```

### decompressPublicKey

Decompresses a 33-byte compressed public key to 64-byte uncompressed format.

```typescript theme={null}
function decompressPublicKey(compressed: Uint8Array): Uint8Array
```

```typescript theme={null}
const compressed = new Uint8Array(33)
compressed[0] = 0x02 // even y prefix
// ... set x-coordinate

const uncompressed = StealthAddress.decompressPublicKey(compressed)
console.log(uncompressed.length) // 64
```

### computeViewTag

Extracts the view tag (first byte) from a hashed shared secret.

```typescript theme={null}
function computeViewTag(hashedSharedSecret: Uint8Array): ViewTag
```

```typescript theme={null}
import * as Keccak256 from '@tevm/voltaire/Keccak256'

const sharedSecret = new Uint8Array(32)
const hash = Keccak256.hash(sharedSecret)
const viewTag = StealthAddress.computeViewTag(hash)

console.log(viewTag >= 0 && viewTag <= 255) // true
```

### parseAnnouncement

Parses announcement data into ephemeral public key and view tag.

```typescript theme={null}
function parseAnnouncement(announcement: Uint8Array): {
  ephemeralPublicKey: EphemeralPublicKey
  viewTag: ViewTag
}
```

```typescript theme={null}
const announcement = new Uint8Array(34) // 33 + 1
const { ephemeralPublicKey, viewTag } = StealthAddress.parseAnnouncement(announcement)

console.log(ephemeralPublicKey.length) // 33
console.log(typeof viewTag) // 'number'
```

## Types

### StealthMetaAddress

66-byte stealth meta-address (spending public key + viewing public key).

```typescript theme={null}
type StealthMetaAddress = Uint8Array & {
  readonly __tag: "StealthMetaAddress"
}
```

### SpendingPublicKey

33-byte compressed secp256k1 public key for deriving stealth addresses.

```typescript theme={null}
type SpendingPublicKey = Uint8Array & {
  readonly __tag: "SpendingPublicKey"
}
```

### ViewingPublicKey

33-byte compressed secp256k1 public key for scanning the blockchain.

```typescript theme={null}
type ViewingPublicKey = Uint8Array & {
  readonly __tag: "ViewingPublicKey"
}
```

### EphemeralPublicKey

33-byte one-time public key generated by sender and announced on-chain.

```typescript theme={null}
type EphemeralPublicKey = Uint8Array & {
  readonly __tag: "EphemeralPublicKey"
}
```

### ViewTag

Single byte (0-255) for fast rejection of non-matching stealth addresses.

```typescript theme={null}
type ViewTag = number
```

### GenerateStealthAddressResult

```typescript theme={null}
interface GenerateStealthAddressResult {
  stealthAddress: AddressType
  ephemeralPublicKey: EphemeralPublicKey
  viewTag: ViewTag
}
```

### CheckStealthAddressResult

```typescript theme={null}
interface CheckStealthAddressResult {
  isForRecipient: boolean
  stealthPrivateKey?: Uint8Array  // Hashed shared secret (offset)
}
```

### StealthAnnouncement

```typescript theme={null}
interface StealthAnnouncement {
  ephemeralPublicKey: EphemeralPublicKey
  viewTag: ViewTag
  stealthAddress: AddressType
}
```

## Constants

```typescript theme={null}
const STEALTH_META_ADDRESS_SIZE = 66     // spendingPubKey (33) + viewingPubKey (33)
const COMPRESSED_PUBLIC_KEY_SIZE = 33    // 0x02/0x03 prefix + x-coordinate
const UNCOMPRESSED_PUBLIC_KEY_SIZE = 64  // x (32) + y (32)
const PRIVATE_KEY_SIZE = 32
const VIEW_TAG_SIZE = 1
const SCHEME_ID = 1                      // ERC-5564 scheme for SECP256k1 with view tags
```

## Errors

### StealthAddressError

Base error for all stealth address operations.

```typescript theme={null}
import { StealthAddressError } from '@tevm/voltaire/StealthAddress'

try {
  StealthAddress.checkStealthAddress(...)
} catch (error) {
  if (error instanceof StealthAddressError) {
    console.log(error.code)    // 'CHECK_FAILED'
    console.log(error.message)
  }
}
```

### InvalidStealthMetaAddressError

Thrown when meta-address format or length is invalid.

```typescript theme={null}
import { InvalidStealthMetaAddressError } from '@tevm/voltaire/StealthAddress'
```

### InvalidPublicKeyError

Thrown when public key format or length is invalid.

```typescript theme={null}
import { InvalidPublicKeyError } from '@tevm/voltaire/StealthAddress'
```

### StealthAddressGenerationError

Thrown when stealth address generation fails.

```typescript theme={null}
import { StealthAddressGenerationError } from '@tevm/voltaire/StealthAddress'
```

### InvalidAnnouncementError

Thrown when announcement format is invalid.

```typescript theme={null}
import { InvalidAnnouncementError } from '@tevm/voltaire/StealthAddress'
```

## Privacy Considerations

**View Tag Tradeoff**: The view tag reduces scanning overhead by \~6x but leaks 1 byte of the shared secret. This is considered acceptable for the performance benefit.

**Key Separation**: Use separate spending and viewing keys. The viewing key can be delegated to a scanning service without risking funds.

**On-chain Privacy**: Stealth addresses are unlinkable to the recipient's public identity. Only the recipient (with viewing key) can identify which payments are theirs.

**Metadata Leakage**: The sender's identity may still be linkable through transaction graph analysis, timing, or gas funding patterns.

## See Also

* [EIP-5564 Specification](https://eips.ethereum.org/EIPS/eip-5564) - Stealth Addresses standard
* [Address](/primitives/address) - Ethereum address primitive
* [Secp256k1](/crypto/secp256k1) - ECDH and key derivation
* [Keccak256](/crypto/keccak256) - Hash functions for shared secrets
