← All posts

Web3 Meets AI: Giving Autonomous Agents a Wallet

How AI agents transact on-chain, what changes for the frontend, and the guardrails that keep an autonomous agent from draining a wallet

Two technologies that grew up in different rooms are now in the same conversation. AI gives software the ability to reason and act; Web3 gives software an account, money, and a way to settle value without a middleman. Put an AI agent behind a wallet and it can do something a chatbot never could: pay for things and act economically on its own. That is powerful, and it is exactly as dangerous as it sounds. This post is about building that bridge responsibly — mostly from the frontend engineer's seat.

Why the Two Are Converging

An agent loop perceives, decides, and acts. Most of the interesting actions — booking compute, buying an API call, tipping a data provider, settling a trade — eventually involve a payment. Traditional rails (cards, banks) assume a human clicking “confirm” and are gated behind KYC and merchant accounts. Crypto rails do not. A wallet is just a keypair; a stablecoin transfer is just a function call. For an autonomous agent, that programmability is the whole point:

  • Agents can hold and spend value without a human in the loop for every transaction

  • Settlement is near-instant and global, with no merchant onboarding

  • Every action is auditable on a public ledger — useful for accountability

  • Smart contracts let you encode rules (spending caps, allow-lists) the agent literally cannot break

The last point matters most. With Web3 you do not just ask an agent nicely to stay within budget — you can enforce it at the protocol level.

The New Frontend Problem: Signing on Behalf of an Agent

On a normal dApp the human owns the keys. They click a button, the wallet pops up, they read the transaction and approve it. An autonomous agent has no hands to click and no eyes to read the popup. So the frontend's job shifts from “render a connect button” to “design a trust boundary.” Three models exist, in increasing order of autonomy:

  1. Human-in-the-loop — the agent proposes a transaction; the UI surfaces it; a human signs. Safest, slowest.

  2. Delegated session keys — the human pre-authorises a scoped key (spend up to X, only these contracts, expires in 24h). The agent signs within those limits.

  3. Fully autonomous smart account — the agent operates a smart-contract wallet whose on-chain policy enforces every limit. No human per transaction.

Most production systems today live at level 2. It is the sweet spot: real autonomy, bounded blast radius.

The Stack, Concretely

On the frontend, the modern toolchain is viem (typed, lightweight Ethereum client) and wagmi (React hooks on top of viem). Connecting a human wallet is a few lines:

import { createConfig, http, useConnect, useAccount } from "wagmi";
import { mainnet, base } from "wagmi/chains";
import { injected } from "wagmi/connectors";

export const config = createConfig({
  chains: [mainnet, base],
  connectors: [injected()],
  transports: { [mainnet.id]: http(), [base.id]: http() },
});

function ConnectButton() {
  const { connect } = useConnect();
  const { address, isConnected } = useAccount();

  if (isConnected) return <span>Connected: {address}</span>;
  return <button onClick={() => connect({ connector: injected() })}>Connect wallet</button>;
}

But an agent does not use a browser wallet. It uses a programmatic account. With viem you derive a local account from a key the agent controls — and that key should never be a human's main private key. It should be a session key or a smart-account signer with narrow permissions:

import { createWalletClient, http, parseEther } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { base } from "viem/chains";

// A scoped session key, provisioned for the agent — NOT the owner's root key.
const account = privateKeyToAccount(process.env.AGENT_SESSION_KEY as `0x${string}`);

const wallet = createWalletClient({ account, chain: base, transport: http() });

async function payProvider(to: `0x${string}`, amountEth: string) {
  return wallet.sendTransaction({ to, value: parseEther(amountEth) });
}

Agentic Payments: x402 and Smart Accounts

The cleanest mental model for agent payments is x402 — a revival of the long-dormant HTTP 402 Payment Required status code. A server responds 402 with a price and a payment address; the client (an agent) pays in stablecoin and retries with proof of payment. No accounts, no API keys, no checkout flow — just a machine paying a machine over HTTP.

// Agent-side: handle a 402 the way a browser handles a redirect.
async function fetchWithPayment(url: string) {
  let res = await fetch(url);
  if (res.status === 402) {
    const { amount, asset, payTo } = await res.json();
    const proof = await wallet.sendStablecoin({ to: payTo, amount, asset });
    res = await fetch(url, { headers: { "X-Payment": proof.hash } });
  }
  return res;
}

Underneath, autonomy at scale leans on account abstraction (ERC-4337 and the newer EIP-7702). A smart-contract account can encode policy directly: “this session key may spend at most 50 USDC per day, only to addresses on this allow-list, and only call these two contracts.” The agent physically cannot exceed that — the blockchain rejects the transaction. This is the enforcement layer that makes level-2 and level-3 autonomy safe.

Feeding the Agent: RAG Over On-Chain Data

An agent acting on-chain needs to understand on-chain state: balances, prices, contract events, governance proposals. Raw RPC calls return bytes, not meaning. This is where the AI patterns connect back — you build a retrieval layer over indexed chain data.

  • The Graph — index contract events into a queryable GraphQL subgraph

  • Embeddings — embed proposal text, docs, and protocol changelogs for semantic retrieval (the RAG pattern, applied to chain data)

  • Tool calls — expose read-only queries (balanceOf, getReserves, quote) as agent tools so the model fetches live state on demand

If you have read my posts on RAG and agent harnesses, this is the same architecture — the retrieved context just happens to be blockchain state instead of internal docs.

Guardrails: The Part You Cannot Skip

An agent that can sign transactions and a bug in your loop is a drained wallet. Defence is layered — never trust a single check:

  1. Never give an agent a root private key. Use session keys or smart-account signers with on-chain limits.

  2. Cap spending on-chain, not just in code — per-transaction and per-day caps the contract enforces.

  3. Allow-list destinations and contracts. An agent should only be able to call what its task requires.

  4. Simulate before sending. viem's simulateContract / eth_call previews the result and reverts caught bugs for free.

  5. Add human-in-the-loop above a threshold. Small payments autonomous; anything large surfaces a confirmation.

  6. Log every signed action. The ledger is public, but you still want your own audit trail tied to the agent's reasoning.

// A guarded tool the agent calls instead of signing directly.
async function proposePayment(to: `0x${string}`, usd: number) {
  if (!ALLOWLIST.has(to)) throw new Error("Destination not allow-listed");
  if (usd > PER_TX_CAP)   throw new Error(`Exceeds per-tx cap of $${PER_TX_CAP}`);
  if (spentToday + usd > DAILY_CAP) throw new Error("Daily cap reached");

  // Simulate first — catches reverts before spending gas.
  await publicClient.call({ account, to, value: toWei(usd) });

  if (usd > HUMAN_REVIEW_THRESHOLD) return requestHumanApproval({ to, usd });
  return wallet.sendTransaction({ to, value: toWei(usd) });
}

The rule of thumb: assume the model will eventually try to do the wrong thing — hallucinate an address, misread an amount, loop on a failing call. Your guardrails are not there for the happy path. They are there for the day the model is confidently wrong.

Where This Is Going

The agent-with-a-wallet pattern is early but moving fast: stablecoin payment rails for machines, smart accounts that ship with policy primitives, and standards like x402 turning “pay per request” into plumbing. For frontend engineers, the work is less about pixels and more about designing the trust boundary — what the agent may do, what a human must approve, and what the chain enforces no matter what the model decides.


RAG gives a model memory. Agent harnesses give it agency. MCP gives it reach. Web3 gives it economic agency — the ability to hold value and act on it. The interesting engineering is no longer “can the agent do this?” but “can we let it, safely?” That question is a frontend problem as much as a protocol one.