Chameleon
Start
OverviewHow it worksQuickstart
Concepts
ConceptsPrivacy model
Reference
The routerJavaScript engineContracts and addresses
Practice
Real examplesSelling and exitingSecurity and limitsFAQChangelog
Robinhood Chain · 4663

JavaScript engine

site/assets/core/stealth.js is the whole client side: key derivation, address derivation, scanning and call encoding. It runs in the browser and in Node, depends only on @noble/curves and @noble/hashes (bundled), and never sends a key anywhere.

Constants

NameValue
SCHEME_ID1 (ERC-5564, secp256k1 with view tags)
ANNOUNCER0x55649E01B5Df198D18D95b5cc5051630cfD45564
REGISTRY0x6538E6bf4B0eBd30A8Ea093027Ac2422ce5d6538
ANNOUNCEMENT_TOPICkeccak256("Announcement(uint256,address,address,bytes,bytes)")
ROUTER_BUY_SIGbuy(address,uint256,address,bytes,bytes1,uint256)

Keys

FunctionReturns
randomKeys(){ spendPriv, viewPriv, spendPub, viewPub, metaAddress } from 32 random bytes.
deriveKeys(seed)The same shape, deterministically from 32 or more bytes, for example a wallet signature.
keysFromPrivates(spendPriv, viewPriv)Rebuilds the full set from two private keys.
encodeMeta(spendPub, viewPub) / parseMeta(meta)Meta-address string and back. parseMeta rejects malformed input and points that are not on the curve.

Sender

FunctionReturns
generateStealth(metaAddress, ephemeralPriv?){ stealthAddress, ephemeralPubKey, ephemeralPriv, viewTag }. A fresh random key is drawn when none is given.
encodeBuy({ curve, minTokensOut, stealth, ephemeralPubKey, viewTag, gasDust })The hex call data for router.buy. Checked byte for byte against cast calldata.

Receiver

FunctionReturns
checkAnnouncement(a, viewPriv, spendPub)The stealth address if the announcement is yours, otherwise null. Checks the view tag first.
stealthPrivateKey(ephemeralPubKey, spendPriv, viewPriv)The private key that controls that stealth address.
scan(announcements, keys){ found, scanned, skipped }. Each found item carries its private key and parsed metadata.
decodeAnnouncementLog(log)Turns an eth_getLogs entry into an announcement object.

Metadata

FunctionReturns
buildMetadata(viewTag, token, amount)The 57-byte metadata the router publishes.
parseMetadata(metadata){ viewTag, token, amount }.

Helpers

FunctionReturns
pubToAddress(pub) / privToAddress(priv)A checksummed address.
toChecksum(addr)EIP-55 checksum. Verified against cast to-check-sum-address.
explain(keys)Every intermediate value of one derivation, used by the explorer on the home page.
addressHue(addr)A hue from 0 to 359 taken from the address, used to colour it on the site.

Verified against independent code

The 12 tests in tests-js/engine.test.mjs compare the engine with Foundry's cast: the address derived from a stealth private key matches cast wallet address for random keys, the checksum encoder matches, the router call data is identical to cast calldata, the event hash matches cast keccak, and log decoding round-trips a log built with cast abi-encode.