v1.9.0-base-x402·EIP-712 + EIP-7702

Q402 Developer Docs

Gasless stablecoin payments across 11 EVM chains. One API key, one signed request.

Overview

A managed relay for USDC / USDT / RLUSD across 11 EVM chains. Users hold no native token; Q402 submits the TX and pays the gas.

Architectureend-to-end flow
  User wallet              Q402 API                 On-chain                 Your app
  ───────────              ────────                 ────────                 ────────

  1. Sign EIP-712  ──▶  /api/payment/intent   lock quote, planChain
                        /api/payment/activate scan TX, grant credits
  2. Get API key   ◀──  (sandbox or live)

  3. Call pay()    ──▶  /api/relay            ──▶  EIP-7702 Type-4 TX
                        verify · decrement         USDC/USDT transfer
                        credits · cap checks       user EOA ──▶ recipient
                                                                          ◀── webhook
                                                                              HMAC-signed
                                                                              relay.success

                        Dashboard ◀── delivery log · key rotation · gas tank balance

intent locks the quote · activate grants credits after the on-chain transfer · relay submits each payment. Every relay can fire an HMAC-signed webhook.

Protocol
EIP-712 + EIP-7702
Settlement token
USDC · USDT · RLUSD (eth-only)
Gas source
Your gas pool

Agentic Wallet

A dedicated signing wallet for each AI agent, with on-chain guardrails so an agent can transact without holding your main keys. Each owner can provision up to 10 agent wallets; every payment settles gaslessly through the same EIP-712 + EIP-7702 relay.

Spend limits
Per-transaction + daily caps
Multi-payee
Up to 20 recipients / batch
Trust gate
On-chain ERC-8004 reputation

Manage wallets, caps, and reputation gates from /dashboard, or introspect them from an MCP client with q402_agentic_info.

Yield · Aave V3

Supply and withdraw stablecoins on Aave V3 over BNB Chain straight from an Agent Wallet. The EIP-7702 relay sponsors the gas, so idle balances compound while you pay $0 to move them.

Protocol
Aave V3
Chain
BNB Chain
Actions
Gasless supply / withdraw

From an MCP client: q402_yield_reserves, q402_yield_positions, q402_yield_deposit, q402_yield_withdraw.

Bridge · Chainlink CCIP

Move native USDC across chains in a single signed request over Chainlink CCIP. Quote, send, and track a transfer from the dashboard or an MCP client — no manual bridge hops.

Protocol
Chainlink CCIP
Lanes
Ethereum · Avalanche · Arbitrum
Asset
Native USDC

From an MCP client: q402_bridge_quote, q402_bridge_send, q402_bridge_history, q402_bridge_gas_tank.

Payment Requests

The receive side of Q402. Publish a payment request (a fixed amount on a chain, with an optional memo) and Q402 returns a shareable /pay/req_… link plus a req_ id. Creating one moves no funds; anyone can fulfill it later. It is the inverse of q402_pay: instead of sending, you bill.

Create
API key or dashboard
Share
/pay/req_… link
Settle
Gasless, Mode C

Two ways to get paid. A Q402 agent settles a request gaslessly from its own Agent Wallet with q402_request_pay: the agent-to-agent flow, where agent A bills and agent B pays. Or open the /pay link, which shows the exact amount, token, chain, and recipient. Those terms are read from the stored request on every settlement, so a payer can never redirect funds or change the sum.

From an MCP client: q402_request_create, q402_request_status, q402_request_pay (two-phase consent, same as q402_pay).

How It Works

Three actors. One transaction. Zero gas for your users.

A
User signs an authorization (no gas, no blockchain)
Using their wallet (e.g. MetaMask), the user signs a typed message saying "I allow transferring X USDC to address Y". This is purely a cryptographic signature — no transaction is sent, no gas is needed.
B
Your server calls POST /api/relay
You pass the user's signature to Q402's API. That's it — one HTTP call. Q402 verifies the signature, constructs the on-chain transaction, and submits it.
C
Gas is deducted from your gas pool, USDC lands in recipient wallet
Q402 uses your pre-funded gas pool to pay the network fee. The USDC moves on-chain, verifiable on BscScan / Snowtrace / Etherscan. User sees zero gas cost.
// Full flow
User wallet → signs EIP-712 → your frontend
Your backend → POST /api/relay → Q402 API
Q402 → uses gas pool → on-chain TX
Chain → confirms TX → recipient gets USDC

Quick Start

Pick a key, load the SDK, call pay(). Under 5 minutes.

0 · Trial key vs Multichain key

Trial API Key

BNB only · 2,000 sponsored TX · Q402 pays gas. /event.

Multichain API Key

11 chains · USDC / USDT / RLUSD (eth) · self-funded Gas Tank. /payment.

Tip
Trial keys reject non-BNB with TRIAL_BNB_ONLY. Use Multichain for non-BNB chains.

1 · Load the SDK

html
<script src="https://q402.quackai.ai/q402-sdk.js"></script>

2a · Trial key — BNB-only sponsored payment

javascript
// Trial keys are BNB-only. No Gas Tank needed — Q402 sponsors gas.
const q402 = new Q402Client({
  apiKey: "q402_live_YOUR_TRIAL_KEY",   // from /event (your Trial API Key)
  chain:  "bnb",
});

const result = await q402.pay({
  to:     recipientAddress,
  amount: "1.00",
  token:  "USDT",   // or "USDC"
});
// result → { success: true, txHash: "0x...", chain: "bnb", method: "eip7702" }

2b · Multichain key — full 11-chain payment

javascript
// Multichain keys work across all supported chains. Each chain needs
// a funded Gas Tank — deposit at /dashboard → Treasury.
const q402 = new Q402Client({
  apiKey: "q402_live_YOUR_KEY",
  chain:  "bnb",   // "bnb" | "avax" | "eth" | "xlayer" | "stable" | "mantle" | "injective" | "monad" | "scroll" | "arbitrum" | "base"
});
// Note: Injective supports both USDC and USDT (native Circle USDC via CCTP).

// Wallet popup appears — user signs, Q402 relays on-chain
// amount MUST be a human-readable decimal STRING (e.g. "50.00", "0.123456").
// Never pass a JS Number — IEEE-754 loses precision on 18-decimal tokens.
// Inputs exceeding the token's decimals or non-decimal strings throw.
const result = await q402.pay({
  to:     recipientAddress,
  amount: "50.00",
  token:  "USDC",
});
// result → { success: true, txHash: "0xabc...", tokenAmount: "50", chain: "bnb" }

3 · Injective EVM

Native Circle USDC (CCTP) and USDT are both supported on Injective EVM. Cosmos and EVM share one balance via the MultiVM Token Standard.

javascript
const q402 = new Q402Client({
  apiKey: "q402_live_YOUR_KEY",
  chain:  "injective",
});

const result = await q402.pay({
  to:     recipientAddress,
  amount: "50.00",
  token:  "USDC",   // USDC and USDT both supported on Injective
});

4 · Ethereum RLUSD (NY DFS regulated)

Ripple USD, NY DFS regulated. Ethereum mainnet only — rejects on any other chain. Decimals = 18; the SDK handles conversion, pass amount as a decimal string.

javascript
const q402 = new Q402Client({
  apiKey: "q402_live_YOUR_KEY",
  chain:  "eth",
});

const result = await q402.pay({
  to:     recipientAddress,
  amount: "10.00",
  token:  "RLUSD",   // Ethereum-only — throws on any other chain
});

5 · That's it

typescript
// Full result shape:
// {
//   success:       true,
//   txHash:        "0xdef456...",
//   chain:         "bnb",
//   blockNumber:   "38482910",
//   tokenAmount:   "50",
//   token:         "USDC",
//   gasCostNative: 0.000021,
//   method:        "eip7702",
// }
console.log("Paid! TX:", result.txHash);
Tip
One call signs + relays. User holds no native token. Gas comes from your pool.

MCP for AI Clients

An MCP server for Claude / Codex / Cursor / Cline / Copilot / Hermes. @quackai/q402-mcp · bitgett/q402-mcp.

1 · Install

Same package, one snippet per client. No secrets here.

bash
# Claude Code / Claude Desktop
claude mcp add q402 -- npx -y @quackai/q402-mcp

# OpenAI Codex CLI
codex mcp add q402 -- npx -y @quackai/q402-mcp

# Cursor — paste into ~/.cursor/mcp.json (or .cursor/mcp.json for per-project scope)
# Cline  — Cline → Settings → MCP Servers → Edit JSON. Same shape.
{
  "mcpServers": {
    "q402": {
      "command": "npx",
      "args": ["-y", "@quackai/q402-mcp"]
    }
  }
}

# GitHub Copilot (VS Code) — .vscode/mcp.json. Root key is "servers", NOT mcpServers.
{
  "servers": {
    "q402": {
      "command": "npx",
      "args": ["-y", "@quackai/q402-mcp"]
    }
  }
}

# Hermes Agent (Nous Research) — ~/.hermes/config.yaml (YAML). Reload with /reload-mcp.
mcp_servers:
  q402:
    command: "npx"
    args: ["-y", "@quackai/q402-mcp"]
    enabled: true

2 · First-time setup — ask your AI

Restart the client, then say “Set up Q402”. The agent runs q402_doctor → creates + opens ~/.q402/mcp.env → walks you through pasting keys into the file. Auto-loaded for every client.

Tip
🔒 Keys never paste into chat. Signing is local; the key never leaves your machine.

3 · Wallet modes — which signing path?

Three paths. q402_doctor asks once; change later in ~/.q402/mcp.env.

Recommended
Mode C
Server-managed

Q402 holds an encrypted Agent Wallet for you. No private key in your env. No MetaMask popup. Best for AI agents and most users.

Q402_MULTICHAIN_API_KEY=q402_live_...
Mode B
Local Agent Wallet PK

Same Agent Wallet as Mode C, but you hold the PK. Export from the dashboard once. Signs locally — key never leaves your machine. MetaMask never touched.

Q402_AGENTIC_PRIVATE_KEY=0x...
Q402_MULTICHAIN_API_KEY=q402_live_...
Mode A
Your MetaMask EOA

Your existing EOA signs directly via EIP-7702. The "Smart account" marker after first use is normal + reversible with q402_clear_delegation. Use a fresh wallet.

Q402_PRIVATE_KEY=0x...
Q402_MULTICHAIN_API_KEY=q402_live_...

4 · Tools exposed — 27 total

ToolAuthPurpose
q402_doctornoneFirst-install onboarding + ongoing health check (quota, EIP-7702 state, relay reachability).
q402_quotenoneCompare gas + supported tokens across 11 chains.
q402_balanceapi keyVerify key + remaining quota. Returns Trial + Multichain in one read when both keys set.
q402_paylive modeSingle-recipient gasless USDC / USDT / RLUSD send. Sandbox by default.
q402_batch_paylive modeUp to 20 recipients per call (trial: 5). 6+ BNB batches with Trial → status="ambiguous" so the agent asks how to split.
q402_receiptnoneFetch + locally verify a Trust Receipt by rct_… id (ECDSA recovery against the relayer EOA).
q402_wallet_statusprivate keyPer-chain EIP-7702 delegation state for the EOA derived from Q402_PRIVATE_KEY. Read-only.
q402_clear_delegationprivate key / api keyClear EIP-7702 delegation on a single chain (Mode A/B local key OR Mode C api key, server-signed). Sponsored except Ethereum (billed to Gas Tank). Two-phase consentToken (preview then execute).
q402_agentic_infoapi keyAgent Wallet info (addresses, caps, daily-spend used, ERC-8004 id). Drives Mode C.
q402_recurring_listapi keyList scheduled rules.
q402_recurring_createapi keyAuthor a rule. Paid Multichain on EVERY chain (BNB included).
q402_recurring_firesapi keyLast 50 fires per rule (timestamp + txHashes + amount).
q402_recurring_pauseapi keyPause a rule. Reversible.
q402_recurring_resumeapi keyResume a paused / stopped rule.
q402_recurring_skip_nextapi keySkip ONLY the next scheduled fire. Cadence preserved.
q402_recurring_cancelapi keyPermanently stop a rule.
q402_bridge_quotenoneQuote a Chainlink CCIP USDC bridge across the eth/avax/arbitrum triangle (LINK + native fee + ETA).
q402_bridge_sendlive modeExecute a CCIP USDC bridge from the Agent Wallet (Mode C). Sandbox by default.
q402_bridge_historyapi keyRecent CCIP bridge attempts for the Agent Wallet (src/dst/amount/CCIP msgId/status).
q402_bridge_gas_tankapi keyPer-chain Gas Tank native balance + auto-fund window so the agent can top up before bridging.
q402_yield_reservesnoneList Q402 Yield (Aave V3) lending markets + live supply APY. BNB Chain only today.
q402_yield_positionsapi keyThe Agent Wallet's current Q402 Yield positions — value + live supply APY. Read-only.
q402_yield_depositlive modeSupply the Agent Wallet's USDC / USDT into Aave (Mode C). PAID feature — Trial cannot deposit. Confirm-gated + sandbox by default.
q402_yield_withdrawlive modeWithdraw supplied stablecoin out of Aave (amount="max" for the full position). Always allowed, even after a plan downgrade.
q402_request_createapi keyPublish a payment request (invoice). No funds move; returns a /pay link + req_ id. Recipient defaults to the Agent Wallet.
q402_request_statusnoneLook up a request by req_ id (amount, token, chain, recipient, status). Read-only; notFound instead of throwing.
q402_request_paylive modePay a request gaslessly from your own Agent Wallet (Mode C). Two-phase consent, same as q402_pay.

5 · Sandbox vs live mode

Default is sandbox — fake txHash, sandbox: true, no funds move. Live = API key + a signing path. Pick ONE mode:

  • Mode AQ402_PRIVATE_KEY = your MetaMask EOA. Shows “Smart account” in MetaMask after first use (reversible via q402_clear_delegation).
  • Mode BQ402_AGENTIC_PRIVATE_KEY = exported Agent Wallet PK. Local signing; MetaMask untouched.
  • Mode C — paid Multichain key only. Q402 holds the AES-GCM-encrypted Agent Wallet key server-side. Optionally set Q402_AGENT_WALLET_ADDRESS to pick a wallet.

q402_doctor writes the file with every secret line empty. Live mode only flips when an API key + a signing path are populated — saving the template as-is stays in sandbox.Q402_ENABLE_REAL_PAYMENTS=0 forces sandbox even with real keys.

bash
# ~/.q402/mcp.env — what q402_doctor creates on first install.
# Paste your values on the right of `=`. Q402_ENABLE_REAL_PAYMENTS
# already defaults to 1 — the gate refuses empty values, so partial
# setups stay in sandbox automatically.

# ── API key (pick one or both for auto-routing) ──
Q402_TRIAL_API_KEY=          # Free Trial, BNB only (from /event)
Q402_MULTICHAIN_API_KEY=     # Paid Multichain, all 11 chains (from /payment)

# ── Signing path — pick ONE of Mode A / B / C ──
# Mode A: your MetaMask EOA's hex private key
Q402_PRIVATE_KEY=
# Mode B: exported Agent Wallet pk from dashboard (keeps MetaMask untouched)
Q402_AGENTIC_PRIVATE_KEY=
# Mode C: no PK needed. Paid Multichain key alone + server-managed Agent Wallet.
#   Optional picker when you have multiple wallets:
# Q402_AGENT_WALLET_ADDRESS=0x...

# Live mode switch:
#   0 = sandbox (test mode, no funds move)
#   1 = real on-chain payments
# Default 1 — safe because mode only flips to live when an API key AND
# at least one valid signing path (A/B/C) are populated above.
Q402_ENABLE_REAL_PAYMENTS=1

# Default Q402 deployment. Only change for self-hosted.
Q402_RELAY_BASE_URL=https://q402.quackai.ai/api

Missing config → sandbox fallback with a hint. Two extra guards: Q402_MAX_AMOUNT_PER_CALL (default $200) and Q402_ALLOWED_RECIPIENTS (address allowlist).

Tip
q402_pay requires explicit in-chat confirmation. Four guards total: confirm + sandbox default + per-call cap + allowlist.

Trust Receipt

A verifiable proof page for every Q402 settlement — signed by the relayer EOA, recoverable in any browser.

What's on a receipt

  • Settlement facts — payer, recipient, amount, chain, EIP method.
  • On-chain proof — tx hash, block, sponsored gas, explorer link.
  • Signature — EIP-191 ECDSA over the canonical hash, recoverable locally in your browser.
  • Delivery trace — webhook state, retry count, last response code.

Receipt URL

/api/relay responses include receiptId + receiptUrl:

json
{
  "success":      true,
  "txHash":       "0x9afd...52a4",
  "tokenAmount":  "0.10",
  "token":        "USDT",
  "chain":        "bnb",
  "receiptId":    "rct_afa5f50bc49a65ebba3b28ab",
  "receiptUrl":   "https://q402.quackai.ai/receipt/rct_afa5f50bc49a65ebba3b28ab"
}

Mirrored in the webhook payload — no second lookup needed.

JSON endpoint

Machine-readable for downstream verification:

bash
curl https://q402.quackai.ai/api/receipt/rct_afa5f50bc49a65ebba3b28ab

Rate limited 120/min per IP, returned with X-Robots-Tag: noindex, nofollow. The id is unguessable (12 random bytes) so the URL doubles as a shareable audit link without iteration risk.

Best-effort inline + durable backfill

The relay path tries createReceipt() synchronously twice and awaits a queue write to receipt-backfill-queue before responding. /api/cron/receipt-backfill drains the queue with a per-tx KV lock and SET-NX idempotency, so a successful relay normally produces exactly one receipt — synchronously when KV is healthy, within the next cron run otherwise. If both inline retries andthe backfill enqueue fail (rare, requires KV to be unreachable for both writes within the same request), the relay route fires a critical Telegram alert with the tx hash + payload snapshot so an operator can manually recover. That alert path is the third leg; we don't claim a hard guarantee in the absence of it.

Verify any receipt from Claude

The @quackai/q402-mcp server (v0.8.45) exposes a q402_receipt tool. After q402_pay hands back a rct_… id, ask Claude to verify it and the recovery runs inside the MCP process — same canonical-JSON + ECDSA recovery the receipt page does, no UI trust required:

text
> Send 0.10 USDT to alice on BNB via Q402, then verify the receipt.

Claude → q402_pay   → settles + returns rct_afa5...
Claude → q402_receipt → verified: true · signed by 0xfc77...74ff466

Live demo

Important
First production Trust Receipt — 0.01 USDT settled on BNB Chain via EIP-7702:
q402.quackai.ai/receipt/rct_afa5f50bc49a65ebba3b28ab ↗

Gas Pool

Deposit native tokens into your per-wallet Gas Tank. Every relay deducts the actual gas cost.

Deposit

Send BNB / ETH / AVAX / OKB / MNT / INJ / MON (or USDT0 on Stable) to the Gas Tank address on the dashboard. The Tank is a cold wallet, NOT the relayer — never send to the relayer directly.

Auto-deduction

Per-relay native-token deduction, real-time balance.

Withdraw

Manual via business@quackai.ai. Funds remain yours.

Important
Empty Gas Tank → relays fail. Set 20% / 10% email alerts from the dashboard.

EIP-7702 Delegation

EIP-7702 set-code delegation (Pectra) lets your EOA settle gasless payments without a per-user smart-account deploy. Persists across payments, reversible anytime.

Inspect or clear

From your AI client (MCP) — ask in plain English:

text
"Show my Q402 wallet status."
"Clear my Q402 delegation on BNB Chain."

q402_wallet_status reads state. q402_clear_delegationsigns locally (Mode A/B) or server-side (Mode C, api key); Q402 sponsors the clear TX on every chain except Ethereum, where it's billed to your Gas Tank. The next payment recreates the delegation automatically.

From the terminal (CLI):

bash
PRIVATE_KEY=0x<yourKey> node scripts/undelegate-7702.mjs --chain bnb

All 11 chains, self-paid (~$0.001 native gas).

Why we use it

One primitive, 11 chains, no per-user contract deploy. Each chain's impl is source-verified on Sourcify. The delegation marker is the only on-chain trace.

Troubleshooting (things to know)
  • • Your wallet's eth_getCode returns 0xef0100…<impl> instead of 0x while the delegation is active.
  • • MetaMask / OKX may display a Smart accountindicator — the delegation is to Q402's vetted impl, not a third-party contract.
  • • Native gas tokens (BNB / ETH / etc.) sent directly to a delegated EOA will not land — the impl doesn't accept native receives. Clear the delegation first if you want to receive native to that EOA.

Authentication

API key goes in the request body's apiKey field. Sandbox keys (q402_test_*) on wallet connect. Live keys (q402_live_*) from /event (Trial, BNB) or /payment (Multichain, 11 chains).

json
// POST /api/relay
{
  "apiKey": "q402_live_YOUR_API_KEY",
  "chain":  "avax",
  "token":  "USDC",
  ...
}
q402_live_*
Production. Mainnet TX. Keep private — tied to your Gas Tank.

API Reference

Base URL: https://q402.quackai.ai/api

POST/relay

Submit a signed EIP-712 + EIP-7702 payload. Q402 verifies and relays on-chain.

json
// Request body
{
  "apiKey":      "q402_live_YOUR_API_KEY",
  "chain":       "avax",           // avax | bnb | eth | xlayer | stable | mantle | injective | monad | scroll | arbitrum | base
  "token":       "USDC",           // USDC | USDT
  "from":        "0xUserWallet...",
  "to":          "0xRecipient...",
  "amount":      "50000000",       // atomic units (6 decimals = 50 USDC)
  "deadline":    1751289600,
  "witnessSig":  "0xabc123...",
  "authorization": { ... }         // EIP-7702 authorization object
}

// Response 200
// All numeric amounts are returned as decimal strings to preserve
// 18-decimal token precision (IEEE-754 doubles lose precision at that scale).
{
  "success":        true,
  "txHash":         "0xdef456...",
  "chain":          "avax",
  "blockNumber":    "54540550",
  "tokenAmount":    "50",
  "gasCostNative":  "0.000021",
  "method":         "eip7702"
}
GET/relay/info

Returns the relayer (facilitator) address. The facilitator field in your EIP-712 payload must match.

json
// GET /api/relay/info
{ "facilitator": "0xRelayerAddress..." }

Chain Support

Same API, same SDK. Switch with one parameter.

Chainchain paramChain IDGas tokenStatusAvg gas/tx
BNB Chain
bnb56BNBMainnet Live~$0.001
Ethereum
eth1ETHMainnet Live~$0.19
Avalanche
avax43114AVAXMainnet Live~$0.002
X Layer
xlayer196OKBMainnet Live~$0.001
Stable
stable988USDT0 ★Mainnet Live~$0.001
Mantle
mantle5000MNTMainnet Live~$0.001
Injective
injective1776INJMainnet Live~$0.10
Monad
monad143MONMainnet Live~$0.001
Scroll
scroll534352ETHMainnet Live~$0.001
Arbitrum
arbitrum42161ETHMainnet Live~$0.001
Base
base8453ETHMainnet Live~$0.001
Note
Gas comes from your Gas Tank, not Q402, not your users. Ethereum costs ~$0.19/TX — size the pool accordingly.
Important
★ Stable: USDT0 is the gas token AND the payment token. Fund the Gas Tank with USDT0, not a native coin.

EIP-712 Signing

EIP-712 typed structured data (same standard as Uniswap, Compound). User signs a human-readable message — no gas, no on-chain TX.

Contract Addresses & Domain Names

typescript
// Implementation contract per chain
const CONTRACTS = {
  avax:   "0x96a8C74d95A35D0c14Ec60364c78ba6De99E9A4c", // Q402 Avalanche (chainId: 43114)
  bnb:    "0x6cF4aD62C208b6494a55a1494D497713ba013dFa", // Q402 BNB Chain (chainId: 56)
  eth:    "0x8E67a64989CFcb0C40556b13ea302709CCFD6AaD", // Q402 Ethereum  (chainId: 1)
  xlayer: "0x8D854436ab0426F5BC6Cc70865C90576AD523E73", // Q402 X Layer   (chainId: 196)
  stable: "0x2fb2B2D110b6c5664e701666B3741240242bf350", // Q402 Stable    (chainId: 988)
  mantle: "0xE5b90D564650bdcE7C2Bb4344F777f6582e05699", // Q402 Mantle    (chainId: 5000)
  injective: "0xa9a7dcE76DEF2AC36057FeF0d8103dF10581d61e", // Q402 Injective (chainId: 1776)
  monad:  "0xc5d4dFA6D2e545409C1abf86f336Dd43bb87621f", // Q402 Monad     (chainId: 143)
  scroll: "0x7635F32D893B64b5944CB8cbF2AC4cd3dA41B2f1", // Q402 Scroll    (chainId: 534352)
  arbitrum: "0x8D854436ab0426F5BC6Cc70865C90576AD523E73", // Q402 Arbitrum  (chainId: 42161)
  base:   "0x2fb2B2D110b6c5664e701666B3741240242bf350", // Q402 Base      (chainId: 8453)
};

// EIP-712 domain name — must match contract NAME constant exactly
const DOMAIN_NAMES = {
  avax:      "Q402 Avalanche",
  bnb:       "Q402 BNB Chain",
  eth:       "Q402 Ethereum",
  xlayer:    "Q402 X Layer",
  stable:    "Q402 Stable",
  mantle:    "Q402 Mantle",
  injective: "Q402 Injective",
  monad:     "Q402 Monad",
  scroll:    "Q402 Scroll",
  arbitrum:  "Q402 Arbitrum",
  base:      "Q402 Base",
};

// verifyingContract:
//   ALL chains → user's own EOA  (address(this) under EIP-7702 delegation)

Witness Type (unified across all chains)

typescript
// Every deployed Q402 impl contract uses the same EIP-712 typed struct:
const types = {
  TransferAuthorization: [
    { name: "owner",       type: "address" }, // token sender (user's EOA)
    { name: "facilitator", type: "address" }, // gas sponsor (Q402 relayer)
    { name: "token",       type: "address" }, // ERC-20 contract (USDC / USDT / USDT0)
    { name: "recipient",   type: "address" }, // payment destination
    { name: "amount",      type: "uint256" }, // atomic units
    { name: "nonce",       type: "uint256" }, // random uint256, replay protection
    { name: "deadline",    type: "uint256" }, // unix timestamp
  ],
};

// verifyingContract is ALWAYS the user's own EOA — the contract computes its
// domain separator with address(this), which equals the user EOA under EIP-7702.

Signing with ethers.js

typescript
// Fetch facilitator address first (required for all chains)
const { facilitator } = await fetch("https://q402.quackai.ai/api/relay/info").then(r => r.json());

const domain = {
  name:              DOMAIN_NAMES[chain],
  version:           "1",
  chainId:           chainId,
  verifyingContract: userAddress,  // user's own EOA — same for all chains under EIP-7702
};

const nonce = ethers.toBigInt(ethers.randomBytes(32)); // random uint256

const signature = await signer.signTypedData(domain, types, {
  owner:       userAddress,
  facilitator,
  token:       tokenAddress,
  recipient:   recipientAddress,
  amount:      ethers.parseUnits("50", decimals), // 6 decimals on most chains; 18 only for Stable chain's USDT0
  nonce,
  deadline:    BigInt(Math.floor(Date.now() / 1000) + 600),
});
Note
EIP-7702: All 11 chains use Type-4 TX — atomic delegate + transfer. X Layer also supports EIP-3009 (pass eip3009Nonce instead of authorization).
Important
Stable: USDT0 here = 18 decimals. Use ethers.parseUnits(amount, 18). Gas Tank also in USDT0. (Mantle USDT0 = 6 decimals.)

Error Responses

JSON body: { "error": string, "code"?: string }. error is human-readable; code is a stable machine-readable tag (when present).

Codes below are the currently-emitted set; most failures return only error.

400
(no code)
Generic validation failure — malformed JSON, missing required field, or chain-specific shape error. The error message describes the offending field.
401
(no code)
Missing or invalid API key, or API key has been rotated.
401
NONCE_EXPIRED
Auth challenge has expired or already been consumed. Fetch a fresh nonce from /api/auth/nonce.
401
SIG_MISMATCH
Auth signature does not match the expected challenge for the given address.
402
(no code)
Insufficient gas tank balance for the selected chain. Top up via dashboard.
402
NO_INTENT
Activate called without a prior /api/payment/intent — call intent first to lock the quote.
402
INTENT_MISMATCH
Activate intentId does not match the stored latest intent for this address.
402
SENDER_MISMATCH
On-chain TX sender does not match the calling wallet address.
402
CHAIN_MISMATCH
On-chain payment was found but on a different chain than the intent specified.
402
TOKEN_MISMATCH
On-chain payment used a different ERC-20 than the intent specified.
402
AMOUNT_LOW
On-chain payment amount is below the intent's expectedUSD threshold.
403
(no code)
Subscription expired. Renew on /payment to continue.
409
ACTIVATION_IN_PROGRESS
Another activation request is currently processing this txHash. Retry after a brief pause.
429
(no code)
Rate limit exceeded for the IP or API key, OR no TX credits remaining (purchase additional credits).
500
ACTIVATION_RETRY
Activation failed during the KV write phase. Retry — the operation is idempotent, so a second attempt will pick up where the first stopped.

FAQ

Do users need BNB, ETH, or AVAX to use Q402?
No. Users only need USDC (or USDT) in their wallet. All gas is paid from your gas pool. The user signs a message — that's it.
Who pays gas?
You — from your per-chain Gas Tank. Withdrawals are manual via business@quackai.ai.
Does Q402 hold my keys?
Your personal wallet is non-custodial — you connect it (MetaMask / OKX) and Q402 never holds its key; the EIP-712 signature authorizes one transfer A→C, so Q402 only pays gas and relays and cannot redirect funds. Agent Wallets (Mode C) are managed: Q402 custodies an AES-256-GCM-encrypted key you can export or archive anytime.
What if a transaction fails?
Failed relays discard the payload — no funds move. Common causes: empty Gas Tank or expired deadline.
Which tokens are supported?
USDC + USDT on every chain (Injective added native Circle USDC via CCTP). RLUSD on Ethereum only (18 decimals, NY DFS regulated).
How do I get an API key?
Connect a wallet → sandbox key (q402_test_*). For a live key: /event (Trial, BNB, 2,000 TX free) or /payment (Multichain, 11 chains).
How does billing work?
Each paid purchase = 30-day window + TX credits for the tier. Top up within the window to upgrade. Plans never downgrade mid-window; cumulative resets on lapse.

Ready to go gasless?

Get a live API key on payment. Sandbox key is free to test first.