verify[402]
live · block — Run free check

Failure modes · 9 min read · Oct 25, 2026

Three payment intents your agent should never sign unchecked.

An unlimited approval, an x402 offer that does not match the resource, and a swap with no floor. In each case the signature is valid; the intent is not.

The most expensive agent payment is not the one that fails. It is the one that succeeds exactly as signed, on an intent nobody should have approved.

These three shapes are common because they are convenient. Each one is a valid transaction. Each one can be built by a competent model. Each one has a check that belongs before the signature, not after the loss.

They are examples, not a complete list. The point is the pattern: decode the intent, compare it to a policy, and make the decision explicit.

1. The unlimited approval

An ERC-20 approval grants a spender permission to move your tokens. An unlimited approval grants that permission up to the maximum integer — forever, or until it is revoked. If the spender is a fresh contract, a compromised router, or a typo of a real address, the approval is the whole balance.

The signature prompt often says "approve." It rarely says "this contract can move everything, later, without another signature." That is the gap.

POST /preflight
{
  "kind": "transaction",
  "chainId": 8453,
  "to": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
  "data": "0x095ea7b30000000000000000000000001111111111111111111111111111111111111111ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
  "value": "0",
  "policy": { "forbidUnlimitedApproval": true }
}

What to check: the selector is approve; the spender is on an allowlist or is the expected router; the allowance is not the maximum; the token contract is the one you think it is; and when a from address is supplied, the simulation does not revert.

What a gate can return: BLOCK with unlimited_approval. What it cannot tell you: whether the spender is honest six months from now. An allowlist is a decision you make; the gate enforces it.

The practical rule

Approve the exact amount for the exact router, or use a permit with a deadline and a signature you can revoke. If the workflow truly needs an unlimited approval, make it an explicit, logged decision — not a default in a helper function.

2. The x402 offer that does not match the resource

An x402 payment is a small JSON object: scheme, network, asset, amount, payee, resource, timeout. It looks harmless. It is also the easiest place to swap one field and turn a 0.01 USDC call into a 10 USDC payment to a different address.

The failure is not "the payment failed." The failure is that the agent paid the wrong payee, on the wrong chain, for a resource it did not bind — and the facilitator settled it correctly.

POST /preflight
{
  "kind": "payment",
  "payment": {
    "scheme": "exact",
    "network": "eip155:84532",
    "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
    "amount": "10000",
    "payTo": "0xAeEF086DD4e436c69d6d0F684b4c9F43392df9cb",
    "resource": "https://verify402.onrender.com/verify"
  },
  "policy": {
    "maxAmountUsd": 0.25,
    "allowedResourceHosts": ["verify402.onrender.com"]
  }
}

What to check: the scheme is the one you expect; the network is the one you expect; the asset is the canonical USDC contract for that network, not a lookalike; the amount in raw units is under the cap; the payee is the seller you intended; the resource host is bound by policy; the timeout is not already in the past.

What a gate can return: PASS for the intent above, and BLOCK when the payee, asset, amount, or resource host is swapped. What it cannot tell you: whether the seller will deliver the resource, or whether the payTo address is controlled by the seller. Those stay in notChecked unless you supply a payout manifest.

Why host binding matters

A resource URL is part of the offer. If the policy binds a host, a payment for a resource on another host is a different offer. That is not a formatting error; it is a different purchase.

3. The swap with no floor

A swap can be valid and still be a donation. If amountOutMinimum is zero and the deadline is open, the transaction can execute at almost any price. The agent sees "swap succeeded." The wallet sees the result.

This is not a prediction problem. It is a parameter problem: the intent does not contain a floor, so the chain is free to fill it at the worst price available in the block.

The shape to inspect (illustrative fields; the raw calldata is what the gate decodes):

{
  "kind": "transaction",
  "chainId": 8453,
  "from": "0xYourAgentWallet",
  "to": "0xRouter",
  "data": "0x…",          // a common V2/V3 swap selector
  "value": "0",
  "policy": { "maxValueWei": "0" }
}

What to check in the decoded call: the selector is a swap you recognize; amountOutMinimum is greater than zero; the deadline is in the future; the recipient is the agent's wallet, not an arbitrary address; the input token and amount match the plan; and the simulation from the agent's address does not revert.

What a gate can return: CAUTION or BLOCK with a named reason, depending on the policy and the decoded fields. What it cannot tell you: whether the price is good. That is a measurement question, not a preflight question.

What the three have in common

Each one is a valid signature on an intent that should have been stopped or questioned. Each check is cheaper than the loss. And each one is visible before the signature — if something looks at the decoded intent instead of the model's summary of it.

That is the job of a pre-signing gate: not to predict the future, but to make the present intent explicit, bounded, and auditable.

Test these against the gate

Run the unlimited approval above and expect BLOCK. Run the x402 payment and expect PASS; then change payTo, the asset, or the resource host and run it again. If any swapped version still passes, that is the finding worth reporting.

The ask

Send the intent you are afraid of.

One you expect to pass, one you are unsure about, one you think should be blocked. I will run all three and reply with the raw verdict and reason codes.

Previous: What a pre-signing gate can check — and what it can't. · All notes