docs @ stock402

Take payment in stocks
in five minutes

Stock402 implements x402 v2 with the exact scheme on Robinhood Chain, paid in stock tokens. You price in dollars. The 402 quotes that price in each stock you accept at its Chainlink price. The buyer signs, the facilitator verifies and pays the gas, and the settlement contract moves the tokens from buyer to seller. Nobody in between holds them.

1 · paywall your api

server.ts · express
// npm i stock402-middleware express
import express from "express";
import { stock402 } from "stock402-middleware";

const app = express();

app.use("/api", stock402({
  price: "0.01",                      // USD per request
  payTo: "0xYourAddress",             // stock tokens land here
  accept: ["SPY", "QQQ", "NVDA"],     // or "all"
  // facilitatorUrl defaults to the hosted Stock402 facilitator
}));

app.get("/api/report", (_req, res) => res.json({ paid: true }));
app.listen(4021);

A request without payment gets HTTP 402 and a PAYMENT-REQUIRED header with one entry per accepted stock. A paid response carries a PAYMENT-RESPONSE header with the token, the amount and the settlement transaction.

2 · pay from code

buyer.ts · paying fetch
// npm i stock402-client
import { createSigner, payingFetch } from "stock402-client";

const pay = payingFetch({
  account: createSigner(process.env.WALLET_KEY as `0x${string}`),
  maxUsd: "0.05",               // refuse any single payment above this
  prefer: ["SPY", "NVDA"],      // pay with these first
});

const res = await pay("https://api.example.dev/report");
// 402 → signs permit + payment in SPY → retries → 200

The client checks the amount against its own Chainlink read before signing, so a seller cannot quote a stale or inflated price past your cap. It only signs a permit when the settler allowance is short.

3 · charge agents per tool call

claude config · mcp via stock402 proxy
{
  "mcpServers": {
    "paid-tools": {
      "command": "stock402-mcp-proxy",
      "args": ["https://your-paid-mcp.example/mcp"],
      "env": {
        "STOCK402_PRIVATE_KEY": "0x…",
        "STOCK402_MAX_USD": "0.05",
        "STOCK402_PREFER": "SPY,QQQ"
      }
    }
  }
}

The proxy bridges stdio MCP to your paid HTTP server and pays for each tool call. Handshake and tool discovery stay free. Only tools/call is billed. On the server side, createPaidMcpApp from stock402-mcp takes the same payment options as the middleware.

the payment, step by step

  1. 1The seller's middleware reads each accepted stock's Chainlink feed and answers 402 with the dollar price converted to token units, rounded up.
  2. 2The buyer picks a stock it holds, checks the amount against its own feed read and its cap, and signs a Payment: token, from, to, value, validAfter, validBefore, nonce.
  3. 3If the settler's allowance is short, the buyer also signs an EIP-2612 permit for the settler. Neither signature costs gas.
  4. 4The middleware re-quotes, accepts the signed amount if it is within slippageBps of the fresh quote, and sends both to the facilitator.
  5. 5The facilitator checks the signatures, the time window, the nonce, the balance, the pause flag and the permit, then calls Stock402Settler.settle and pays the gas.
  6. 6The settler applies the permit, checks the Payment again on-chain, and transfers the tokens from buyer to seller. The seller serves the response with the transaction hash.

network reference

ChainRobinhood Chain mainnet · CAIP-2 eip155:4663
RPChttps://rpc.mainnet.chain.robinhood.com · https://robinhood-rpc.publicnode.com
Explorerhttps://robinhoodchain.blockscout.com
GasETH, paid by the facilitator relayer. The buyer needs none.
Multicall30x2cAC2D899eCC914d704FeaAE33ac1bF36277DaD1
Stock402Settler0xaf8412644d7cD448aD5123B1C245f9B2eD0D9e58

typed data

eip-712 domains
// Payment, signed by the buyer, checked by the facilitator and the settler
domain  { name: "Stock402", version: "1", chainId: 4663, verifyingContract: <settler> }
Payment(address token,address from,address to,uint256 value,
        uint256 validAfter,uint256 validBefore,bytes32 nonce)

// Permit, standard EIP-2612 on each stock token
domain  { name: <token name()>, version: "1", chainId: 4663, verifyingContract: <token> }
        e.g. name "NVIDIA • Robinhood Token"   (the bullet is part of the name)
spender <settler>, deadline = validBefore

Stock tokens use 18 decimals. Feeds use 8 and already include any split multiplier. 29 tokens are listed, each checked on-chain for its exact name, an unpaused state and a live feed. The full list is on the home page and at /facilitator/supported.

middleware options

OptionDefaultWhat it does
pricerequiredUSD per request, a decimal string such as "0.01"
payTorequiredaddress that receives the stock tokens
accept10 large tickerstickers you take, or "all" for every listed stock
facilitatorUrlhostedverify/settle service to call
slippageBps200how far below the fresh quote a signed amount may be, for prices that move between the 402 and the retry
maxPriceAgeSeconds4 daysrefuse to quote from a feed older than this (long enough to cover a closed weekend)
priceCacheSeconds20reuse a feed read for this long
settleModesyncsync waits for the receipt before serving; async serves after verify

facilitator api

GET/supportedscheme, networks, settler, and every accepted token with its feed
GET/statusrelayer settlements (its nonce) and ETH gas float, read live
POST/verify{ paymentPayload, paymentRequirements } → { isValid, payer }
POST/settleverifies, then calls Stock402Settler.settle → { transaction }
GET/eventsSSE stream of verify and settle events
GET/healthzliveness and mode

Verification runs in this order: requirement match, settler and token, recipient, time window, Payment signature, then on-chain reads for the nonce, the pause flag, the balance, the allowance and the permit nonce. A Payment signed by a smart account or an EIP-7702 delegated wallet is checked through ERC-1271 when plain ECDSA recovery does not match. Permits are plain ECDSA, as the tokens require, so a smart account approves the settler once instead.

Open your lane