How I Built an Autonomous AI Agent That Earns USDC While I Sleep
An honest, step‑by‑step walkthrough for developers who want to put a language model to work earning micropayments on‑chain.
1. Why “earn while I sleep” matters
Autonomous agents are only useful if they can sustain themselves financially without constant human oversight. By tying the agent’s output to a verifiable, on‑chain payment flow (USDC on Base via the x402 protocol), the agent can:
- Pull in revenue directly from users who value its service.
- Keep a transparent ledger of earnings that can be reinvested (e.g., for more compute or model fine‑tuning).
- Operate in a permissionless environment—no bank accounts, no KYC, just a wallet address.
The trade‑off is that you now inherit the complexities of blockchain latency, gas costs, and the need to handle payment failures gracefully. Below I detail the choices I made, the code that glues everything together, and the honest shortcomings you should anticipate.
2. High‑level architecture
+-------------------+ +-------------------+ +-------------------+
| Task Queue (e.g. | --> | Agent Worker | --> | LLM + Tools |
| Redis / SQS) | | (Cloudflare | | (OpenAI, APIs) |
+-------------------+ | Workers) | +-------------------+
^ | (x402 handler) |
| +-----------+------------+
| |
| v
| +-------------------+
| | USDC Ledger (Base|
| | via x402) |
| +-------------------+
+-------------------------------------------+
- Task Queue – a simple FIFO of jobs the agent should perform (e.g., “summarize this article”, “extract entities from a tweet”).
- Agent Worker – a stateless Cloudflare Workers script that pulls a job, pays for it via x402, runs the LLM, and returns the result.
- LLM + Tools – the core reasoning engine; I used OpenAI’s GPT‑4‑turbo via their API, but any compatible endpoint works.
- x402 Handler – validates the incoming payment, extracts the USDC amount, and forwards the request only if the payment succeeds.
- USDC Ledger – the on‑chain record; the agent never touches funds directly—x402 escrows the payment and releases it to the worker’s wallet after successful execution.
3. Payment flow with x402
The x402 spec defines a HTTP header X-Payment-Required that contains a JSON‑Web‑Token (JWT) describing the required amount, token, and destination. The worker must:
- Read the header from the inbound request.
- Verify the JWT using the x402 public key (published on‑chain).
-
Escrow the USDC by calling the x402 smart contract’s
payfunction (via ethers.js). -
Proceed only if the transaction succeeds; otherwise return
402 Payment Required.
Below is the minimal TypeScript snippet that lives inside the Cloudflare Worker. It assumes you have installed @x402/protocol and ethers via npm i @x402/protocol ethers.
import { X402 } from '@x402/protocol';
import { ethers } from 'ethers';
// Configuration – replace with your own values
const USDC_ADDRESS = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'; // Base USDC
const AGENT_WALLET = '0xYourAgentWalletAddress';
const PRIVATE_KEY = process.env.AGENT_PRIVATE_KEY; // never commit this!
const X402_PUBLIC_KEY = '0xYourX402PublicKey'; // from the x402 registry
const provider = new ethers.JsonRpcProvider('https://base.mainnet.rpc.dev');
const wallet = new ethers.Wallet(PRIVATE_KEY, provider);
const usdc = new ethers.Contract(
USDC_ADDRESS,
['function balanceOf(address) view returns (uint256)',
'function transfer(address to, uint256 amount) returns (bool)'],
wallet
);
const x402 = new X402({ publicKey: X402_PUBLIC_KEY });
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
// 1️⃣ Extract payment header
const paymentHeader = request.headers.get('X-Payment-Required');
if (!paymentHeader) {
return new Response('Missing payment header', { status => 400 });
}
// 2️⃣ Verify JWT
let payload;
try {
payload = await x402.verify(paymentHeader);
} catch (e) {
return new Response('Invalid payment token', { status: 401 });
}
// 3️⃣ Check amount & token
const requiredAmt = ethers.parseUnits(payload.amount.toString(), 6); // USDC has 6 decimals
if (payload.token.toLowerCase() !== USDC_ADDRESS.toLowerCase()) {
return new Response('Wrong token', { status: 402 });
}
// 4️⃣ Escrow payment (simple transfer for demo; x402 contract would lock funds)
try {
const tx = await usdc.transfer(AGENT_WALLET, requiredAmt);
await tx.wait(); // ensure inclusion
} catch (e) {
return new Response('Payment failed', { status: 402 });
}
// 5️⃣ If we get here, payment succeeded – process the task
const { task } = await request.json();
const result = await processTask(task);
return new Response(JSON.stringify({ result }), { headers: { 'Content-Type': 'application/json' } });
},
};
What this does:
- The worker never holds USDC in its own balance; it simply forwards the payment to the agent’s wallet after verifying the x402 token.
- In a production setting you’d replace the simple
transferwith the official x402 escrow contract (X402Paymaster) to guarantee refunds if the worker fails.
4. Agent logic: pulling tasks and invoking the LLM
The worker’s processTask function is where the actual AI work happens. I kept it deliberately simple: read a JSON payload, call OpenAI, optionally run a tool (e.g., a web scraper), and return the answer.
import OpenAI from 'openai';
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
async function processTask(task: string): Promise<string> {
// Example: summarization task
const prompt = `
You are a helpful assistant. Summarize the following text in 2-3 sentences:
${task}
`;
const completion = await openai.chat.completions.create({
model: 'gpt-4-turbo-preview',
messages: [{ role: 'user', content: prompt }],
temperature: 0.3,
max_tokens: 150,
});
return completion.choices[0].message?.text?.trim() ?? '';
}
Tool integration (optional):
If the task requires external data (e.g., “latest price of ETH”), you can augment the prompt with a function call. The OpenAI API supports tool calling; you’d expose a tiny HTTP endpoint that fetches the price from a public API and return it as a tool result. The same worker can handle both plain LLM calls and tool‑augmented ones without changing the payment flow.
5. Deployment & operational considerations
| Aspect | Decision | Reasoning | Trade‑off |
|---|---|---|---|
| Compute platform | Cloudflare Workers (serverless, edge) | Sub‑50 ms cold start, zero‑maintenance, built‑in KV for simple state. | Limited execution time (50 s on paid plan) – unsuitable for very long‑running jobs. |
| State store | Redis (managed) or Cloudflare KV for task queue | Simple FIFO; KV offers eventual consistency but is cheap and globally distributed. | KV’s eventual consistency can cause duplicate task processing if not guarded with a lock. |
| Wallet security | Private key stored as Worker secret (AGENT_PRIVATE_KEY) |
Never baked into code; rotated via CI/CD. | If the Worker environment is compromised, the key is exposed – monitor for anomalous transfers. |
| Payment reliability | Use x402 escrow contract + fallback to direct transfer on failure | Guarantees refund if worker crashes after receiving funds. | Adds an extra contract call (≈0.0005 ETH on Base) and requires the worker to hold enough ETH for gas. |
| Observability | Cloudflare Logpush → Loki + Grafana alerts on failed payments or high latency. | Gives visibility into earnings per hour and error rates. | Requires setting up a log pipeline; otherwise you’re flying blind. |
| Rate limiting | In‑memory token bucket per IP (reset every minute) | Prevents a single user from draining the agent |
Top comments (0)