HTTP/1.1 402 PAYMENT REQUIRED agent402.base.eth · BASE · SOLANA · POLYGON · ARBITRUM · ROBINHOOD · USDC · USDG
Home / Guides / Give your agent a wallet — non-custodial, from zero to first x402 payment

Give your agent a wallet — non-custodial, from zero to first x402 payment

Agents need wallets, and the single most important property of an agent wallet is that nobody else ever sees the private key — not a SaaS, not a marketplace, and not Agent402. This guide is the flow we test in CI end to end: a keypair born inside the test runner completes a real x402 purchase, and the run fails if the key ever appears in any log.

The trust model, stated plainly

1. Create the wallet (locally — one line)

import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";

const pk = generatePrivateKey();          // stays on YOUR machine
const account = privateKeyToAccount(pk);
console.log("agent address:", account.address);  // the ONLY thing you share

Store pk in your secret manager (env var, keychain, KMS) — never in code, never in git. If you prefer managed custody, Coinbase's Embedded Wallets give end users email-login wallets where you never handle keys at all; everything below works the same since only addresses are exchanged.

2. Rehearse on testnet (free money)

Fund the new address with testnet USDC via the paid testnet-fund tool — a tenth of a cent buys a full testnet dollar from the Coinbase faucet:

curl -X POST https://agent402.tools/api/testnet-fund \
  -H "Content-Type: application/json" \
  -d '{"address":"<your agent address>","token":"usdc"}'

Then point your x402 client at any base-sepolia seller (or run the open-source Agent402 server locally with NETWORK=base-sepolia) and make a paid call:

import { x402Client } from "@x402/core/client";
import { registerExactEvmScheme } from "@x402/evm/exact/client";
import { wrapFetchWithPayment } from "@x402/fetch";

const client = new x402Client();
registerExactEvmScheme(client, { signer: account });
const payFetch = wrapFetchWithPayment(fetch, client);
const res = await payFetch("http://localhost:3000/api/hash", {
  method: "POST", headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ text: "first paid call" }),
});
// The PAYMENT-RESPONSE header carries the on-chain settlement tx.

3. Fund it for real

When the rehearsal settles, fund the same address with real USDC on Base. Two ways:

Verify with wallet-balances, then the same buyer code works against every seller in the index — and every tool here. Budget guardrails: the agent402-mcp server adds pre-signature spend controls (per-call and per-session caps) on top.

How we hold ourselves to this

The exact flow above runs in our CI as a gated test: a fresh keypair is generated inside the runner, faucet-funded, and completes a real x402 settlement against a paid-mode server — then the test scans every byte of its own output and the server's full log for the key material (all prefix/case forms) and fails on any hit. Keys in, addresses out, provably.

Back to guides