RH402 documentation
RH402 is a payment layer for USDG on Robinhood Chain (chain id 4663). You use it with the wallet you already have (MetaMask, Rabby, OKX, Bitget Wallet, Coinbase Wallet, any injected wallet). A username such as @ada is optional: an ERC-721 token whose owner receives every payment sent to that name. Sending is gasless for you: RH402 keeps your wallet topped up with the ETH it needs.
Your wallet, no gas
Connect a wallet at rh402.xyz/app. The app adds Robinhood Chain (chain id 4663) to the wallet if it is missing. You keep your keys and sign every transaction yourself; RH402 only makes sure the gas is there.
Before each transaction the app calls POST /v1/gas/drip with the exact transaction it is about to send. RH402 simulates that transaction from your wallet (it must be an RH402 or USDG call that would succeed, so a wallet with nothing to spend gets nothing), computes the gas it costs, and sends your wallet that amount before you sign, whether or not you already hold ETH. Every RH402 transaction is paid for by RH402. Each top-up is one transaction's worth of gas (capped) and is only repeated once that transaction has actually been sent: cancelling in the wallet and retrying reuses the ETH already delivered. Gas is covered for wallets that are RH402 users (at least 0.5 USDG, an RH402 name, or an order, listing or received payment on record), so throwaway wallets cannot use it. Wallets without an RH402 name get 3 covered transactions and 3 invoices a day; wallets that own a name get 30 covered transactions and unlimited invoices.
The first payment needs two confirmations in your wallet (a one-time USDG allowance for the router, then the payment). After that, one confirmation per payment.
Agent accounts (EIP-7702)
For servers, bots and AI agents that hold their own key, RH402 offers a fully relayed path. USDG on Robinhood Chain has no permit and no transferWithAuthorization, so the agent's address is upgraded instead: the first relayed transaction carries an EIP-7702 authorization that delegates the address to RH402Account. From then on:
- Every action is an EIP-712 intent:
Execute(Call[] calls, uint256 nonce, uint256 deadline)signed by the account key. The domain is{name:"RH402Account", version:"1", chainId:4663, verifyingContract:<the account address>}. - Anyone may submit an intent; only the account's own key can produce a valid signature. There are no spending limits and no third-party keys.
- The relayer simulates first, refuses calls to contracts outside RH402 and USDG, and rate-limits per account and IP to protect its gas.
- The account still works as a normal EOA. Funds never move without the key.
- Browser wallets do not sign third-party 7702 authorizations, which is why the app uses the gas top-up path above instead.
Usernames
Lowercase a-z 0-9 _, 2 to 32 characters, lifetime, no renewal. Price depends on length and is paid once in USDG or in $RH402X at the live price with a slippage guard (maxAmount). Half of every $RH402X payment is burned.
| Length | Price |
|---|---|
| 2 | 2,000 USD |
| 3 | 500 USD |
| 4 | 100 USD |
| 5 | 20 USD |
| 6 and more | 10 USD |
Instant buyback and burn. USDG is never burned. When a username is paid in USDG, the registry sends the USDG to the RH402Buyback contract and, in the same transaction, that contract spends it to buy $RH402X and sends the $RH402X it bought to the dead address. The buyback contract can do nothing else with USDG. If the route or the price feed is not ready (before the token exists, or during a stale-price window) the registration still succeeds, the USDG waits in the contract, and the RH402 keeper completes the buyback within seconds of the route being available. A username paid directly in $RH402X is burned 100% on the spot. So: USDG in → $RH402X bought → $RH402X burned, at purchase time. No name revenue is kept by the platform.
The first name an address receives becomes its primary name. Names are NFTs: transfer them, or list them in the market (10% fee on sale). Reverse lookup: GET /v1/names/of/<address>. Forward: GET /v1/names/resolve/<name>.
Paying
All payments go through RH402Router so they are indexed with sender, receiver, amount, memo and optional invoice id:
pay(address to, uint256 amount, bytes32 invoiceId, string memo)
payName(string name, uint256 amount, bytes32 invoiceId, string memo)
payInvoice(bytes32 id, string memo)
The API builds the calls for you: POST /v1/pay/calls {name|to, amount, memo, invoiceId} returns [approve, pay]. An invoice id is one-time: a second payment against the same id reverts, and an id registered with createInvoice can only be settled through payInvoice.
Invoices and QR
GET /v1/qr/<name>.svg?amount=&memo=: a styled QR that opens the pay page. Any phone camera reads it.POST /v1/invoice/create {amount, memo, expiresAt}(signed-in): an off-chain invoice, free and instant; the payment call is built from it and the router accepts the id once, so amount and payee are exact.POST /v1/invoice/callsregisters an invoice on chain instead (one transaction) when the payer must see the amount enforced by the contract.GET /v1/qr/invoice/<id>.svgrenders either kind,GET /v1/invoice/<id>shows its status.GET /v1/explorer/card/<tx>.svg: a shareable receipt card for any payment.
Escrow market
Goods and services are bought through RH402Escrow. The buyer's USDG sits in the contract until the order resolves:
| State | Who | What |
|---|---|---|
| Open | buyer | funded; buyer may withdraw until the seller accepts |
| Accepted | seller | the 3-day clock starts; the seller can note delivery |
| Released | buyer or time | buyer confirms, or anyone releases after 3 days; 10% fee, rest to the seller |
| Reported | buyer | clock frozen; only an arbiter can resolve |
| Resolved | arbiter | only after a report: split by basis points, with a written note |
| Refunded | seller or RH402 | full refund; RH402 can refund the buyer at any time before release |
Every order has a report at GET /v1/order/<id> with the full timeline and outcome. Usernames are sold atomically (payment and NFT transfer in one transaction) and do not use escrow.
Private payments
RH402 has a shielded pool for USDG. Inside it, a payment to a username shows nobody who paid, who received or how much. It works like Privacy Pools: money goes in and out publicly, and everything in between is a zero-knowledge proof.
- Unlock. Your wallet signs one message (no transaction). The app derives your private keys from that signature on your device and forgets them when you close the page. The same wallet unlocks the same balance on any device. RH402 never sees the signature or the keys.
- Add funds. A public deposit from your wallet into
RH402Pool. The pool gives it a label. After a short review the RH402 association-set provider (ASP) adds the label to the approved set, and the funds can move privately. - Pay privately. Your device makes a proof that you own notes of an approved deposit, that they were not spent before, and that the amounts balance. The RH402 relayer submits it, so your address never appears in the transaction. The recipient's wallet finds the new note by decrypting it (X25519 + XChaCha20-Poly1305); nobody else can read it. Recipients turn on private receiving once (
RH402Keys), then anyone can pay their username privately. - Withdraw. The same kind of proof pays USDG out to any address. For privacy, withdraw to a wallet that never deposited.
- Take it back. A deposit that has not moved can always be returned to the wallet it came from (
ragequit), even if the ASP never approves it and even while the pool is paused. - Limits. One private payment spends up to two notes of the same deposit. Deposits are capped per transaction. The relay fee and the review delay are shown in the app.
Proofs use Noir circuits and Barretenberg's UltraHonk prover, verified on chain by contracts generated from the circuits. UltraHonk needs no per-circuit trusted setup; it uses the public reference string from Aztec's Ignition ceremony, which rh402.xyz serves itself, so proving never contacts a third party. Circuits, verifiers and tests are in the RH402 repository.
ZK receipts
When you pay privately, the app makes the note so that only you can later prove the payment. A receipt proves "the holder of this receipt paid at least X USDG to this recipient" and nothing more: not your wallet, not the exact amount, not which payment. Anyone can check a receipt link on rh402.xyz/app/#/receipt; the check runs RH402Receipts.verifyReceipt on chain. A receipt can carry a reference (an order number) and has a receipt id, so a shop can refuse the same receipt twice.
Anonymous track record
A seller registers one anonymous identity (a commitment) and counts released escrow sales into RH402Reputation. Later, from any wallet, the seller can attach a badge to a listing: a proof that one identity completed at least N sales (up to 10). Buyers see "N+ sales proven"; nobody learns which wallet or which orders.
X402 rail
X402 lets any API or AI agent charge USDG per request. The exchange is two round trips:
GET /resource
→ 402 Payment Required
PAYMENT-REQUIRED: base64(JSON requirement)
GET /resource
PAYMENT: base64(JSON signed intent)
→ 200 OK
PAYMENT-RESPONSE: base64({"txHash":"0x…","invoiceId":"0x…"})
A requirement looks like this. The calls array is exactly what the payer's account must execute; the facilitator refuses anything else.
{ "version": 1, "scheme": "rh402-intent", "network": "eip155:4663",
"asset": "0x5fc5…d168", "assetSymbol": "USDG", "decimals": 6,
"amount": "10000", "payTo": "0x…", "invoiceId": "0x…", "memo": "…",
"expiresAt": 1790000000, "facilitator": "https://api.rh402.xyz/v1/402",
"calls": [ { "to": "<USDG>", "data": "approve(router, amount)" },
{ "to": "<Router>", "data": "pay(payTo, amount, invoiceId, memo)" } ] }
Your server never holds a key. It asks RH402 for a requirement, sends it in the 402, and when the client retries it forwards the PAYMENT header to /v1/402/settle. A settled requirement cannot be paid twice.
Server side (any language)
# 1. create a requirement for this request
curl -X POST https://api.rh402.xyz/v1/402/requirements \
-H 'content-type: application/json' \
-d '{"payTo":"0xYourAddress","amount":"250000","memo":"weather api","resource":"/v1/weather"}'
# → { paymentRequired: {...}, header: "eyJ2…" } put header in PAYMENT-REQUIRED, answer 402
# 2. when the client retries with PAYMENT, settle it
curl -X POST https://api.rh402.xyz/v1/402/settle \
-H 'content-type: application/json' \
-d '{"payment": <decoded PAYMENT header JSON>}'
# → { txHash, invoiceId } serve the resource, echo PAYMENT-RESPONSE
Try it: GET https://api.rh402.xyz/v1/402/demo charges 0.01 USDG.
Facilitator API
| Endpoint | Purpose |
|---|---|
POST /v1/402/requirements | create a requirement (payTo, amount, memo, resource, ttlSec) |
GET /v1/402/requirement/:id | read it back, see if it was settled |
POST /v1/402/verify | simulate a payment without sending it |
POST /v1/402/settle | relay the intent, return the tx hash |
POST /v1/relay/execute | generic intent relay for delegated accounts |
POST /v1/relay/delegate | first transaction: EIP-7702 authorization plus optional intent |
GET /v1/account/:address | delegation state, intent nonce, balances, names |
GET /v1/config | chain id, contract addresses, EIP-712 types, price table |
Client SDK
The browser app and the tests share one client (web/src/rh402-client.mjs, viem based). It signs locally and posts only signatures.
import { RH402 } from './rh402-client.mjs';
const c = RH402.fromPrivateKey('https://api.rh402.xyz', pk);
await c.registerName('ada'); // delegates + registers in one relayed tx
await c.pay({ name: 'ada', amount: 5_000000, memo: 'thanks' });
const r = await c.fetch402('https://api.rh402.xyz/v1/402/demo'); // pays automatically
Contracts
| Contract | Address |
|---|---|
| loading from /v1/config… | |
- RH402Account: EIP-7702 delegate, ERC-7201 storage, sequential nonce, ERC-1271, token receivers.
- RH402Names: ERC-721 usernames, price table by length, USDG or $RH402X via oracle, reserved names, primary names, pausable registrations.
- RH402Router: pay by address or name, one-time invoice ids, registered invoices, optional fee (0 by default, hard cap 10%).
- RH402Escrow: orders with accept window, 3-day release window (cannot be set longer), reports, arbiter split (reported orders only), emergency refund, pause.
- RH402Market: name listings (atomic sale, 10% fee) and goods listings (escrow orders).
- RH402Oracle: $RH402X price pushed by the RH402 feeder, 30% jump guard per push, 15-minute freshness.
- RH402Pool: shielded USDG pool: deposits with fresh labels, relayed private spends verified by the transact proof, association-set roots from the ASP, ragequit that is never paused. No owner withdrawal.
- RH402Keys: public keys for private receiving, per address.
- RH402Receipts: on-chain check of a ZK payment receipt against a recent pool root.
- RH402Reputation: anonymous seller identities and the tree of counted escrow sales; the market verifies badges against it.
- RH402Buyback: receives all USDG from name sales and, in the same transaction, buys $RH402X through a pluggable route and burns 100% of what it bought (USDG is spent, never burned); the keeper only retries what the instant path had to skip; owner can only set the route and the keeper.
Security notes
- RH402 never holds your key: you sign every transaction in your own wallet. The gas top-up is a plain ETH transfer to your address.
- Intents carry a deadline and a nonce; a captured signature cannot be replayed.
- The relayer only executes calls that target RH402 contracts or USDG, after simulating them.
- Contract owners can pause the router, escrow and market. Escrow funds can only leave to the buyer, the seller, or the treasury fee, never elsewhere.
- Private pool trust model: the ASP can refuse or remove a deposit label (its notes then cannot move privately), but it cannot move anyone's money; a depositor can always take an untouched deposit back. The owner can pause deposits and private spends and cap deposits; there is no function that takes funds out of the pool.
- What stays public: deposits (depositor and amount), withdrawals (recipient and amount) and that a private transaction happened. The relay sees the connection a proof comes from; RH402 does not store it, and you can use a VPN or Tor for more.
- The pool is new code. It has an internal review and tests on real proofs, not yet an external audit. Deposits are capped per transaction.