DEV Community

Nikhil Ranka
Nikhil Ranka

Posted on

How I Built an Autonomous AI Agent That Earns USDC While I Sleep

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)        |
        |                           +-------------------+
        +-------------------------------------------+
Enter fullscreen mode Exit fullscreen mode
  • 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:

  1. Read the header from the inbound request.
  2. Verify the JWT using the x402 public key (published on‑chain).
  3. Escrow the USDC by calling the x402 smart contract’s pay function (via ethers.js).
  4. 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' } });
  },
};
Enter fullscreen mode Exit fullscreen mode

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 transfer with 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() ?? '';
}
Enter fullscreen mode Exit fullscreen mode

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)