Give Your AI Agent a Wallet: Autonomous USDC Payments in 10 Minutes

AI agents can write code, analyze data, and orchestrate complex workflows. But ask them to pay for an API subscription or purchase a dataset autonomously, and they hit a wall. The missing piece? Economic agency.

In this tutorial, you'll build an AI agent that can execute real payments using USDC stablecoins—complete with safety rails, spending limits, and transaction approval flows.

Why AI Agents Need Wallets

The use cases are already here:

  • Autonomous API payments: Your agent needs access to a paid geocoding API to complete a task
  • Data acquisition: Purchasing datasets or compute credits on-demand
  • Service orchestration: Paying for cloud resources, domains, or third-party tools
  • Multi-agent economies: Agents paying each other for services

Without payment capabilities, agents must interrupt you for every transaction. With a wallet and proper guardrails, they can operate autonomously within defined boundaries.

The Stack: USDC + Base + Smart Contract Wallets

We'll use USDC (USD Coin) on Base, Coinbase's L2 network. Why this combo?

  • USDC: Stablecoin pegged 1:1 with USD—no volatility
  • Base: Sub-cent transaction fees, fast finality (~2 seconds)
  • Smart contract wallets: Programmable spending limits and approval logic

For the wallet, we'll use Safe{Wallet} (formerly Gnosis Safe)—it supports spending policies and rate limits out of the box.

Step 1: Deploy a Safe Wallet for Your Agent

First, install the Safe protocol kit:

npm install @safe-global/protocol-kit @safe-global/safe-core-sdk-types

Create and deploy a Safe wallet on Base:

import Safe from '@safe-global/protocol-kit';
import { ethers } from 'ethers';

const provider = new ethers.JsonRpcProvider('https://mainnet.base.org');
const ownerWallet = new ethers.Wallet(process.env.OWNER_PRIVATE_KEY, provider);

const safeFactory = await Safe.create({
  provider: provider.url,
  signer: ownerWallet.privateKey,
});

const safe = await safeFactory.deploySafe({
  owners: [ownerWallet.address],
  threshold: 1, // Single signature for agent operations
});

console.log('Agent wallet deployed:', await safe.getAddress());

Fund this Safe wallet with USDC from Coinbase or bridge from Ethereum mainnet.

Step 2: Add Spending Limits Module

Safe supports spending limit modules that enforce per-transaction and daily caps:

import { AllowanceModule } from '@safe-global/safe-modules-deployments';

const USDC_BASE = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';
const dailyLimit = ethers.parseUnits('100', 6); // 100 USDC, 6 decimals
const perTxLimit = ethers.parseUnits('20', 6);  // 20 USDC max per transaction

const allowanceData = AllowanceModule.encodeDelegateSetup({
  delegate: process.env.AGENT_SIGNER_ADDRESS,
  token: USDC_BASE,
  amount: dailyLimit,
  resetPeriod: 86400, // 24 hours in seconds
});

const enableModuleTx = await safe.createEnableModuleTx(AllowanceModule.address);
await safe.executeTransaction(enableModuleTx);

Now your agent can spend up to 100 USDC per day, with a 20 USDC per-transaction limit. If it tries to exceed this, the transaction reverts.

Step 3: Integrate with Your AI Agent

Here's how your AI agent (using Anthropic's SDK) can execute payments:

import Anthropic from '@anthropic-ai/sdk';

const anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });

const tools = [
  {
    name: 'send_usdc_payment',
    description: 'Send USDC payment to an address. Max 20 USDC per transaction, 100 USDC daily limit.',
    input_schema: {
      type: 'object',
      properties: {
        recipient: { type: 'string', description: 'Ethereum address to send to' },
        amount: { type: 'number', description: 'USDC amount (max 20)' },
        reason: { type: 'string', description: 'Justification for payment' },
      },
      required: ['recipient', 'amount', 'reason'],
    },
  },
];

async function executePayment(recipient: string, amount: number, reason: string) {
  const agentSigner = new ethers.Wallet(process.env.AGENT_SIGNER_PRIVATE_KEY, provider);
  
  // Create USDC transfer transaction
  const usdcInterface = new ethers.Interface([
    'function transfer(address to, uint256 amount) returns (bool)',
  ]);
  
  const transferData = usdcInterface.encodeFunctionData('transfer', [
    recipient,
    ethers.parseUnits(amount.toString(), 6),
  ]);
  
  const safeTransaction = await safe.createTransaction({
    transactions: [{ to: USDC_BASE, data: transferData, value: '0' }],
  });
  
  await safe.signTransaction(safeTransaction, agentSigner);
  const txResponse = await safe.executeTransaction(safeTransaction);
  
  console.log(`Payment executed: ${amount} USDC to ${recipient}`);
  console.log(`Reason: ${reason}`);
  console.log(`Tx hash: ${txResponse.hash}`);
  
  return txResponse.hash;
}

Step 4: Add Approval Flow for High-Value Transactions

For payments above 10 USDC, require human approval:

async function processToolCall(toolName: string, toolInput: any) {
  if (toolName === 'send_usdc_payment') {
    const { recipient, amount, reason } = toolInput;
    
    // Require approval for amounts > 10 USDC
    if (amount > 10) {
      console.log(`⚠️  Approval required for ${amount} USDC payment`);
      console.log(`Recipient: ${recipient}`);
      console.log(`Reason: ${reason}`);
      
      const readline = require('readline').createInterface({
        input: process.stdin,
        output: process.stdout,
      });
      
      const approved = await new Promise((resolve) => {
        readline.question('Approve this payment? (yes/no): ', (answer: string) => {
          readline.close();
          resolve(answer.toLowerCase() === 'yes');
        });
      });
      
      if (!approved) {
        return { success: false, error: 'Payment rejected by user' };
      }
    }
    
    const txHash = await executePayment(recipient, amount, reason);
    return { success: true, txHash };
  }
}

Safety Rails: What You Must Include

  1. Address allowlists: Only permit payments to pre-approved contracts or addresses
  2. Rate limiting: Beyond the module limits, add application-level throttling
  3. Transaction logging: Store every payment with timestamp, reason, and agent decision context
  4. Balance monitoring: Alert when the wallet drops below operational thresholds
  5. Emergency pause: Circuit breaker to freeze all agent payments

Here's a minimal allowlist implementation:

const APPROVED_RECIPIENTS = new Set([
  '0x1234...', // OpenAI API payment address
  '0x5678...', // AWS Marketplace contract
]);

function validateRecipient(address: string): boolean {
  if (!APPROVED_RECIPIENTS.has(address.toLowerCase())) {
    throw new Error(`Recipient ${address} not in allowlist`);
  }
  return true;
}

Real-World Example: Autonomous API Payments

Put it all together—an agent that purchases geocoding credits when needed:

const message = await anthropic.messages.create({
  model: 'claude-opus-4-7',
  max_tokens: 4096,
  tools,
  messages: [{
    role: 'user',
    content: 'I need to geocode 500 addresses. Check if we have enough credits, and purchase more if needed.',
  }],
});

// Agent realizes it needs credits, calls send_usdc_payment tool
// Payment executes within spending limits
// Agent continues with geocoding task

The Bottom Line

Giving AI agents economic agency is no longer theoretical—it's a 10-minute integration with the right primitives. Smart contract wallets + stablecoins + spending modules = autonomous agents that can navigate real-world economic constraints.

Start conservative: low limits, allowlists, and approval flows. As you gain confidence in your agent's decision-making, you can expand its economic autonomy.

The future isn't agents that ask for permission. It's agents that operate within permission boundaries you define once.

Full code: github.com/stackradar/ai-agent-wallet-tutorial
Safe docs: docs.safe.global
Base network: base.org