SDK x402 wallet mode

Pass a viem WalletClient to the SDK factory and Cloven handles the entire x402 payment dance for you — quote, sign, wait for confirmation, retry the call with proof, return the response. No human in the loop. No API key issued.

This is the canonical autonomous-agent path. Your agent owns a wallet, the wallet holds USDG on Robinhood Chain (plus a little ETH for gas), every call costs fractions of a cent, and there is no monthly subscription to provision in advance.

Cloven's x402 is a Cloven-native on-chain-receipt scheme — the SDK broadcasts your own USDG transfer and hands Cloven the settled tx hash. It does not talk to any hosted facilitator, and third-party x402 client libraries will not work against Cloven because they do not support chain 4663. Use this SDK, or the raw wire format.

Define the chain

Robinhood Chain is not shipped in viem/chains, so declare it once with defineChain:

import { defineChain } from "viem";
 
export const robinhoodChain = defineChain({
  id: 4663,
  name: "Robinhood Chain",
  nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
  rpcUrls: {
    default: { http: ["https://rpc.mainnet.chain.robinhood.com"] },
  },
  blockExplorers: {
    default: {
      name: "Blockscout",
      url: "https://robinhoodchain.blockscout.com",
      apiUrl: "https://robinhoodchain.blockscout.com/api",
    },
  },
});

Testnet is chain id 46630. The public RPC above is a keyless read fallback only — Robinhood's terms bar it from production or high-throughput use, so point ROBINHOOD_RPC_URL at a provisioned endpoint in production.

Setup

import { cloven } from "@cloven/sdk";
import { createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { robinhoodChain } from "./robinhood-chain";
 
const account = privateKeyToAccount(process.env.AGENT_PK as `0x${string}`);
 
const wallet = createWalletClient({
  account,
  chain: robinhoodChain,
  transport: http(
    process.env.ROBINHOOD_RPC_URL ?? "https://rpc.mainnet.chain.robinhood.com",
  ),
});
 
const client = cloven({ wallet });

The chain field is required — the SDK reads wallet.chain.id to pick the stable-asset contract, and throws if the id is not 4663. A wallet on any other chain fails fast rather than paying to the wrong address.

That's it. Every call now auto-pays:

const ctx = await client.pack("crypto").fresh();
// internally:
//   1. GET /v1/fresh → 402 with { payment: PaymentRequirements }
//   2. wallet.writeContract(USDG.transfer, recipient, amount)
//      USDG = 0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168 (6 decimals)
//   3. wait for 1 confirmation on Robinhood Chain (~50-100ms blocks)
//   4. GET /v1/fresh + x-402-payment header
//   5. return MindResponse to caller

Fund the wallet

The paying wallet needs two balances:

  • USDG0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168, 6 decimals, 1:1 USD. This is what the call costs.
  • ETH — Robinhood Chain's native gas token. An ERC-20 transfer costs a fraction of a cent, but a zero-ETH wallet cannot pay at all.

Every settled payment is inspectable at https://robinhoodchain.blockscout.com/tx/<txHash>.

Cost ceilings

Locked v1 pricing — every call's exact USDG cost is known in advance from the PRICING_USDC table. USDG is 1:1 USD with 6 decimals, so the figures are unchanged from the previous rail:

OperationUSDG per call
cloven.fresh0.001
cloven.brief0.005
cloven.search0.002
cloven.cite0.001
cloven.snapshot0.01
cloven.subscribe0.005 / hour

To enforce a per-call budget, wrap the client:

const MAX_PER_CALL_USDG = 0.01;
 
async function safeCall<T>(fn: () => Promise<T>): Promise<T> {
  // The SDK fires a 'x402:quote' event before signing — abort if too pricey
  return new Promise((resolve, reject) => {
    const onQuote = (q: { amount: string }) => {
      if (parseFloat(q.amount) > MAX_PER_CALL_USDG) {
        client.off("x402:quote", onQuote);
        reject(new Error(`quote ${q.amount} exceeds ceiling ${MAX_PER_CALL_USDG}`));
      }
    };
    client.on("x402:quote", onQuote);
    fn().then(resolve, reject).finally(() => client.off("x402:quote", onQuote));
  });
}
 
const ctx = await safeCall(() => client.pack("crypto").fresh());

Events

Planned, not shipped. The client.on / client.off event surface described in this section and in the cost-ceiling snippet above is not implemented in the current @cloven/sdk release. Until it lands, enforce budgets by pre-checking the operation against the price table above, and read settled amounts from the console or the block explorer.

The SDK emits typed events you can subscribe to for observability:

client.on("x402:quote", ({ tool, amount, recipient, expiresAt }) => {
  log.info("about to pay", { tool, amount });
});
 
client.on("x402:paid", ({ txHash, blockNumber }) => {
  log.info("tx confirmed", { txHash, blockNumber });
});
 
client.on("x402:settled", ({ tool, mindResponse }) => {
  log.info("response received", { tool, brief: mindResponse.brief.slice(0, 60) });
});

Useful for billing reconciliation — your spend ledger is the sum of x402:paid events.

Discount hints (Phase 3)

When the $CLOVEN token launches in Phase 3, your wallet may qualify for a stake-tier discount on each call. The SDK automatically passes X-402-Payer-Hint: <wallet> so the 402 quote arrives pre-discounted. No code change required — the discount mechanism activates server-side once the token is live.

Until Phase 3 ships, lib/billing/discounts.ts returns 0% for every wallet. The hint is sent regardless; it's just ignored.

Failure modes

SymptomLikely causeMitigation
X402Required thrownWallet rejected the sign requestCheck the wallet holds USDG and ETH for gas on Robinhood Chain.
Chain id … is not supportedWallet's chain is not Robinhood ChainBuild the WalletClient with the robinhoodChain definition above (id 4663).
wrong_networkPayment header declares a non-robinhood networkStale client or stale chain config — the Base rail is retired.
tx_too_old returnedTx confirmed > 10 min after quoteRPC was slow; retry — the SDK will re-quote.
tx_not_foundTx was broadcast on the wrong chain, or RPC is behindConfirm the hash resolves at robinhoodchain.blockscout.com.
insufficient_paymentTx amount under quoted priceShould not happen — file an issue with the tx hash.
Endless retry loopRPC down, wallet client failingThe SDK caps retries at 3. After that, X402Required is thrown.

Gasless settlement (future, not shipped)

USDG exposes EIP-3009 transferWithAuthorization and EIP-2612 permit, so a self-hosted facilitator that relays signed authorizations — letting an agent pay without holding ETH — is technically possible. It is not implemented. Today every payment is a transfer your own wallet broadcasts and gases.

Prepaid escrow (future)

For high-throughput agents that don't want the per-call sign overhead, a prepaid escrow mode is on the roadmap: deposit USDG to a Cloven-managed contract once, and each call debits from the balance without a per-call transaction. Same SDK constructor, same call surface. Not shipped — until it lands, use prepaid credit packs for the equivalent economics.

Combined modes

You cannot combine apiKey and wallet in one client — pass one. If you operate both a human-owned subscription and an autonomous agent, instantiate two clients:

const human = cloven({ apiKey: process.env.CLOVEN_KEY });
const agent = cloven({ wallet });

They are independent — separate quotas, separate billing paths, separate dashboards.