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 callerFund the wallet
The paying wallet needs two balances:
- USDG —
0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168, 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:
| Operation | USDG per call |
|---|---|
cloven.fresh | 0.001 |
cloven.brief | 0.005 |
cloven.search | 0.002 |
cloven.cite | 0.001 |
cloven.snapshot | 0.01 |
cloven.subscribe | 0.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.offevent surface described in this section and in the cost-ceiling snippet above is not implemented in the current@cloven/sdkrelease. 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
| Symptom | Likely cause | Mitigation |
|---|---|---|
X402Required thrown | Wallet rejected the sign request | Check the wallet holds USDG and ETH for gas on Robinhood Chain. |
Chain id … is not supported | Wallet's chain is not Robinhood Chain | Build the WalletClient with the robinhoodChain definition above (id 4663). |
wrong_network | Payment header declares a non-robinhood network | Stale client or stale chain config — the Base rail is retired. |
tx_too_old returned | Tx confirmed > 10 min after quote | RPC was slow; retry — the SDK will re-quote. |
tx_not_found | Tx was broadcast on the wrong chain, or RPC is behind | Confirm the hash resolves at robinhoodchain.blockscout.com. |
insufficient_payment | Tx amount under quoted price | Should not happen — file an issue with the tx hash. |
| Endless retry loop | RPC down, wallet client failing | The 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.