Give an AI agent a real Solana wallet
without giving it your money.

An allowance the SPL Token program enforces on-chain. Guardrails you control. x402 payments. Revocable in one instruction.

The problem

Every agent wallet today works the same way: you generate a keypair, you hand it to the agent, and now the agent has everything in it. No ceiling, no allowlist, no expiry, and no way to take it back short of racing the agent to move the funds. People work around this by keeping almost nothing in the wallet, which means the agent cannot do anything useful.

An allowance is the older, better answer. The money never moves to the agent. The agent gets permission to spend a slice of it, and the chain enforces the slice.

How it works

The owner owns a vault token account holding 500 USDC. The agent holds a delegation capped at 100 USDC over that vault, enforced by the SPL Token program. YOU Phantom · Solflare · Ledger owns it, can revoke or withdraw any time vault token account owner = you balance = 500 USDC delegate = the agent, capped at 100 ← SPL Token enforces this may spend up to the cap, and not one unit more AGENT · holds only fee SOL

Two independent layers protect you, and they fail separately:

If the machine running the agent is compromised, the local rules can be bypassed and the on-chain allowance still cannot. That is the point of splitting them. Keep the allowance at the smallest number that lets the agent work, and top it up.

Install

claude mcp add onchain-agent-wallets -- npx -y @three-ws/onchain-agent-wallets

No key required to start. Owner actions come back as an unsigned transaction for your wallet to sign. Set OWNER_SECRET_KEY only if you want the server to sign for you.

Quickstart

Give a research agent 100 USDC of spending power, capped at 5 per transaction and 20 per day, for one API only:

create_agent_wallet
  id: "researcher"
  allowance: "100"
  per_tx: "5"
  daily: "20"
  allow_hosts: ["api.example.com"]
  confirm: true

fund_agent_wallet  id: "researcher"  amount: "500"  sol: "0.02"  confirm: true

The agent then spends on its own, and you stay in control:

pay_x402             id: "researcher"  url: "https://api.example.com/premium"  confirm: true
agent_wallet_status  id: "researcher"                    # live balances, remaining allowance
spend_log            id: "researcher"                    # every spend AND every refusal
set_guardrails       id: "researcher"  paused: true      # instant, free, no transaction
revoke_agent_wallet  id: "researcher"  withdraw: true  confirm: true

A refusal tells you exactly which rule fired

{
  "ok": false,
  "error": "over_daily",
  "message": "20.5 would exceed the 20 daily cap (18 already spent)",
  "checks": [
    { "check": "per_tx", "ok": true,  "detail": "2.5 is within the 5 per-transaction cap" },
    { "check": "daily",  "ok": false, "detail": "20.5 would exceed the 20 daily cap (18 already spent)" }
  ]
}

Guardrails

RuleEffectEnforced by
allowanceTotal the agent may ever spend before a new approvalSPL Token program
vault balanceThe agent cannot spend what is not thereSPL Token program
per_txMaximum single spendthis server
dailyMaximum per rolling 24 hoursthis server
allow_recipientsOnly these addresses may receive fundsthis server
allow_hostsOnly these hosts may be paid over x402this server
expires_atEvery spend refused after this timestampthis server
confirm_overSpends above this need an explicit human yesthis server
pausedEverything refused, instantly and for freethis server

x402

pay_x402 probes the endpoint unpaid first, so the price is known before anything moves and the guardrails see the real number. Then it tops the agent up for exactly that amount out of the vault, pays, and leaves the agent empty again. It refuses to pay on a chain other than Solana, on the wrong cluster, in a token the allowance is not denominated in, or above the per-call max_price.

What the agent cannot do

Proven in test/token-delegation.test.mjs, which runs the real SPL Token program in process and asserts that a spend of 61 against a remaining allowance of 60 fails on-chain, with 460 still sitting in the vault.

Rehearse for free

OAW_NETWORK=devnet npx -y @three-ws/onchain-agent-wallets

State is kept per network, so a devnet rehearsal never touches your mainnet agents.