# NoN stealth SDK

One ES module, `non-stealth.js`, that implements ERC-5564 stealth addresses (scheme 1: secp256k1 with view tags) and the ERC-6538 registry calls for Robinhood Chain (4663). It is the same code the NoN Bond wallet page runs, so anything you derive with it is found by that page and by any other ERC-5564 wallet, and vice versa. It needs ethers v6: in a browser that loaded `/vendor/ethers.min.js` it picks up `globalThis.ethers`, anywhere else call `useEthers(ethers)` once.

Constants: `ANNOUNCER` 0x55649E01B5Df198D18D95b5cc5051630cfD45564, `REGISTRY` 0x6538E6bf4B0eBd30A8Ea093027Ac2422ce5d6538, `SCHEME_ID` 1, `CHAIN_ID` 4663, `ANNOUNCEMENT_TOPIC`, `N0N`, `RPCS`.

## The five calls

- `generateKeys()` returns `{spendPriv, viewPriv, spendPub, viewPub}`. Keep the private keys; publish the meta-address.
- `metaAddressOf(keys)` returns the 66-byte meta-address (spendPub33 ++ viewPub33) as hex. `metaAddressText(bytes)` wraps it as `st:rh:0x...`; `parseMetaAddress(strOrBytes)` reads either form back.
- `deriveStealth(meta)` (sender) returns `{stealthAddress, ephemeralPubKey, viewTag, sharedSecret}` for a fresh ephemeral key. Pay `stealthAddress`, then announce.
- `buildMetadata({viewTag, token, amount})` returns the ERC-5564 metadata: view tag, then `0xeeeeeeee` + 20 x `0xee` for ETH or the transfer selector + token address for an ERC-20, then the 32-byte amount. `announceCalldata({stealthAddress, ephemeralPubKey, metadata})` and `registerCalldata(metaBytes)` encode the two contract calls; send them with any wallet.
- `checkAnnouncement(viewPriv, spendPub, announcement)` (recipient) returns the stealth address when an announcement is yours, else `null`; it checks the view tag byte before doing the full derivation. `stealthPrivateKey(spendPriv, viewPriv, ephemeralPubKey)` gives the key that spends from it.

Helpers for scanning: `announcementFilter(fromBlock, toBlock)` builds the `eth_getLogs` filter (the official node accepts at most 100,000 blocks per call), `decodeAnnouncement(log)` turns a log into `{stealthAddress, ephemeralPubKey, metadata, caller, blockNumber, txHash}`, `stealthMetaAddressOfCalldata(addr)` + `decodeMetaAddressResult(hex)` read the registry.

## Example

```js
import * as non from './non-stealth.js';
import {ethers} from 'ethers';             // skip in a browser that loaded /vendor/ethers.min.js
non.useEthers(ethers);

// recipient, once
const keys = non.generateKeys();
const meta = non.metaAddressOf(keys);      // register: tx {to: non.REGISTRY, data: non.registerCalldata(meta)}

// sender
const d = non.deriveStealth(meta);         // pay d.stealthAddress 0.01 ETH, then:
const metadata = non.buildMetadata({viewTag: d.viewTag, token: null, amount: ethers.parseEther('0.01')});
const announceTx = {to: non.ANNOUNCER, data: non.announceCalldata({stealthAddress: d.stealthAddress, ephemeralPubKey: d.ephemeralPubKey, metadata})};

// recipient, later, for each decoded Announcement log
const mine = non.checkAnnouncement(keys.viewPriv, keys.spendPub, {stealthAddress: d.stealthAddress, ephemeralPubKey: d.ephemeralPubKey, metadata});
const priv = mine && non.stealthPrivateKey(keys.spendPriv, keys.viewPriv, d.ephemeralPubKey);   // spends from mine
```

MIT license. Audited by nobody. Use small amounts. Bugs: the NoN Bond bug bounty at nonbond.trade/task covers this file.
