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
| Name | Value |
|---|---|
SCHEME_ID | 1 (ERC-5564, secp256k1 with view tags) |
ANNOUNCER | 0x55649E01B5Df198D18D95b5cc5051630cfD45564 |
REGISTRY | 0x6538E6bf4B0eBd30A8Ea093027Ac2422ce5d6538 |
ANNOUNCEMENT_TOPIC | keccak256("Announcement(uint256,address,address,bytes,bytes)") |
ROUTER_BUY_SIG | buy(address,uint256,address,bytes,bytes1,uint256) |
Keys
| Function | Returns |
|---|---|
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
| Function | Returns |
|---|---|
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
| Function | Returns |
|---|---|
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
| Function | Returns |
|---|---|
buildMetadata(viewTag, token, amount) | The 57-byte metadata the router publishes. |
parseMetadata(metadata) | { viewTag, token, amount }. |
Helpers
| Function | Returns |
|---|---|
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.