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

A pragmatic walk‑through for developers who want to turn a simple AI service into a self‑sustaining, micro‑paid workload.


Why “earn while you sleep” matters (and what it doesn’t mean)

The phrase “earn while you sleep” is often wrapped in hype. In practice it simply describes a system that:

  1. Runs unattended (no human-in-the-loop for each invocation).
  2. Receives payment automatically each time it provides a useful output.
  3. Keeps operating as long as the underlying infrastructure stays healthy and the market for its service exists.

It does not guarantee passive income, infinite scalability, or immunity from bugs. The goal is to offset the cost of running the agent (compute, storage, network) and maybe pocket a small surplus—nothing more.


High‑level architecture

+----------------+      +----------------+      +-----------------+
|  Trigger (e.g. | ---> |  Payment Gate | ---> |  AI Worker      |
|  webhook / cron) |    |  (x402 on Base) |    |  (Node/TS)      |
+----------------+      +----------------+      +-----------------+
                                   |
                                   v
                         +-----------------+
                         |  USDC Escrow    |
                         +-----------------+
Enter fullscreen mode Exit fullscreen mode
  • Trigger – Anything that can start a job on a schedule or via an HTTP request (Cloudflare Workers cron, AWS EventBridge, or a simple HTTP webhook).
  • Payment Gate – A thin wrapper that validates an x402 payment receipt before forwarding the request to the worker. If payment fails, the request is rejected with HTTP 402.
  • AI Worker – The core logic that does useful work (e.g., summarising a URL, classifying an image, fetching a price feed). It returns the result and optionally writes a receipt to a durable store for audit.
  • USDC Escrow – Not a separate service; the x402 standard already handles locking USDC in a smart contract on Base and releasing it to the agent’s address once the worker acknowledges success.

The loop is stateless: each invocation is independent, which simplifies scaling and failure recovery.


Setting up the x402 payment gate

x402 is an experimental HTTP status code (402 Payment Required) that lets a server request payment before serving a resource. The client must attach a valid ERC‑20 payment proof (a signed Payment object) in the X-Payment header.

Below is a minimal Cloudflare Worker that acts as the gate. It verifies the payment using the x402-js library, then forwards the request to the AI worker (another Worker bound as a service).

// payment-gate.ts
import { verifyPayment } from 'x402-js';
import { env } from '$fresh';

// Address that will receive USDC (the agent's wallet)
const AGENT_ADDRESS = '0xYourAgentAddressHere';

// Minimum price in USDC (6 decimals) – adjust to your cost model
const PRICE_USDC = 500_000; // $0.005 (5 milli‑USDC)

export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    // Extract payment header
    const paymentHeader = request.headers.get('X-Payment');
    if (!paymentHeader) {
      return new Response('Missing X-Payment header', { status: 402 });
    }

    let payment;
    try {
      payment = JSON.parse(paymentHeader);
    } catch {
      return new Response('Invalid X-Payment JSON', { status: 400 });
    }

    // Verify the payment against the x402 contract on Base
    const valid = await verifyPayment({
      payment,
      payer: payment.payer, // address that signed
      payee: AGENT_ADDRESS,
      token: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', // USDC on Base
      amount: PRICE_USDC,
      chainId: 8453, // Base
    });

    if (!valid) {
      return new Response('Payment verification failed', { status: 402 });
    }

    // Payment OK – forward to the AI worker service
    const aiWorker = env.AI_WORKER; // binding defined in wrangler.toml
    return aiWorker.fetch(request);
  },
};
Enter fullscreen mode Exit fullscreen mode

Trade‑offs

Aspect Choice Reason Downside
Payment verification On‑chain verification via x402-js Guarantees authenticity; no trusted third party Adds ~150‑300 ms latency (depends on RPC node)
Price granularity Fixed price in micro‑USDC Simplifies client integration Hard to adjust per‑request cost without redeploy
Host Cloudflare Workers (serverless) Zero‑ops, cheap cold starts, global edge Limited execution time (50 s max) and no persistent filesystem

If you need longer-running jobs (e.g., model training), replace the worker with an AWS Lambda or a Kubernetes job and keep the gate as a thin auth layer.


The AI worker – a concrete example

For illustration, I built a URL summariser that fetches a public article, runs a lightweight open‑source summarization model (via Hugging Face’s API), and returns a 2‑sentence abstract. The model is cheap enough that the $0.005 per call covers the inference cost plus a tiny margin.

// ai-worker.ts
import { HfInference } from '@huggingface/inference';

const HF_TOKEN = env.HF_TOKEN; // set in wrangler.toml
const hf = new HfInference(HF_TOKEN);

export default {
  async fetch(request: Request): Promise<Response> {
    const { url } = await request.json<{ url: string }>();
    if (!url || !/^https?:\/\//.test(url)) {
      return new Response('Invalid or missing "url" field', { status: 400 });
    }

    // 1️⃣ Fetch the article (simple GET, no JS rendering)
    let articleText: string;
    try {
      const resp = await fetch(url, { headers: { 'User-Agent': 'x402-agent/1.0' } });
      if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
      articleText = await resp.text();
    } catch (e) {
      return new Response(`Failed to fetch URL: ${e}`, { status: 502 });
    }

    // 2️⃣ Extract plain text (very naive – replace with readability.js if needed)
    const text = articleText
      .replace(/<[^>]*>/g, ' ')   // strip tags
      .replace(/\s+/g, ' ')
      .slice(0, 4000); // limit to avoid huge prompts

    // 3️⃣ Call Hugging Face summarization model
    let summary: string;
    try {
      const out = await hf.summarization({
        model: 'facebook/bart-large-cnn',
        inputs: text,
        parameters: { max_length: 100, min_length: 30 },
      });
      summary = out[0]?.summary_text ?? '';
    } catch (e) {
      return new Response(`Summarization failed: ${e}`, { status: 502 });
    }

    if (!summary) {
      return new Response('Empty summary produced', { status: 500 });
    }

    // 4️⃣ Return result
    return new Response(JSON.stringify({ summary }), {
      headers: { 'Content-Type': 'application/json' },
    });
  },
};
Enter fullscreen mode Exit fullscreen mode

Trade‑offs

Decision Why Caveat
Model choice (bart-large-cnn) Good quality/size trade‑off for free HF inference API Still relies on external API; if HF throttles, you pay for retries or need a self‑hosted model.
Text truncation (4000 chars) Keeps prompt within model limits and reduces latency May cut off important context for very long articles.
No JS rendering Simpler, faster, cheaper Fails on SPA‑only sites; you could add a headless browser (e.g., Puppeteer) at the cost of higher compute and latency.
Error handling Returns explicit HTTP codes so the gate can retry or chargeback Requires clients to interpret 402/502 correctly; not all generic HTTP clients do.

Deploying and funding the agent

  1. Create a wallet on Base (e.g., via MetaMask) and fund it with a small amount of USDC (enough for a few hundred calls).
  2. Set the agent’s address as the payee in the payment gate.
  3. Publish the two Workers (payment-gate and ai-worker) using wrangler publish. Bind the AI worker as a service in the gate’s wrangler.toml:
   [[services]]
   binding = "AI_WORKER"
   service = "ai-worker"
Enter fullscreen mode Exit fullscreen mode
  1. **

Top comments (0)