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.

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(),
    },
  },
})