> ## Documentation Index
> Fetch the complete documentation index at: https://docs.giwa.tokscale.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# x402 Stage B: Per-Request Settlement

> Learn how one signature binds to exactly one request, and the three independent layers that prevent a double settlement.

In Stage B each model request authorizes a maximum and settles only the actual measured usage, via the x402 `upto` scheme.

## The scheme: authorize max, settle actual

A stock x402 request carries no payment on its first attempt. The gateway answers `402` with a body-aware maximum quote: the request is parsed into an input-token upper bound and an output-token cap, priced against the sealed catalog price, and the resulting `maximum_amount` is the single ceiling the authorization binds. The payer signs a Permit2 `PermitWitnessTransferFrom` for that maximum. After the streamed response completes, the gateway settles the actual priced usage, never more than the maximum. `actual <= maximum` is enforced three times: in the gateway client, in the adapter's word-by-word calldata decode, and on-chain by the Permit2 authorization itself.

The witness `{to, facilitator, validAfter}` is what makes maximum-authorize safe: the facilitator can settle any amount up to `permitted.amount`, to the treasury in the witness and nowhere else.

## Why Permit2-only, and why three contracts went to Sepolia

In current x402-rs, `upto` is implemented with Permit2 and a hardcoded `UptoPermit2Proxy`. ERC-3009 cannot substitute: its signed value is fixed at signing time, so it serves the fixed-price `exact` flow ([Stage A](/gateway/x402-stage-a)) and nothing more. Stage B therefore requires the canonical Permit2 contracts to exist on the chain:

| Contract                       | Address on GIWA Sepolia                                                                                                             | Why it was deployed                                                                                                                                                                                                                                                                                                                                                                   |
| ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| UptoPermit2Proxy               | [`0x4020A4f3b7b90ccA423B9fabCc0CE57C6C240002`](https://sepolia-explorer.giwa.io/address/0x4020A4f3b7b90ccA423B9fabCc0CE57C6C240002) | The settlement target x402-rs hardcodes. Source-locked build reproduced the pinned hashes; `cast compute-address` reproduced the canonical address; deployed runtime hash and `PERMIT2()` getter read back and matched (tx [`0xea7708f6…`](https://sepolia-explorer.giwa.io/tx/0xea7708f6a0ac3f2f872b1ea6be99f8d010064d76e5e0e390bc49669a17b66920), block 31,395,852). Byte-verified. |
| ExactPermit2Proxy              | [`0x402085c248EeA27D92E8b30b2C58ed07f9E20001`](https://sepolia-explorer.giwa.io/address/0x402085c248EeA27D92E8b30b2C58ed07f9E20001) | Required by the facilitator's startup check (not by the Stage A ERC-3009 path). The pinned facilitator refused to start without it.                                                                                                                                                                                                                                                   |
| ERC-6492 UniversalSigValidator | [`0xdAcD51A54883eb67D95FAEb2BBfdC4a9a6BD2a3B`](https://sepolia-explorer.giwa.io/address/0xdAcD51A54883eb67D95FAEb2BBfdC4a9a6BD2a3B) | Also required by the facilitator startup check, so counterfactual and contract-wallet signatures can be validated.                                                                                                                                                                                                                                                                    |

Permit2 itself is an OP Stack preinstall at [`0x000000000022D473030F116dDEE9F6B43aC78BA3`](https://sepolia-explorer.giwa.io/address/0x000000000022D473030F116dDEE9F6B43aC78BA3); its observed runtime hash equals the repository's source-locked build, closing the canonical-source comparison gate.

<Info>
  Provenance honesty: both facilitator prerequisites reproduce their canonical CREATE2 addresses exactly, so the deployed code is cryptographically bound to its address. But the Exact proxy's init code comes from x402-foundation's frozen artifact (a fresh `forge` build embeds different CBOR metadata), and the ERC-6492 validator is verified by Sourcify match rather than a git-revision pin. Neither rebuilds as cleanly from source as the Upto proxy does.
</Info>

## The request binding

Every immutable request and chain fact is captured once in an `X402RequestBinding` (project, API key, protocol, route, request ID, fingerprint, maximum, expiry, network, asset, treasury, facilitator, Upto proxy, and transfer-method metadata). The binding produces:

* a `binding_digest`: length-prefixed SHA-256 over every field, under the domain `GIWA/x402/request-binding/v1`;
* the signed Permit2 nonce: `derive_upto_authorization_nonce(binding_digest)`, under `GIWA/x402/upto-nonce/v2`.

Because Permit2 signs that nonce, a valid signature cannot be rewrapped for a different project, API key, route, request, price maximum, expiry, or chain configuration. Quote expiry is issued once at challenge time and carried back unchanged inside the signed header; a stale or freshly extended expiry is rejected with a new `402`, never silently repriced.

## The settlement journal: BoundV2 canonical, LegacyV1 frozen

The isolated x402 adapter, which links no x402 crate into the gateway, owns the sole settlement side effect, fenced by an append-only journal:

1. Before broadcasting, `settle` writes a **prepared** journal record keyed by the payment's `JournalIdentity`, recording a `scan_next_block` cursor read before the side effect.
2. If a prepared record already exists without a transaction hash, the adapter refuses to broadcast again and returns `FacilitatorUnavailable`. Recovery is read-only.
3. Recovery scans blocks from the cursor for a settlement transaction from the facilitator to the Upto proxy whose decoded calldata matches the authorization. More than one match is a hard error.

Double-settle prevention is three independent layers: the Permit2 nonce bitmap on-chain, the journal fence in the adapter, and the gateway's idempotent accept plus lease- and hash-fenced finalize. The adapter journal speaks two formats: **BoundV2** (binding-keyed) is canonical for all new requests; **LegacyV1** (`GIWA/x402/upto-nonce/v1`, flat request shape) is frozen and exists only so durable rows and journals written before the binding existed can still recover. New execution never falls back to it.

## Response-loss recovery

The fault harness injects a lost settle response between the adapter and the facilitator on the local localnet topology, then proves every journey reaches exactly one terminal settled state with a recovery proof digest:

* **pre-deadline**: the response is lost inside the authorization window; one `/recover` re-read finalizes the payment;
* **post-deadline**: the response is lost across the recovery delay; the journal scan path finds the mined settlement;
* **native\_v2**: the full native gateway flow under the same fault, including database evidence that exactly one admission, payment, ledger row, and receipt snapshot exist.

```sh theme={null}
bun run local:x402:fault:e2e
# pass marker: GIWA_LOCAL_X402_FAULT_E2E_OK
```

A client disconnect mid-stream is handled separately: settlement commits in a detached background task, the success suffix is emitted only after commit, and the terminal receipt stays retrievable at `GET /v1/receipts/:request_id` regardless of the stream's fate.

## Next steps

* For the fixed-amount scheme that funds prepaid credit, see [x402 Stage A: Credit Top-Up](/gateway/x402-stage-a).
* For where the body-aware maximum quote comes from, see [The Gateway and Its Workers](/gateway/service).
* For the isolated adapter that owns the settlement side effect, see [The Gateway and Its Workers](/gateway/service).
* For what is proven on the public chain versus locally, see [Live Evidence](/overview/live-evidence).
