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

# Stealth payments

> EIP-5564 meta-addresses, DKSAP, announcements, and the receive flow.

Opaque stealth payments follow [CSAP](https://github.com/opaquecash/spec/blob/main/CSAP.md) (Confidential Stealth Address Protocol), built on [EIP-5564](https://eips.ethereum.org/EIPS/eip-5564) scheme 1 (secp256k1).

## Key material

When a user signs `SETUP_MESSAGE`, the SDK derives:

| Key              | Role                                                       |
| ---------------- | ---------------------------------------------------------- |
| **Viewing key**  | Prefilter announcements (view tags) and scan owned outputs |
| **Spending key** | Reconstruct one-time private keys to sweep funds           |

These keys are **chain-neutral**: the same wallet has one meta-address across Ethereum, Solana, and Starknet.

## Meta-address

A 98-byte value (viewing pubkey ‖ secp256k1 spending pubkey ‖ ed25519 Solana spend pubkey) registered on `StealthMetaAddressRegistry`. Share it so senders can derive one-time destinations without linking payments to your public identity. (Legacy 66-byte `V‖S` values still work for Ethereum.)

The same meta-address serves all three chains — only custody differs: an EVM address on Ethereum, an ed25519 account on Solana, and a counterfactual OpenZeppelin `EthAccount` (signed by the one-time secp256k1 stealth key `P_stealth`) on Starknet.

```ts theme={"dark"}
const meta = client.getMetaAddressHex();
await client.registerMetaAddress("ethereum");
```

## Send flow

1. Resolve recipient meta-address (registry lookup or direct hex)
2. `prepareStealthSend(meta)` derives an ephemeral keypair + one-time stealth address
3. Transfer native asset to the stealth address
4. `announce` on `StealthAddressAnnouncer` so the recipient can scan

```ts theme={"dark"}
const send = client.prepareStealthSend(metaAddressHex);
// send.stealthAddress, send.ephemeralPublicKey, send.metadata
```

Or use `sendStealthPayment` for a single high-level call. To weaken timing analysis,
delay the announcement or interleave decoy announcements; see
[Anonymity utilities](/sdk/stealth-api#anonymity-utilities).

<Note>
  Starknet send and sweep go through dedicated builders — `buildStarknetStealthSend`
  and `buildStarknetSweep` — that return unsigned calls for the app's Starknet wallet to
  broadcast; the generic `sendStealthPayment` facade covers Ethereum and Solana only. See
  [Starknet](/concepts/starknet) for the full flow.
</Note>

## Receive flow

1. Indexer or adapter fetches `Announce` events
2. WASM view-tag prefilter + DKSAP ownership check (`filterOwnedAnnouncements`)
3. Reconstruct signing key (`getStealthSignerPrivateKey`)
4. Sweep to a fresh address (`sweep`)

```ts theme={"dark"}
const owned = await client.scan({ chains: ["ethereum", "solana", "starknet"] });
await client.sweep({ output: owned[0], chain: owned[0].chain, destination });
```

## Ghost receive

Receive without a prior on-chain announcement: derive a stealth address to yourself, fund it, then announce later using the stored ephemeral private key.

```ts theme={"dark"}
const ghost = client.prepareGhostReceive();
// Persist ghost.ephemeralPrivateKey securely
const announce = client.buildAnnounceTransactionRequestForGhost(ghost.ephemeralPrivateKey);
```
