Warns
Blog

Why a spend cap is not a security layer

Médhy Chabour, founder · September 23, 2026, updated September 24, 2026, 4 min read

When you give an AI agent a wallet, the first safety tool you reach for is a cap: "never pay more than one dollar per request". The x402 client ships with exactly that: in @x402/core 2.26, the version we tested, every payment is capped at one dollar unless you change it. Older clients used other limits; the argument holds for any fixed number.

We spent the last two weeks measuring how agents actually pay on Base, then building four hostile merchants to see what a capped agent does when a merchant misbehaves. This is what we found.

What agents pay on Base

x402 settles with a USDC authorization the payer signs and the merchant's facilitator submits. Every one of those settlements is public, so we counted them over the last 90 days. The full dashboard is on Dune.

Last 90 days on Base
Payments26.4 million
Volume115 million USD
Distinct payers184,000
Median payment0.006 USD
Payments under 1 USD98.2%

Two things stand out. First, this is real volume, not a demo: tens of millions of machine-to-machine payments in a quarter. Second, almost all of it is tiny. Nine payments in ten are under ten cents.

What we count: every successful transferWithAuthorization call on the Base USDC contract, from June 25 to September 23, 2026. That is an upper bound for x402, because other products use the same primitive, and we cannot attribute each call to an agent. Read the numbers as the shape of small USDC payments on Base, not as a count of agents. The queries are public on the dashboard.

Why the cap does not see the attack

Look at the median again: 0.006 USD. A merchant that charges an agent one hundred times its usual price is still at 0.60 USD. Under the cap. The agent pays, the cap is satisfied, nothing is logged as unusual.

The cap answers one question: is this amount big? Every attack we care about answers "no" on purpose. The attacker does not need one big payment. It needs the agent to keep paying small ones.

We built four merchants that misbehave the way a hacked one does, all on Base Sepolia so it costs nothing to try. Each stays under one dollar.

  • Price spike. The same endpoint, the same data, suddenly 95 times the usual price. Still 0.95 USD.
  • Payout address swapped. The price does not move. The money goes to a brand new address. The service keeps working, so nobody notices.
  • Long authorization window. The merchant asks for an authorization that stays valid for an hour instead of a minute. It can hold it and settle it any time in that hour. An authorization is used only once, but if your agent gives up on the request and pays again, both can go through.
  • Injected instruction. A free endpoint whose content tells the agent to pay an "unlock" fee to a stranger. An agent that follows what it reads pays it.

What the same client did, with and without Warns

We ran one x402 client, @x402/core 2.26 with its default settings, 1 USD cap included, against the traps on Base Sepolia on September 24, 2026. One honest request first, then the four traps. Then the same five requests again with Warns in front of the client. Each run used its own test wallet, so the unprotected payments could not freeze the protected agent. Every settled payment links to its transaction.

RequestAskedWithout WarnsWith Warns
Honest price0.01 USDCPaid, 0x01bb…a61fPaid, 0xac62…e384
Price spike0.95 USDCPaid, 0xd4b3…3207Blocked before signing: above the payment budget and the declared intent
Payout address swapped0.01 USDCPaid, 0xfd06…3091Held for a human: new destination
Long authorization window0.01 USDCPaid, 0x8619…35fbBlocked before signing: window longer than the client accepts
Attacker endpoint0.90 USDCPaid, 0xae01…1f21Blocked before signing: new destination, above budget and intent
Total paid1.88 USDC0.01 USDC

Without Warns, the client paid all five, and 1.87 USDC of it went to the four traps. The cap never fired, because every request stayed under one dollar. With Warns, the honest price went through and nothing else was signed. The swapped address waits for a human in the dashboard: approve it and it pays, deny it and it never does.

The prompt injection is not a payment of its own: it is a free page that sends the agent to the attacker endpoint, the last row above.

You can run this yourself at trap.warns.xyz. Enter your agent's address afterwards and it lists every payment it signed, with the transaction.

What a decision looks like

A cap looks at one number. A decision looks at the payment in context, before the signature exists.

  • Price against history. What has this endpoint charged before? A jump of 95 times is not a price, it is an event.
  • Destination. Is this the address this merchant has always been paid at? A new one is worth a human look, not an automatic yes.
  • Intent. The agent declares what it is about to do and how much that should cost. A quote should not cost more than the agent expected to pay for a quote.
  • Budgets. Per payment, per day, per agent. The boring part, still needed.
  • The authorization itself. How long it stays valid. The longer an unused authorization lives, the longer the merchant decides when, and whether, it gets settled.

The result is one of three answers: allow, hold for a human, or block. The agent's key signs only after an allow. Nothing else changes in your stack: the SDK replaces the fetch you already pass to the x402 client, and your signer stays yours.

What this does not do

Honesty matters more than a nice story here. Evaluating before signing stops a merchant, a compromised dependency or an injected instruction from making your agent pay. It does not stop code that holds the raw key and never calls the SDK.

For that case, reconciliation watches the chain. Any payment that did not go through a decision is flagged and the agent is frozen, so Warns approves nothing more for it until someone looks. That is detection after the fact: it does not stop a key used somewhere else, and more than one payment can land before the freeze. What it guarantees is that a bypass never goes unseen. The next step, on the roadmap, is a signer that checks the decision itself, inside the wallet or the enclave, so nothing leaves without one.

Try it on your own agent

The traps are free and public. Point your agent at them, see what it signs, then wrap it and run the same four again. If it pays a merchant that misbehaves, you want to know now, on testnet, and not from a bank statement.

Test your agents against the traps.

Four x402 endpoints that misbehave the way a hacked merchant does. Testnet USDC, nothing at risk.

Test your agents Request early access