Skip to content

feat(x402): auth-capture unlock gate + fee revenue metrics#798

Open
bussyjd wants to merge 2 commits into
mainfrom
feat/authcapture-unlock
Open

feat(x402): auth-capture unlock gate + fee revenue metrics#798
bussyjd wants to merge 2 commits into
mainfrom
feat/authcapture-unlock

Conversation

@bussyjd

@bussyjd bussyjd commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Integration notes, dependencies & deferred work: #799

Inline first-message platform-fee capture for gate:auth offers, riding the x402 auth-capture scheme (ObolNetwork/x402-rs#9, escrow-enforced on-chain fee split).

What

  • First chat/agent message pays an auth-capture charge (EIP-3009 single-shot, autoCapture); the SIWX session cookie mints on settle success, and every later request rides the cookie free — one on-chain tx per session, no per-turn wallet interaction.
  • The escrow itself pays feeBps → feeRecipient at charge() within the client-signed bounds — no off-chain split.
  • New fee metrics on the verifier /metrics: obol_x402_verifier_fee_revenue_atomic_total + obol_x402_verifier_settled_volume_atomic_total (labels network/asset/fee_recipient), excluded from route-pruning.
  • Global authCaptureUnlock config block (offerPrefix, price, payTo, feeRecipient, min/maxFeeBps, captureAuthorizer). Config-gated, off by default.

Correctness notes

  • The unlock 402 advertises x402Version: 2 (auth-capture is v2-only; @x402 clients reject otherwise).
  • Settlement uses the client's signed accepted requirement verbatim — the PaymentInfo hash commits server-issued deadlines, so a rebuilt requirement drifts and breaks the signature. validateSignedUnlockRequirement pins every economic field (amount, payTo, feeRecipient, fee bounds, authorizer) against config so a client can't underpay or redirect the fee.

Proven

End-to-end on Base Sepolia and Base mainnet (live cluster): on-chain fee split (e.g. mainnet settle 0xce7ac4bd7f821017b9707a0cc6e0f4354a9c6bbeaa032eec79f290e5454b1343: 10000 → 250 fee + 9750 payTo at 250bps), cookie mint, free-ride on 2nd request (no double charge), fee counters materialized. Requires a facilitator serving v2-eip155-auth-capture (overlay image 2.0.2-auth-capture.2+; the .1 tag does not register the blueprint).

https://claude.ai/code/session_01PnhCQLz7CHuDBUhWd5xF8v

@bussyjd
bussyjd changed the base branch from integration/v0.14.0-rc0 to main July 20, 2026 14:39
@bussyjd
bussyjd marked this pull request as draft July 20, 2026 14:39
@bussyjd
bussyjd force-pushed the feat/authcapture-unlock branch from 8224493 to a95d2ca Compare July 20, 2026 14:42
Inline first-message fee capture for gate:auth offers: the SIWX session is
minted by settling an x402 auth-capture charge (EIP-3009 single-shot,
autoCapture) instead of a plain signature; subsequent requests ride the
session cookie free. The escrow enforces the client-signed fee split
(feeBps -> feeRecipient) on-chain at charge() time.

- internal/x402/authcapture.go: auth-capture requirement builder + signed
  payload validation (validateSignedUnlockRequirement pins every economic
  field of the client-signed requirement against config)
- internal/x402/unlockgate.go: unlock flow — 402 (x402Version 2) ->
  verify+settle against the signed payload -> mint SIWX cookie
- internal/x402/metrics.go: obol_x402_verifier_fee_revenue_atomic_total +
  settled_volume_atomic_total (network/asset/fee_recipient), excluded from
  route-pruning
- config: global authCaptureUnlock block (offerPrefix, price, payTo,
  feeRecipient, min/maxFeeBps, captureAuthorizer)

Config-gated, off by default. Settlement must use the client's signed
'accepted' requirement verbatim (PaymentInfo hash commits server-issued
deadlines; rebuilding drifts them and breaks the signature).

Proven end-to-end on Base Sepolia and Base mainnet (on-chain fee split,
cookie mint, free-ride, metrics materialization).

Claude-Session: https://claude.ai/code/session_01PnhCQLz7CHuDBUhWd5xF8v
handlePaidUnlock discarded the settle response when facilitatorSettle
errored, losing the tx hash the facilitator returns when it submits the
settle tx on-chain and then fails on the receipt path. A charged buyer got
a bare settle_failed with no cookie, no answer, and no way to reconcile the
on-chain debit.

Mirror the per-request paid path (HandleProxy): surface settleResp.Transaction
via X-PAYMENT-RESPONSE + a 'you may pay twice' hint, and log it, on both the
transport-error and !Success branches. A failed settle still mints no session.

Claude-Session: https://claude.ai/code/session_01PnhCQLz7CHuDBUhWd5xF8v
@bussyjd
bussyjd marked this pull request as ready for review July 20, 2026 18:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant