One signed header
lifts the gate.
Your route says 402. The agent signs once, pays USDC on Elysium and walks through.
/api/hype
PAYMENT-REQUIRED, decoded: accepts[0]
{ "scheme": "exact", "network": "eip155:99901", "amount": "10000", "asset": "0xb47c4C8F73F009CC0C0d3f92B388d13E293F0B65", "payTo": "0xf7e9A2703CF246704fd202BE1E6E46a048Ec4315", "maxTimeoutSeconds": 300, "extra": { "assetTransferMethod": "permit2", "name": "USDC", "version": "1" } }
Every piece is already on Elysium.
Read live from the chain each time you open this page.
USDC
reading
0xb47c4C…3F0B65
Permit2
reading
0x000000…C78BA3
x402 proxy
reading
0x402085…E20001
Elysium
reading100 ms blocks, gas in HYPE
Pay a route, step by step.
This is the client. Run it and every step answers from the live gate.
signs with a fresh key
- scheme and network
- spender is the x402 proxy
- pays the route
- amount and token
- deadline open
- signature recovers
- Permit2 allowance
- USDC balance
- settled by the facilitatorat launch
1
// call the paid route like any other export async function knock(url: string) { const res = await fetch(url) const header = res.headers.get('PAYMENT-REQUIRED') ?? '' return { status: res.status, header } }
2
// the price is in the header, base64 JSON export function decode(header: string) { return decodeHeader<PaymentRequired>(header) }
3
// sign one Permit2 witness: nothing moves, nothing is sent export async function sign(required: PaymentRequired, signer: Signer) { const accepted = required.accepts[0] const now = Math.floor(Date.now() / 1000) const random = crypto.getRandomValues(new Uint8Array(32)) const permit2Authorization = { from: signer.address, permitted: { token: accepted.asset, amount: accepted.amount }, spender: PERMIT2_PROXY, nonce: BigInt(toHex(random)).toString(), deadline: String(now + accepted.maxTimeoutSeconds), witness: { to: accepted.payTo, validAfter: '0' }, } const signature = await signer.signTypedData({ domain: permit2Domain, types: permit2WitnessTypes, primaryType: 'PermitWitnessTransferFrom', message: permit2Authorization, }) const payment: PaymentPayload = { x402Version: 2, resource: required.resource, accepted, payload: { signature, permit2Authorization }, } return payment }
4
// call again with the signed header export async function retry(url: string, payment: PaymentPayload) { const signed = { 'PAYMENT-SIGNATURE': encodeHeader(payment) } const res = await fetch(url, { headers: signed }) const header = res.headers.get('PAYMENT-RESPONSE') ?? '' return { status: res.status, response: decodeHeader<SettlementResponse>(header), body: (await res.json()) as { checks?: Check[] }, } }
Put a toll on your route.
The gate is one handler in front of yours. Settlement opens with $TOLL.
src/routes/api/hype.ts
import { createFileRoute } from '@tanstack/react-router' import { gate, preflight } from '#/server/toll' /* 0.01 USDC on Elysium for the live HYPE mid, behind x402 v2 */ export const Route = createFileRoute('/api/hype')({ server: { handlers: { GET: ({ request }) => gate(request), OPTIONS: () => preflight(), }, }, })