exact scheme over ERC-3009.
x402 on GIWA is a port, not a builtin: GIWA does not officially support x402, and Coinbase’s hosted facilitator does not support GIWA, so Tokscale runs a self-hosted, digest-pinned x402-rs facilitator (ghcr.io/x402-rs/x402-facilitator@sha256:89ab5c03…) and deployed the chain prerequisites itself.
Direct ERC-3009 on tUSD: no proxy
The payment asset is tUSD (“Tokscale Test USD”, ERC-20 with 6 decimals and an EIP-3009 subset), deployed on GIWA Sepolia at0x58d5608e89b5c1c3b96481a199756b1a292061a9 (byte-verified, domain and metadata validated).
Because the asset implements ERC-3009, the x402 exact flow needs no proxy contract. The client signs an ERC-3009 authorization for the eip3009 asset, and the facilitator calls transferWithAuthorization directly on the token. The ExactPermit2Proxy exists only on the separate Permit2 branch and plays no role here (see x402 Stage B).
The flow
1
Return the 402 challenge
The top-up route responds
402 with scheme=exact, network=eip155:91342, asset=tUSD, payTo=treasury, and the exact requested amount.2
Sign the ERC-3009 authorization
The payer signs one
transferWithAuthorization payload covering token, payer, treasury, amount, validity window, and nonce.3
Verify with the facilitator
The gateway posts to the facilitator’s
/verify. The facilitator exposes synchronous /verify and /settle only; every state transition is driven by Tokscale’s own lifecycle and reconciler.4
Persist the intent, then settle
Only after a successful verify does the gateway persist the intent and call
/settle. A /settle timeout leaves the intent dispatched. It is never a silent failure and never an immediate retry that could obscure the on-chain outcome.5
Credit from chain evidence only
Credit is written only by the reconciler, and only from canonical chain evidence.
The durable intent lifecycle
Every top-up intent moves through a persisted state machine:- Intents are unique on
(chain_id, asset, payer, authorization_nonce), with a stored hash of the full authorization payload. One authorization can fund one intent, once. - The credit insert and the
confirmed -> creditedtransition commit in one database transaction. - An intent expires to
abandonedonly aftervalid_beforeplus the configured finality window with no matching on-chain authorization. - Flashblocks
pending-tag polling gives ~200ms “payment seen” feedback only. It is never a credit trigger: preconfirmation is not finality.
The reconciler’s exactly-once credit rule
The reconciler proves one authorization, not merely a transfer. For eachdispatched intent it requires final-chain evidence that:
- the expected token emitted
AuthorizationUsed(payer, nonce)for the stored payer and nonce, and - the successful token call decodes to the stored
transferWithAuthorizationfields: token, payer, treasury, amount, validity window, nonce, and authorization commitment.
authorizationState is a useful negative check but proves neither payee nor amount by itself.
Two consequences:
- A plain transfer cannot mint credit. A generic same-value tUSD transfer to the treasury never credits a pending intent, because it carries no
AuthorizationUsedand no matching authorization calldata. - The reconciler sends no transactions. It scans, matches, and writes credit rows. Settlement is the facilitator’s job; crediting is the reconciler’s.
Proof on public GIWA Sepolia
On 2026-07-23 the pinned facilitator ran a fullexact round trip against public GIWA Sepolia (eip155:91342): it advertised exact for the network, rejected a wrong chain (unsupported_scheme), rejected a wrong asset and a tampered signature, accepted one valid ERC-3009 authorization, and settled it on-chain. The settlement is recorded in the committed deployment manifest:
- Settlement transaction:
0xad1a2f7b…, 1.0 tUSD (1,000,000 base units), block 31,331,118 - Replay of the same authorization: rejected, reverting with the ERC-3009 authorization-used selector
0xd309466d. No double settlement.
Crash recovery, proven by killing the reconciler
The crash harness drives the worst case on the local localnet topology: an authorization settles on-chain, then the reconciler container is stopped and force-recreated before it credits. The harness then asserts, against both the database and the chain:- pre-restart: the intent is
dispatch_unknownwith zero credit rows (settled but uncredited); - post-restart: the new reconciler instance reconciles exactly one canonical settlement into exactly one credit (
scanned=1 claimed=1 confirmed=1); - a separate plain same-value tUSD transfer to the treasury (selector
0xa9059cbb, zeroAuthorizationUsedlogs) mints no credit.
exact mechanism is proven once on public GIWA Sepolia, with the evidence above. The full product top-up path (402 route, intent lifecycle, reconciler, crash recovery) is proven on the local localnet topology. Dedicated RPC, treasury custody, and production operation remain external gates.
Next steps
- For the per-request scheme that settles actual usage instead of a fixed amount, see x402 Stage B: Per-Request Settlement.
- For the ported facilitator stack both stages run on, see x402 Payments on GIWA.
- For the tUSD deployment record and the settlement transaction, see The Public Chain.
- For which of these gates are still open, see Live Evidence.