Repo Explainer: `stealth-addresses` and the One-Byte Trick Behind Private Payments

A Solidity implementation of ERC-5564 and ERC-6538 that makes stealth addresses usable by shrinking wallet scanning to a tiny filter, then doing the heavy cryptography only when a match looks real.

11 min read • View on GitHub • More from gear-foundation

A public notice wall filled with sealed envelopes, while a small brass tag on each envelope catches a scanner's eye before the rest of the contents are examined. The scene explains that the real breakthrough is not hiding every message, but letting a wallet skip almost all of them cheaply.
The practical win is not secrecy alone. It is making discovery cheap enough that private payments can live inside normal wallets.
Key Takeaways

Privacy is a scan problem

Public chains make every payment legible by default. That is the obvious problem. The less obvious one is discovery: if a wallet has to inspect every announcement to see whether a payment belongs to you, privacy becomes expensive, slow, and easy to ignore.

The wallet checks the view tag first, then spends real cryptographic effort only on likely matches.

That is the opening move in stealth-addresses. The repository does not treat the stealth address as the hard part. It treats the scan loop as the hard part, and the one-byte view tag is the answer. Most announcements can be rejected with almost no work, which is why this approach feels like infrastructure instead of a demo.

How stealth-addresses turns a meta-address into a spendable output

The sender starts from a stealth meta-address, which bundles the recipient's spending public key and viewing public key. The sender picks an ephemeral key, derives a shared secret with the viewing key, and hashes that secret into two things: a view tag for quick filtering and a tweak for the final address derivation. The recipient can later reproduce the same path with their viewing key, then claim the funds with the spending key.

// Simplified sketch of the ERC-5564 derivation
bytes32 sharedSecret = keccak256(abi.encodePacked(
    ecdh(ephemeralPrivKey, recipientViewingPubKey)
));

bytes1 viewTag = bytes1(sharedSecret);
bytes32 tweak = keccak256(abi.encodePacked(sharedSecret, metadata));

// P_stealth = P_spend + H(e * P_view) * G
address stealthAddress = deriveAddress(recipientSpendingPubKey, tweak);

That formula is the heart of the repo. The sender never reuses the recipient's static address, but the recipient can still recognize the output later because the same shared secret lands on both sides. The result is unlinkability without a new consensus rule, which is why the implementation matters more than the slogan.

The two-contract wrapper around the math

The math lives in StealthAddresses.sol, but the user-facing protocol is split into contracts that do different jobs. ERC5564Announcer emits the announcement event with the ephemeral public key and metadata, ERC5564AnnouncerWithHooks adds curve-point validation before the log is written, and ERC6538Registry maps a user to a stealth meta-address with signed registration flows. That separation is the product decision hiding inside the cryptography.

The registry supports relayed registration through signed messages, so a user can authorize a third party to publish keys without giving up control. The announcer keeps broadcast mechanics simple. The hook-based variant makes malformed inputs harder to spam into the system.

Stealth addresses, defined under the ERC-5564 standard, are a specialized method for generating one-time, unlinkable addresses for on-chain payments. Instead of a recipient publishing a static address that everyone uses, they publish a "stealth meta-address." Senders then use this meta-address to derive a fresh, unique Ethereum address for each transaction.

Joan Alavedra, Author · Deep dive: Stealth Addresses

Why this implementation matters as a standards piece

This repo is not inventing a brand-new privacy model. It is making ERC-5564 and ERC-6538 concrete in Solidity 0.8.33, with Foundry as the build and test harness and frost-secp256k1-evm supplying the low-level curve operations the EVM does not normally expose. That matters because on-chain secp256k1 math is unusual, and most privacy systems push this logic off-chain or into heavier proof systems.

The implementation choice says a lot. By keeping the protocol legible in Solidity, the repo makes it easier for wallet teams, auditors, and protocol designers to reason about the trust boundary. It is less ambitious than full shielded transactions, but far easier to slot into existing Ethereum-style payment flows.

Where stealth addresses sit in the privacy stack

Stealth addresses are not trying to solve every privacy problem at once. They hide recipient linkage, not the full transaction graph. That makes them useful in a different way than mixers or zero-knowledge systems, which aim at a broader concealment but come with heavier operational and cryptographic costs.

ApproachWhat it optimizes forMain trade-off
Stealth addressesRecipient unlinkability with cheap scanningDoes not hide amount or sender by itself
MixersBreaking source-destination links through pooled liquidityNeeds liquidity and adds operational friction
Zero-knowledge privacy systemsHiding sender, recipient, and often amountHeavier proving and verification complexity

That is the real niche of stealth-addresses. It makes private receiving practical enough to fit normal wallets, while staying narrow enough to implement, audit, and integrate. In a market crowded with privacy rhetoric, that kind of specificity is the point.