Read . How it works
Two keys, one secret.
The arithmetic is EIP-5564, scheme 1: elliptic curve keys on secp256k1, the curve every Ethereum account already uses. Nothing new is trusted.
The keys
You hold two private keys: a spending key pspend and a viewing key pview. Their public halves, Pspend and Pview, side by side, are your meta-address: st:eth:0x followed by the two 33-byte keys. On this site both private keys come from one signature of a fixed message in your wallet: the spending key is the keccak256 hash of the signature's r, the viewing key that of its s in its low form. The same wallet gives the same keys every time.
Paying
The sender picks a one-time key r and its public half R. Then:
S = r . Pview, sh = keccak256(S)
Pstealth = Pspend + sh . G
The stealth address is the Ethereum address of Pstealth. The sender pays it, and the payment is announced to the standard ERC-5564 Announcer with R and one byte, the view tag, the first byte of sh.
Finding it
Your inbox reads each announcement Shroud's token and hook made. For each, it computes S = pview . R and compares the first byte of its hash with the view tag: 255 announcements in 256 that are not yours stop there, after one multiplication. For the rest it computes Pstealth and checks the address. When it matches, the stealth private key is
pstealth = pspend + sh (mod n)
which only the holder of the spending key can make. The viewing key alone finds payments but cannot spend them: that is the Audit tab of the wallet.
The announcement
Shroud writes the metadata the standard asks for, so any EIP-5564 wallet can read what was paid:
| Bytes | Field | Holds |
|---|---|---|
| 1 | view tag | the first byte of the hashed shared secret |
| 2 to 5 | 0x23b872dd | an ERC-20 moved |
| 6 to 25 | token | the SHROUD contract |
| 26 to 57 | amount | SHROUD paid, in wei |
| 58 to 61 | 0xeeeeeeee | present only when ETH went too |
| 62 to 81 | 0xEeee...EEeE | the ETH marker address |
| 82 to 113 | ETH amount | the ETH sent along, in wei |
This site computes all of it with the code of stealth.js, which a test proves gives the same addresses, view tags and keys as ScopeLift's stealth-address-sdk, on the SDK's own test keys and on 500 random ones.
Three ways a stealth address is paid
- stealthSend, on the token: SHROUD from your wallet, and any ETH you send along, to the stealth address, announced.
- A stealth buy, through the pool's hook: an ordinary buy whose hookData names the stealth address. The hook takes every SHROUD the swap bought, pays it to the stealth address with up to 0.01 ETH for its gas, and announces it. Your router receives nothing; the least you accept is enforced by the hook.
- A signed move, by relay on the token: a stealth address signs a payment onward, to another stealth address or a plain one, and anyone submits it. If the destination is a stealth address, the signature binds its announcement, so whoever submits cannot redirect it.
Spending without ETH
A stealth address that holds no ETH cannot pay gas, and funding it from your wallet would link the two. So it signs instead: a move (relay) or a sale (sellFor), which anyone may submit. A sale goes through the hook, which sells the SHROUD in the pool and pays the ETH where the holder signed, minus the tip the holder signed for, to whoever submitted it.