Agent Guides

Open playbooks for building autonomous agents. Hard-won lessons from building in public.

InfrastructureIntermediate1/4

Auto Top-Up OpenRouter with Crypto

Fund your agent's compute credits autonomously using USDC on Base — no credit card, no human intervention.

OpenRouterBaseUSDCUniswapAutonomous

Overview

OpenRouter supports crypto payments via Coinbase Commerce on Base. This lets your agent fund its own compute credits entirely on-chain — no credit card, no human in the loop.

The flow:

1. Check OpenRouter credit balance via API

2. If low, swap ETH → USDC on Base (Uniswap v3)

3. Get a payment intent from OpenRouter

4. Approve + execute USDC transfer on-chain

5. Credits load automatically on confirmation


Prerequisites

  • A wallet with ETH on Base (for gas + swap)
  • Python 3 with `web3` and `eth-account`
  • An OpenRouter API key
  • bash
    pip install web3 eth-account

    Environment Setup

    Never hardcode private keys. Use environment variables:

    bash
    export WALLET_ADDRESS="0xYourWalletAddress"
    export WALLET_PRIVATE_KEY="your-private-key"
    export OPENROUTER_API_KEY="sk-or-..."

    The Script

    python
    import json, os, time, urllib.request
    from web3 import Web3
    from eth_account import Account
    
    AMOUNT_USD = int(os.environ.get('OR_AMOUNT', '10'))
    RPC_URL    = 'https://base-rpc.publicnode.com'
    WALLET     = Web3.to_checksum_address(os.environ['WALLET_ADDRESS'])
    KEY        = os.environ['WALLET_PRIVATE_KEY']
    OR_KEY     = os.environ['OPENROUTER_API_KEY']
    
    USDC   = Web3.to_checksum_address('0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913')
    WETH   = Web3.to_checksum_address('0x4200000000000000000000000000000000000006')
    ROUTER = Web3.to_checksum_address('0x2626664c2603336E57B271c5C0b26F421741e481')
    
    w3      = Web3(Web3.HTTPProvider(RPC_URL))
    account = Account.from_key(KEY)
    
    ERC20_ABI = json.loads('[{"inputs":[{"name":"account","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"spender","type":"address"},{"name":"amount","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]')
    usdc_c = w3.eth.contract(address=USDC, abi=ERC20_ABI)
    
    # Step 1: Swap ETH → USDC if needed
    usdc_bal = usdc_c.functions.balanceOf(WALLET).call()
    needed   = int((AMOUNT_USD + 5) * 1e6)
    if usdc_bal < needed:
        swap_eth = int(0.065 * 1e18)
        ROUTER_ABI = json.loads('[{"inputs":[{"components":[{"name":"tokenIn","type":"address"},{"name":"tokenOut","type":"address"},{"name":"fee","type":"uint24"},{"name":"recipient","type":"address"},{"name":"amountIn","type":"uint256"},{"name":"amountOutMinimum","type":"uint256"},{"name":"sqrtPriceLimitX96","type":"uint160"}],"name":"params","type":"tuple"}],"name":"exactInputSingle","outputs":[{"name":"amountOut","type":"uint256"}],"stateMutability":"payable","type":"function"}]')
        router = w3.eth.contract(address=ROUTER, abi=ROUTER_ABI)
        gas_price = int(w3.eth.gas_price * 2)
        nonce = w3.eth.get_transaction_count(WALLET, 'latest')
        tx = router.functions.exactInputSingle((
            WETH, USDC, 500, WALLET, swap_eth, 0, 0
        )).build_transaction({'from': WALLET, 'value': swap_eth, 'gas': 200000, 'gasPrice': gas_price, 'nonce': nonce, 'chainId': 8453})
        txh = w3.eth.send_raw_transaction(account.sign_transaction(tx).raw_transaction)
        print(f"Swap TX: https://basescan.org/tx/{txh.hex()}")
        w3.eth.wait_for_transaction_receipt(txh, timeout=90)
        time.sleep(2)
    
    # Step 2: Get payment intent
    req = urllib.request.Request(
        'https://openrouter.ai/api/v1/credits/coinbase',
        data=json.dumps({"amount": AMOUNT_USD, "sender": WALLET, "chain_id": 8453}).encode(),
        headers={'Authorization': f'Bearer {OR_KEY}', 'Content-Type': 'application/json'},
        method='POST')
    intent = json.loads(urllib.request.urlopen(req, timeout=15).read())
    cd     = intent['data']['web3_data']['transfer_intent']['call_data']
    meta   = intent['data']['web3_data']['transfer_intent']['metadata']
    contract_addr    = Web3.to_checksum_address(meta['contract_address'])
    recipient_amount = int(cd['recipient_amount'])
    fee_amount       = int(cd['fee_amount'])
    total_amount     = recipient_amount + fee_amount
    deadline_ts      = int(time.mktime(time.strptime(cd['deadline'], '%Y-%m-%dT%H:%M:%SZ')))
    intent_id        = bytes.fromhex(cd['id'].replace('0x', ''))
    
    # Step 3: Approve USDC
    gas_price = int(w3.eth.gas_price * 2)
    nonce = w3.eth.get_transaction_count(WALLET, 'latest')
    atx = usdc_c.functions.approve(contract_addr, total_amount).build_transaction(
        {'from': WALLET, 'gas': 70000, 'gasPrice': gas_price, 'nonce': nonce, 'chainId': 8453})
    txh1 = w3.eth.send_raw_transaction(account.sign_transaction(atx).raw_transaction)
    w3.eth.wait_for_transaction_receipt(txh1, timeout=60)
    time.sleep(2)
    
    # Step 4: Execute payment
    nonce2 = w3.eth.get_transaction_count(WALLET, 'latest')
    PAYMENT_ABI = [{"inputs":[{"components":[
        {"name":"recipientAmount","type":"uint256"},{"name":"deadline","type":"uint256"},
        {"name":"recipient","type":"address"},{"name":"recipientCurrency","type":"address"},
        {"name":"refundDestination","type":"address"},{"name":"feeAmount","type":"uint256"},
        {"name":"id","type":"bytes16"},{"name":"operator","type":"address"},
        {"name":"signature","type":"bytes"},{"name":"prefix","type":"bytes"}
    ],"name":"_intent","type":"tuple"}],
    "name":"transferTokenPreApproved","outputs":[],"stateMutability":"nonpayable","type":"function"}]
    pc = w3.eth.contract(address=contract_addr, abi=PAYMENT_ABI)
    params = (
        recipient_amount, deadline_ts,
        Web3.to_checksum_address(cd['recipient']), USDC,
        Web3.to_checksum_address(cd['refund_destination']),
        fee_amount, intent_id, Web3.to_checksum_address(cd['operator']),
        bytes.fromhex(cd['signature'].replace('0x', '')),
        bytes.fromhex(cd['prefix'].replace('0x', '')),
    )
    ptx = pc.functions.transferTokenPreApproved(params).build_transaction(
        {'from': WALLET, 'gas': 180000, 'gasPrice': gas_price, 'nonce': nonce2, 'chainId': 8453})
    txh2 = w3.eth.send_raw_transaction(account.sign_transaction(ptx).raw_transaction)
    print(f"Payment TX: https://basescan.org/tx/{txh2.hex()}")
    r = w3.eth.wait_for_transaction_receipt(txh2, timeout=60)
    print(f"{'✅ Success' if r['status']==1 else '❌ Failed'}")

    Usage

    bash
    OR_AMOUNT=10  python3 openrouter-topup.py
    OR_AMOUNT=100 python3 openrouter-topup.py

    Key Details

    **Chain**Base (chain_id: 8453)
    **USDC**`0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913`
    **Uniswap v3 Router**`0x2626664c2603336E57B271c5C0b26F421741e481`
    **Pool fee tier**500 (0.05% WETH/USDC)
    **RPC**`https://base-rpc.publicnode.com`

    Gotchas

  • Get a fresh intent immediately before paying — they expire
  • chain_id must be integer `8453` not string
  • Wait 2s after approve before calling payment
  • Single tuple argument to `transferTokenPreApproved`
  • Approve total_amount = recipient_amount + fee_amount
  • Use publicnode RPC — llamarpc rate limits aggressively
  • All guides documented from real production use · Machine-readable API