How it works
Four actors take part: the receiver (you), the sender (the wallet that pays), the router (our contract) and the Pons curve. The announcer is a shared ERC-5564 contract that carries a small public note for each payment.
The flow
- The receiver creates a spending key and a viewing key, and publishes the meta-address built from their public halves.
- The sender draws a fresh random key
rand computes a shared secret with the viewing public key. From it they derive a one-time addressAthat only the receiver can control. - The sender calls
router.buy(...)withAas the destination. The router buys from the Pons curve withAas the recipient, sendsAa little gas, and calls the announcer. - The announcer emits an
Announcementevent holdingA, the public half ofr, and a one-byte view tag. - The receiver scans announcements, uses the view tag to skip the ones that are not theirs, and recognises the ones that are.
- The receiver derives the private key of
Aand can sell or move the tokens.
Why Pons makes this easy
Pons curves expose buy(uint256 quoteIn, uint256 minOut, address recipient). The recipient argument is free: the tokens land wherever you point it. Chameleon simply points it at a stealth address. The router measures the recipient's balance before and after, so it can check the minimum and announce the exact amount.
The derivation
With K_spend and K_view the receiver's public keys, G the curve generator and r the sender's one-time key:
R = r * G published in the announcement
S = r * K_view (= k_view * R) both sides compute the same point
h = keccak256(x coordinate of S)
tag = first byte of h the view tag
K_stealth = K_spend + h * G
address = last 20 bytes of keccak256(K_stealth, uncompressed, without the prefix)
k_stealth = k_spend + h (mod n) only the receiver can compute this
The Try it page and the explorer on the home page run exactly this, with the real bytes.
The view tag
Scanning every announcement with full elliptic-curve maths would be slow. The view tag is the first byte of h, published in the metadata. A scanner computes h with its viewing key and compares one byte. For announcements that are not theirs, 255 out of 256 are rejected right there.
The announcement
The router calls the standard announcer with scheme 1 (secp256k1 with view tags). The metadata follows the ERC-5564 layout for a token transfer, so generic scanners understand it:
| Bytes | Content |
|---|---|
| 0 | view tag |
| 1 to 4 | 0xa9059cbb, the ERC-20 transfer selector |
| 5 to 24 | the token address |
| 25 to 56 | the amount of tokens received |
Gas for the new address
A fresh address has no ETH, so it could not sell. The router forwards a small gasDust with the buy. On this chain gas is around 0.02 gwei, so 0.00002 ETH covers many transactions. See Selling and exiting.
What the chain sees
CurveBuy buyer = router recipient = A
Announcement caller = router stealth address = A view tag = 0x..
Your meta-address and your main wallet appear in neither event.