Which wallet capability stops an agent's poisoned signature?

An AI agent that holds a wallet decides in English but signs bytes. A poisoned tool can pair a benign sentence with a malicious signature. This maps which wallet defense stops which drain, and the one that survives all of them.

The gap

Wallet safety used to be a human deciding to sign. An autonomous agent removes the human from that step: it forms an intent in natural language, then signs calldata or a typed message, at machine speed, across many actions. If a tool it uses is poisoned so the English plan is benign but the action sends value to an attacker, every defense that reads the plan is blind by construction. The burden falls on whatever inspects the bytes or the signed message.

An address allowlist gates the counterparty, not the grant. A signature can hand over power that outlives that check, or route value past it.

Nothing here is a new attack. The mechanisms (blind-signing, Permit2 phishing, EIP-7702 drains, tool poisoning) are all documented. The contribution is the object those write-ups do not build: a minimum-capability stop-map, assigning each drain to the cheapest wallet capability that stops it, made runnable, with its residue named.

The capability ladder

Real wallet defenses are a stack of capabilities, not one thing. Weakest first:

  1. Plan review (English only) — reads the stated plan, never the bytes.
  2. Address allowlist — decode the action, veto an un-allowlisted counterparty.
  3. Amount-aware clear-signing — also veto unlimited amounts.
  4. Recipient rendering — also render the ultimate recipient (an order's taker), as ERC-7730 does.
  5. Transaction simulation — fork-run the tx, veto on adverse state diff.
  6. Signature simulation — reason about the net transfer a signature enables.
  7. Transaction-type policy — categorical warnings on dangerous action types.

Each drain is scored by the lowest rung that would stop it.

The coverage map

Eight drains, on-chain and off-chain, to a stranger or to an allowlisted target. A dot marks a capability that stops the drain on its own; the last column is the lowest one that does.

drainL1 planL2 allowL3 amtL4 recipL5 txsimL6 sigsimL7 typestopped at
A — unlimited approve to a stranger····L2 allowlist
B — transfer to a stranger as payment·····L2 allowlist
C — EIP-2612 permit to a stranger····L2 allowlist
D — unlimited permit to the allowed router·····L3 amount-aware
E — call to the allowed router, armed after the check·······RESIDUAL
F — EIP-712 order to the allowed exchange, recipient = attacker·····L4 recipient
G — EIP-7702 delegation to the allowed helper······L7 type†
H — max approve to the allowed Permit2······L7 type†

† L7 is a blunt categorical warning, not a targeted stop; for a Permit2 approval it is impractical (it fires on every legitimate setup) and for a delegation it is a click-through prompt.

What it says, honestly:

A field-complete clear-signing stack closes five of eight. The genuinely hard residue is one on-chain time-of-check/time-of-use race (E) and two categorically dangerous authorization types, and the survivors also require the allowlisted target itself to be adversarial.

Quantifying the one that survives

E's survival is conditional on the attacker winning an ordering race. That condition is where the agent stops being motivation and becomes the variable. Under the defense E demands (re-simulation at inclusion), the attacker must land its arming transaction immediately before the victim's, which it wins with per-attempt probability β. An agent signs many actions K, and each can be targeted, so the probability of at least one drain compounds:

P_drain(β, K) = 1 − (1 − β)^K       (Monte-Carlo confirmed)
β (per attempt)K = 10K = 50K = 100
0.05  weak public-mempool0.400.920.99
0.20  modest searcher0.891.001.00

Re-simulation at inclusion, the only capability that closes E for a human who signs once, does not close it for an agent that signs at volume: even a weak attacker drains an active agent with near-certainty. The surviving drain's success rate is a function of how much the agent signs.

One measured dimension

The ladder is a set of capability models. One dimension is wired to a real deployed service: address reputation, via the GoPlus Security API. Across all eight drains it flags nothing, because every sink is a fresh address with no history. A positive control (a real address GoPlus flags stealing_attack / sanctioned) confirms the query works, so the zero is a finding, not a broken call. It confirms, live and in the agent setting, the documented fact that drainers rotate to fresh addresses. Reputation is orthogonal to the clear-signing ladder, and defeated by address rotation.

What this is, and is not

It is a runnable coverage map: eight drain shapes, the minimum capability that stops each, the residue that a field-complete simulating stack does not close, and one live-measured dimension. It is not a benchmark of any product, not a claim that off-chain signatures defeat clear-signing in general, and not a new attack. The un-defended drain is the same for every row by construction and is not data. The contracts are minimal models on a local fork; on-chain finality is real, economic loss is not. It is a mechanism demonstration.

Run it

Requires Foundry and python3. No pip/npm to run the core.

git clone https://github.com/amarshat/quantum-commit-authorization
cd quantum-commit-authorization/agent-calldata-demo
./run.sh

It boots a local chain, deploys a mock token, runs the eight drains past the seven-capability ladder, and prints the coverage matrix. Optional keys enable the live plan-review and GoPlus lenses.