How I Built an Autonomous AI Agent That Earns USDC While I Sleep A pragmatic walk‑through for developers who want to experiment with paid micro‑services on‑chain. 1. Why bother with an “earning while you sleep” agent? The idea is simple: expose a tiny, useful function (e.g., “return the latest ETH/USDC price from three exchanges”) as an HTTP endpoint that requires payment before it returns data. If the function is cheap to run and the price per call is set low enough, a steady stream of requesters can generate a trickle of revenue that pays for the host’s compute cost—and maybe a little extra. In practice the earnings are modest (fractions of a cent per call) and the system needs constant uptime, monitoring, and a way to handle failed payments. The goal of this post is to show how you can put the pieces together, not to promise passive income. 2. High‑level architecture +-------------------+ x402 payment flow +-------------------+ | Caller (any HTTP) | <----------------------> | Agent Service | | (browser, script) | USDC micropayment (x402) | (Cloudflare Worker| +-------------------+ +-------------------+ + / Python Flask) | | Verify & sign receipt | | v v | +-------------------+ +-------------------+ | USDC on Base | | Agent Logic | | (ERC‑20 contract)| | (price fetch, etc)| +-------------------+ +-------------------+ x402 – a lightweight HTTP 402‑Payment Required extension that lets a service demand a signed USDC transfer before fulfilling a request. Agent Logic – the actual work the agent does (here: a simple price aggregator). Host – any cheap, always‑on compute. I used a Cloudflare Workers script because it gives free sub‑second cold starts and built‑in KV for caching, but a tiny Flask app on a $5 VPS works just as well. The loop is: Caller hits /price?symbol=ETH. Middleware checks for a valid x402 payment header. If missing/invalid, returns 402 Payment Required with payment details. If valid, the agent runs its logic, returns the result, and signs a receipt that the caller can verify on‑chain. 3. Setting up a USDC wallet on Base You need an address that holds enough USDC to cover the gas for the receipt signature (the agent never spends USDC; it only signs). # pip install web3 eth-account from web3 import Web3 from eth_account import Account import os # RPC for Base (mainnet) BASE_RPC = os.getenv("BASE_RPC", "https://mainnet.base.org") w3 = Web3(Web3.HTTPProvider(BASE_RPC)) # Generate or load a key – NEVER commit the private key to repo! PRIVATE_KEY = os.getenv("AGENT_PRIVATE_KEY") if not PRIVATE_KEY: raise RuntimeError("Set AGENT_PRIVATE_KEY env var") acct = Account.from_key(PRIVATE_KEY) ADDRESS = acct.address print(f"Agent address: {ADDRESS}") # Optional: check USDC balance (USDC contract on Base) USDC_ADDRESS = Web3.to_checksum_address("0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913") USDC_ABI = [...] # standard ERC20 abi (balanceOf, decimals) usdc = w3.eth.contract(address=USDC_ADDRESS, abi=USDC_ABI) balance = usdc.functions.balanceOf(ADDRESS).call() print(f"USDC balance: {balance / 1e6:.2f} USDC") Trade‑off: Keeping the private key in an environment variable is fine for a demo, but production workloads should use a managed KMS or a hardware signer to reduce exposure. 4. Minimal x402 middleware (Python/Flask) The x402 spec defines a Payment-Required header that contains: x402-version: 1 network: base currency: usdc maxAmountRequired: 100000 # in smallest units (0.001 USDC) resource: https://example.com/price The client must respond with a signed transfer of USDC to the agent’s address, then retry with the X-Payment header containing the transaction hash. # app.py from flask import Flask, request, jsonify, make_response import json import hashlib import time from web3 import Web3 from eth_account.messages import encode_defunct app = Flask(__name__) w3 = Web3(Web3.HTTPProvider(os.getenv("BASE_RPC"))) AGENT_ACCOUNT = Account.from_key(os.getenv("AGENT_PRIVATE_KEY")) # ---- CONFIG ------------------------------------------------- PRICE_PER_CALL = 1_000_000 # 0.001 USDC (6 decimals) NETWORK = "base" CURRENCY = "usdc" # ----------------------------------------------------------- def build_402_response(resource_path): resp = make_response(jsonify({"error": "payment required"}), 402) resp.headers["x402-version"] = "1" resp.headers["network"] = NETWORK resp.headers["currency"] = CURRENCY resp.headers["maxAmountRequired"] = str(PRICE_PER_CALL) resp.headers["resource"] = f"https://{request.host}{resource_path}" return resp def verify_payment(tx_hash): """Very lightweight check: tx succeeded and sent >= PRICE_PER_CALL.""" try: tx = w3.eth.get_transaction(tx_hash) receipt = w3.eth.get_transaction_receipt(tx_hash) if receipt.status != 1: return False # Ensure it's a USDC transfer to our address if tx["to"].lower() != USDC_ADDRESS.lower(): return False # Decode transfer data (simplified; in prod use abi) # For brevity we assume the caller sent exactly the amount. return True except Exception: return False @app.route("/price") def price(): resource = request.path # 1️⃣ Check for payment header pay_header = request.headers.get("X-Payment") if not pay_header: return build_402_response(resource) # Expect JSON: {"txHash": "0x..."} try: payload = json.loads(pay_header) tx_hash = payload["txHash"] except (json.JSONDecodeError, KeyError): return build_402_response(resource) if not verify_payment(tx_hash): return build_402_response(resource) # 2️⃣ Payment good – run agent logic symbol = request.args.get("symbol", "ETH").upper() price_usd = fetch_price(symbol) # defined below return jsonify({"symbol": symbol, "priceUSDC": price_usd}) def fetch_price(symbol): """Stub: call three public APIs and return the median.""" import requests, statistics urls = [ f"https://api.coingecko.com/api/v3/simple/price?ids={symbol.lower()}&vs_currencies=usdc", "https://api.binance.com/api/v3/ticker/price?symbol=ETHUSDC", "https://api.kraken.com/0/public/Ticker?pair=ETHUSDC", ] prices = [] for u in urls: try: r = requests.get(u, timeout=3) data = r.json() # very naive parsing – replace with proper handling per API if "coingecko" in u: prices.append(data[symbol.lower()]["usdc"]) elif "binance" in u: prices.append(float(data["price"])) elif "kraken" in u: prices.append(float(data["result"]["XETHZUSDC"]["c"][0])) except Exception: continue if not prices: raise RuntimeError("price fetch failed") return statistics.median(prices) if __name__ == "__main__": app.run(host="0.0.0.0", port=8080) What this does Returns 402 with payment details if no X-Payment header is present. Verifies that the supplied transaction is a successful USDC transfer of at least the configured amount to the agent’s address. Runs a simple price‑aggregator and returns the median USDC price. Trade‑offs The verification is deliberately lightweight (no full ERC‑20 decode). In production you’d want to use a contract that escrows funds or a verification service to avoid replay attacks. The price fetcher uses free public APIs; they rate‑limit aggressively. For a real service you’d either pay for a reliable data feed or cache results for a few seconds. 5. Deploying the agent I chose Cloudflare Workers because it gives: Sub‑second global edge latency (important for micro‑services that may be called from dApps or bots). Built‑