Skip to main content
On public blockchains, every transaction is visible to anyone. When you share a wallet address to receive payment, observers can see your full payment history, track your balance over time, and link your identity to all your on-chain activity. Stealth addresses solve this problem by sending every payment to a fresh, one-time address that only you can detect and spend from — breaking the link between sender, receiver, and payment history entirely.

The problem with reusable addresses

When multiple payments go to the same address, they are trivially linkable:
Normal payments:
Alice → 0xBob
Carol → 0xBob
Dave  → 0xBob

Observer: "Bob received 3 payments totaling 5 ETH"
With stealth addresses, each payment lands at a different one-time address:
Stealth payments:
Alice → 0xRandom1  (owned by Bob)
Carol → 0xRandom2  (owned by Bob)
Dave  → 0xRandom3  (owned by Bob)

Observer: "Three unrelated addresses received funds"
The one-time addresses are mathematically derived so only Bob can detect and spend them. No one else — including the senders — can tell the three addresses belong to the same recipient.

Stealth meta-addresses

Instead of sharing a wallet address, you publish a stealth meta-address. This contains two public keys encoded together:
st:eth:0x{spendingPubKey}{viewingPubKey}
The st:eth: prefix identifies the scheme and chain family. The two keys serve different roles.

Spending key

Used to derive stealth addresses. The private half lets you spend funds sent to any stealth address generated from it.

Viewing key

Used to scan for incoming payments. The private half lets you detect payments without the ability to spend them.
This two-key design is deliberate. You can give a third party your viewing key to watch for incoming payments on your behalf — a delegated scanning service, an accounting tool, or a wallet app — without ever granting spending access. Stealth meta-addresses are stored and resolved via two EVM standards:
  • ERC-6538 — Stealth Meta-Address Registry: stores your meta-address on-chain, keyed to your wallet.
  • ERC-5564 — Stealth Address Messenger: defines the announcement format senders publish after each payment.
Wraith extends these with .wraith names so you can register alice.wraith instead of sharing a raw hex string.

The two-key flow

1

Setup (one time)

You sign a message with your wallet. Wraith derives your spending key and viewing key from that signature inside the TEE, encodes them as a stealth meta-address, and registers it on-chain under your .wraith name.
import { Wraith, Chain } from "@wraith-protocol/sdk";

const wraith = new Wraith({ apiKey: "wraith_..." });

const message = "Sign to create Wraith agent";
const signature = await wallet.signMessage(message);

const agent = await wraith.createAgent({
  name: "alice",
  chain: Chain.Horizen,
  wallet: "0xYourWalletAddress",
  signature,
  message,
});

console.log(agent.info.metaAddresses[Chain.Horizen]);
// "st:eth:0x{spendingPubKey}{viewingPubKey}"
2

Sender generates a stealth address

The sender resolves your .wraith name to your meta-address, then generates a one-time stealth address from it using the low-level crypto primitives:
import { generateStealthAddress } from "@wraith-protocol/sdk/chains/evm";

const { stealthAddress, ephemeralPubKey, viewTag } = generateStealthAddress(
  recipientSpendingPubKey,
  recipientViewingPubKey
);
// stealthAddress  — fresh one-time address to receive funds
// ephemeralPubKey — published on-chain in the announcement
// viewTag         — 1-byte shortcut for fast scanning
Internally the sender:
  1. Generates a random ephemeral key pair (r, R)
  2. Computes a shared secret via ECDH: S = r × viewingPubKey
  3. Hashes the secret: h = keccak256(S)
  4. Derives the stealth address: spendingPubKey + h × G
  5. Sends funds to that address
  6. Publishes an on-chain announcement containing (R, viewTag)
3

Sender publishes an announcement

The announcement is a public on-chain event containing the ephemeral public key R and a 1-byte view tag. It reveals nothing about who the recipient is — it is only useful to someone who holds the matching viewing key.
4

Recipient scans for payments

You scan the chain’s announcements using your viewing key. For each announcement, Wraith checks whether it belongs to you:
import { scanAnnouncements } from "@wraith-protocol/sdk/chains/evm";

const matched = scanAnnouncements(
  announcements,
  keys.viewingKey,
  keys.spendingPubKey,
  keys.spendingKey
);
// matched: announcements that belong to you, with private keys ready
Or via the agent with natural language:
await agent.chat("scan for incoming payments");
5

Recipient derives the private key and spends

For each matched announcement, you derive the private key that controls the stealth address:
import { deriveStealthPrivateKey } from "@wraith-protocol/sdk/chains/evm";

const privateKey = deriveStealthPrivateKey(
  keys.spendingKey,
  ephemeralPubKey,
  keys.viewingKey
);
// stealthPrivateKey = spendingKey + keccak256(sharedSecret) mod n
That private key controls the stealth address and lets you sign any transaction spending the funds.

View tags: fast scanning

Without view tags, checking whether an announcement belongs to you requires a full ECDH computation and elliptic curve point addition for every announcement on-chain. At scale this is slow. View tags add a 1-byte shortcut published alongside each announcement. When scanning, you first compare just this byte:
  • If it does not match, skip — this rejects ~255 out of every 256 non-matching announcements with a single byte comparison.
  • If it does match, proceed to the full ECDH check.
1000 announcements — without view tags:  1000 full ECDH checks
1000 announcements — with view tags:     1000 byte comparisons + ~4 full checks
View tags are part of the ERC-5564 announcement format and are used by Wraith on all supported chains.

Visual flow

SETUP (one time)
┌─────────────────────────────────────────────┐
│  Wallet signs message                        │
│       │                                      │
│       ▼                                      │
│  Derive spending key + viewing key (in TEE)  │
│       │                                      │
│       ▼                                      │
│  Encode stealth meta-address                 │
│  "st:eth:0x{spend}{view}"                    │
│       │                                      │
│       ▼                                      │
│  Register as alice.wraith on-chain           │
└─────────────────────────────────────────────┘

SEND (per payment)
┌─────────────────────────────────────────────┐
│  Sender resolves alice.wraith                │
│       │                                      │
│       ▼                                      │
│  Generate random ephemeral key               │
│       │                                      │
│       ▼                                      │
│  ECDH shared secret with viewing key         │
│       │                                      │
│       ▼                                      │
│  Derive one-time stealth address             │
│       │                                      │
│       ▼                                      │
│  Send funds + publish announcement           │
└─────────────────────────────────────────────┘

RECEIVE (scanning)
┌─────────────────────────────────────────────┐
│  Fetch announcements from chain              │
│       │                                      │
│       ▼                                      │
│  For each: check view tag (fast reject)      │
│       │                                      │
│       ▼                                      │
│  If tag matches: compute full address        │
│       │                                      │
│       ▼                                      │
│  If address matches: derive private key      │
│       │                                      │
│       ▼                                      │
│  Spend from stealth address                  │
└─────────────────────────────────────────────┘

EVM vs. Stellar

The same concept applies on both chain families, adapted to their respective cryptographic primitives:
StepEVM (secp256k1)Stellar (ed25519)
Key derivationkeccak256(r) of wallet signatureSHA-256("wraith:spending:" || sig)
ECDHsecp256k1.getSharedSecretX25519 (Montgomery form)
Hash to scalarkeccak256(S) mod nSHA-256("wraith:scalar:" || S) mod L
View tagkeccak256(S)[0]SHA-256("wraith:tag:" || S)[0]
Address format0x... (20 bytes)G... (56 chars)
Signingsecp256k1 ECDSAed25519 with raw scalar

Standards

EVM stealth addresses in Wraith are built on two Ethereum standards:
  • ERC-5564 — defines the announcement format: ephemeral public key, view tag, and stealth address, emitted as an on-chain event after each payment.
  • ERC-6538 — defines the meta-address registry: a contract that stores your stealth meta-address on-chain keyed to your wallet address.
Wraith adds three extensions on top:
  • WraithNames — maps human-readable .wraith names to meta-addresses on-chain, so senders use alice.wraith instead of raw hex.
  • WraithSender — executes the send and the announcement in a single atomic transaction, so they can never get out of sync.
  • WraithWithdrawer — sponsors gas for stealth address withdrawals via EIP-7702, so recipients do not need ETH on the stealth address to spend it.
When using the Wraith agent, all of this — key derivation, address generation, announcement, scanning, and spending — is handled automatically. You interact entirely through natural language via agent.chat().

Next steps

Wraith agents and identity

Learn how agents hold your identity and keys on the Wraith network.

Privacy model and TEE security

Understand how keys are protected and how to avoid privacy pitfalls.