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

Target audience: developers who want to put a language‑model‑driven agent behind a pay‑per‑use API and settle payments in USDC on Base.


1. Why a “sleep‑earning” agent?

The idea isn’t magic; it’s simply exposing a useful AI capability as a metered service. If each invocation costs the caller a fraction of a cent and the agent can run on cheap, stateless infrastructure, the revenue stream can keep ticking while you’re offline. The hard part is making the agent reliable enough that people will pay for it, and integrating a payment rail that doesn’t add operational overhead.


2. High‑level architecture

+----------------+          x402 payment          +-------------------+
|  Caller (any)  | ───────────────────────► |  Cloudflare Worker  |
+----------------+   (USDC on Base)          +-------------------+
                                            │
                                            ▼
                                 +-------------------+
                                 |  AI Agent Core    |
                                 | (LangChain + LLM)│
                                 +-------------------+
                                            │
                                            ▼
                                 +-------------------+
                                 |  Result / Data    |
                                 +-------------------+
Enter fullscreen mode Exit fullscreen mode
  • Caller – any HTTP client (web app, CLI, another agent).
  • x402 – a lightweight micropayment protocol that attaches a payment receipt to the request header. The Worker validates the receipt before forwarding the request to the agent logic.
  • Cloudflare Worker – stateless, globally distributed, executes in < 5 ms cold start for JavaScript/Wasmi‑compiled Rust. Keeps ops cheap (≈ $0.000005 per 10 ms).
  • AI Agent Core – a small LangChain‑style pipeline that calls a hosted LLM (e.g., OpenAI GPT‑4o‑mini) or an open‑source model served via Together.ai. The core is pure Python; we run it inside the Worker via Pyodide or, more practically, as a separate Docker container invoked through a lightweight RPC (e.g., Hypercorn + Uvicorn) when the Worker needs > 100 ms of CPU. For the demo I kept the model call inside the Worker using the OpenAI API (network‑bound, not CPU‑bound).

3. Building the agent core

Below is a minimal, functional agent that answers a single‑turn question and returns a JSON payload. It uses LangChain for prompt templating and the OpenAI chat completion API.

# agent.py
import os
import json
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain
from langchain_openai import ChatOpenAI

OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.2, api_key=OPENAI_API_KEY)

prompt = PromptTemplate.from_template(
    "You are a helpful assistant. Answer concisely: {question}"
)

chain = LLMChain(llm=llm, prompt=prompt)

def run_agent(question: str) -> dict:
    """Execute the agent and return a serializable dict."""
    answer = chain.run(question)
    return {"question": question, "answer": answer.strip()}
Enter fullscreen mode Exit fullscreen mode

Trade‑off: Using a hosted LLM removes the need to manage GPU inference, but it introduces a per‑call cost (≈ $0.0006 for gpt‑4o‑mini) and a network latency of 200‑400 ms. If you need sub‑100 ms latency, you’d have to self‑host a smaller model (e.g., Phi‑2) and accept higher engineering complexity.


4. Integrating x402 payments

x402 works by attaching a payment receipt to the X-Payment header. The Worker validates the receipt against the x402 smart contract on Base (USDC contract 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913).

Below is the Cloudflare Worker (written in JavaScript) that:

  1. Checks for a valid x402 receipt.
  2. Calls the Python agent via a sub‑request to a separate /agent endpoint (running on the same Workers site via a Python‑Wasm wrapper or a lightweight Durable Object).
  3. Returns the agent’s JSON response with a 200 status, or a 402 if payment is missing/invalid.
// worker.js
import { verifyPayment } from "./x402.js"; // helper that calls the Base RPC

export default {
  async fetch(request, env, ctx) {
    const paymentHeader = request.headers.get("x-payment");
    if (!paymentHeader) {
      return new Response("Missing payment header", { status: 402 });
    }

    // Verify the receipt: amount, token, payer, and nonce
    const { valid, error } = await verifyPayment(paymentHeader, {
      amount: "0.01", // USDC amount we charge per call (adjustable)
      token: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // USDC on Base
      payer: env.PAYER_ADDRESS, // set in wrangler.toml
    });

    if (!valid) {
      return new Response(`Payment invalid: ${error}`, { status: 402 });
    }

    // Forward to the internal agent endpoint (same origin)
    const agentResp = await fetch(`${request.url}/agent`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ question: await request.text() }),
    });

    // Propagate status and body
    return new Response(agentResp.body, {
      status: agentResp.status,
      headers: agentResp.headers,
    });
  },
};
Enter fullscreen mode Exit fullscreen mode

Helper (x402.js) – a thin wrapper that uses ethers.js to call the x402 view function on Base. In production you’d cache the latest block hash to reduce RPC calls.

// x402.js
import { ethers } from "ethers";

const X402_ABI = [/* minimal ABI for verifyPayment(uint256 amount, address token, address payer, bytes calldata receipt) */];
const X402_ADDRESS = "0xX402..."; // deployed on Base

export async function verifyPayment(receiptHex, { amount, token, payer }) {
  const provider = new ethers.JsonRpcProvider(env.BASE_RPC_URL);
  const contract = new ethers.Contract(X402_ADDRESS, X402_ABI, provider);
  try {
    const result = await contract.verifyPayment(
      ethers.parseUnits(amount, 6), // USDC has 6 decimals
      token,
      payer,
      receiptHex
    );
    return { valid: result, error: null };
  } catch (e) {
    return { valid: false, error: e.message };
  }
}
Enter fullscreen mode Exit fullscreen mode

Trade‑off: Adding payment verification introduces an extra RPC round‑trip (~120 ms on Base) and requires you to manage a payer address with sufficient USDC balance. If you want truly zero‑fee ingestion you could move to a gas‑less meta‑transaction approach, but that adds custodial complexity.


5. Deploying the agent

  1. Wrangler setupwrangler init x402-agent creates a Worker project.
  2. Dependencies – add ethers and optionally langchain (via a Python‑Wasm build) to package.json.
  3. Secrets – store OPENAI_API_KEY, BASE_RPC_URL, and PAYER_ADDRESS in wrangler.toml under [vars] or use Cloudflare Secrets (wrangler secret put).
  4. Build – if you embed the Python agent via Pyodide, run wrangler build which bundles the WASM. For a simpler approach, expose a separate HTTP endpoint (e.g., a Fly.io VPS) that runs agent.py with Uvicorn; the Worker just proxies to it.
# wrangler.toml
name = "x402-agent"
main = "src/worker.js"
compatibility_date = "2024-09-01"

[vars]
OPENAI_API_KEY = "<from secret>"
BASE_RPC_URL = "https://base.mainnet.rpc.dev"
PAYER_ADDRESS = "0xYourPayerAddress"
Enter fullscreen mode Exit fullscreen mode

Deploy with wrangler publish. The Worker will be reachable at https://x402-agent.<your-subdomain>.workers.dev/.


6. Honest trade‑offs & lessons learned

Aspect What worked What was painful
Cost per call USDC 0.01 – 0.10 covers LLM

Top comments (0)