Skip to main content

Invariant

U2

Category

Universal

Priority

Must-have

Violation class

Free Shopping

What it proves

Real clients retry. A request can time out, drop, or return an ambiguous result, and a naive client resends the exact same signed authorization. A facilitator that mishandles this can end up double-settling — moving funds twice for one client intention. Source: x402 protocol spec — retrying an unresolved settlement must not double-settle; USENIX Security ‘26 study — Free Shopping via naive retry handling.

How it works

Ward builds one signed payload and calls /settle on it three times in a row, unmodified — exactly what a client blindly retrying after a perceived timeout would send.

Why “exactly one success” isn’t the assertion

Algorand’s own txID deduplication makes replaying byte-identical signed transaction bytes to algod safe by construction. A facilitator that echoes success: true with the same transaction id on every retry hasn’t caused any economic harm — it’s just not being maximally strict about idempotency bookkeeping. See D12 for the full reasoning. The sharp, chain-grounded property U2 actually checks: repeated retries of one signed authorization must never resolve to more than one distinct on-chain transaction id.

Pass condition

Three outcomes are all valid PASSes:
  1. All three retries converge on the same single transaction id.
  2. All three retries fail, with a consistent error reason (e.g. the account genuinely lacks funds in this environment) — not evidence of nondeterministic double-processing.
A FAIL is specifically: retries produced more than one distinct on-chain transaction for what was, from the client’s perspective, one authorization.

Next: Concurrent Double-Settle