ERC-5564/ERC-6538 stealth address derivation and scanning.
Implements the secp256k1 stealth address scheme: a sender generates an ephemeral keypair, computes a shared secret with the recipient's viewing key via ECDH, and derives a one-time stealth address. The recipient scans announcements using their viewing key and view tag optimization (256x speedup).
Flow
Sender (generate):
- Parse recipient's meta-address (spending pubkey + viewing pubkey)
- Generate ephemeral keypair (r, R = r*G)
- Compute shared secret: S = r * V (ECDH with viewing pubkey)
- Hash: h = keccak256(compress(S))
- View tag: first byte of h
- Stealth pubkey: P' = P + h*G
- Stealth address: last 20 bytes of keccak256(P'_uncompressed[1:])
Recipient (scan):
- For each announcement, check view tag (fast filter, 1:256 false positive)
- Compute S = v * R (ECDH with ephemeral pubkey)
- Derive expected stealth address
- If match: stealth privkey = (p + h) mod n
Standards
- ERC-5564: Stealth Addresses (announce format)
- ERC-6538: Stealth Meta-Address Registry
- Scheme ID 1: secp256k1 with view tags
Summary
Functions
ERC-5564 Announcer contract address (Ethereum mainnet).
Create metadata bytes with view tag as first byte.
Decode a meta-address from ERC-6538 st:eth:0x format.
Derive stealth keys from an EVM signature (domain-separated).
Encode a meta-address in ERC-6538 st:eth:0x format.
Extract view tag from announcement metadata.
Generate a stealth address from a recipient's meta-address.
Generate a stealth address using a caller-supplied ephemeral private key.
ERC-6538 Registry contract address (Ethereum mainnet).
Scan announcements for payments to our stealth addresses.
Scheme ID for secp256k1 with view tags.
Validate a compressed secp256k1 public key (33 bytes, 02/03 prefix).
Validate a view tag (0-255 integer).
Types
@type announcement() :: %{ scheme_id: pos_integer(), stealth_address: String.t(), caller: String.t(), ephemeral_pub_key: binary(), metadata: binary(), block_number: non_neg_integer(), tx_hash: String.t(), log_index: non_neg_integer() }
@type meta_address() :: %{ spending_pub_key: binary(), viewing_pub_key: binary(), chain_id: pos_integer() }
@type stealth_payment() :: %{ announcement: announcement(), stealth_priv_key: Raxol.Payments.Secret.t() }
Functions
@spec announcer_address() :: String.t()
ERC-5564 Announcer contract address (Ethereum mainnet).
Create metadata bytes with view tag as first byte.
Optionally appends extra data (token address, amount, etc).
@spec decode_meta_address(String.t(), pos_integer()) :: {:ok, meta_address()} | {:error, term()}
Decode a meta-address from ERC-6538 st:eth:0x format.
@spec derive_keys(String.t()) :: {:ok, %{spending: {binary(), binary()}, viewing: {binary(), binary()}}} | {:error, term()}
Derive stealth keys from an EVM signature (domain-separated).
@spec encode_meta_address(meta_address()) :: String.t()
Encode a meta-address in ERC-6538 st:eth:0x format.
Extract view tag from announcement metadata.
The view tag is the first byte of the metadata field.
@spec generate(meta_address()) :: {:ok, settlement()} | {:error, term()}
Generate a stealth address from a recipient's meta-address.
Returns the stealth address, ephemeral public key, and view tag for use in an ERC-5564 announcement.
@spec generate(meta_address(), <<_::256>>) :: {:ok, settlement()} | {:error, term()}
Generate a stealth address using a caller-supplied ephemeral private key.
Identical to generate/1 but deterministic: the same meta-address and
ephemeral key always yield the same stealth address, ephemeral public key,
and view tag. Used for reproducible cross-implementation conformance checks.
@spec registry_address() :: String.t()
ERC-6538 Registry contract address (Ethereum mainnet).
@spec scan(binary(), binary(), [announcement()]) :: {:ok, [stealth_payment()]} | {:error, term()}
Scan announcements for payments to our stealth addresses.
Uses view tag optimization: only performs full ECDH for announcements where the view tag matches (1:256 false positive rate).
Returns a list of discovered payments with their stealth private keys.
@spec scheme_id() :: pos_integer()
Scheme ID for secp256k1 with view tags.
Validate a compressed secp256k1 public key (33 bytes, 02/03 prefix).
Validate a view tag (0-255 integer).