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:
- Runs unattended (no human-in-the-loop for each invocation).
- Receives payment automatically each time it provides a useful output.
- 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 |
+-----------------+
- 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);
},
};
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' },
});
},
};
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
- Create a wallet on Base (e.g., via MetaMask) and fund it with a small amount of USDC (enough for a few hundred calls).
-
Set the agent’s address as the
payeein the payment gate. -
Publish the two Workers (
payment-gateandai-worker) usingwrangler publish. Bind the AI worker as a service in the gate’swrangler.toml:
[[services]]
binding = "AI_WORKER"
service = "ai-worker"
- **
Top comments (0)