owner() getter, and the
owner is bound only through ownerHash. But a hash is a commitment to something,
and the payer still has to find their own boxes again later. Doing that by
remembering “my wallet made these” puts the link back.
Stealth boxes remove it. The box’s owner and vault are a fresh address derived
for that box alone, and only the payer’s viewing key can recognise it. Nobody
else can tell two of your boxes apart, or tell that either is yours.
This is ERC-5564 (
schemeId 1) over secp256k1, announced through a standalone
registry (STEALTH_ANNOUNCER_ADDRESS). It does not touch the factory or the
account, so a stealth box is an ordinary spend box that happens to be owned by an
address nobody can attribute.Keys, from one signature
The payer signs one fixed message and both long-lived keys fall out of it deterministically. Same wallet, same signature (RFC 6979), so the keys are recoverable on any device with nothing stored anywhere.- Viewing key scans announcements. It can recognise your boxes and nothing else. Hand it to a watcher and it can see, not spend.
- Spending key derives the private key that controls a stealth address. It never touches a scan.
Creating a stealth box
Three steps, because the box address does not exist until it is created.1
Derive a fresh owner
2
Create the box owned and vaulted by it
vaultHash is a hash of a 20-byte
address on a transparent chain, which is crackable. The stealth address closes
both ends.3
Announce it
The label rides in the announcement, on purpose. A name kept in the payer’s
browser made a subscription called “Netflix” on one machine an unnamed address on
every other one, and it was the only thing about a subscription not read live from
chain. It is a plaintext string in the metadata, so name boxes the way you would
name them out loud, not with anything you would not publish.
Rediscovery
Nothing is stored. The payer signs the message again, and the announcements identify themselves.STEALTH_ANNOUNCER_DEPLOY_BLOCK, not at 0. Arc caps an
eth_getLogs range at 10,000 blocks, so scanning from the deploy block keeps
discovery to a couple of requests instead of walking the whole chain.
For an offline or pre-fetched batch, the recognition step is pure:
viewTag (the first byte of the shared secret) lets a scanner skip roughly 255 of
every 256 non-matches without doing the full derivation.
Spending and sweeping
To act as the box’s owner or vault, derive the stealth address’s private key:msg.sender == vault.
The vault is the stealth address, so sweeping a stealth box means signing as
stealthAccount, not as the payer’s wallet:
What this does and does not hide
Hidden: which boxes are yours, that two boxes share an owner, and the link from any box back to your wallet. The registry is public, the recognition is not. Not hidden: that a box exists, its merchant, its cap, and the funding transaction that paid into it. Funding is still a plain transfer on a transparent chain, so an observer who watches your wallet fund an address can follow that one hop. Stealth addresses break the rediscovery link, not the funding link. The funding leg is what Arc Privacy Sector closes.Next
- What a box does once it exists: Subscriptions.
- The identity model behind it: Payer-side shield.
- The registry: Contracts.