Skip to content

pull in latest upstream - #2

Open
TtheBC01 wants to merge 1262 commits into
1Shot-API:mainfrom
x402-foundation:main
Open

TtheBC01 wants to merge 1262 commits into
1Shot-API:mainfrom
x402-foundation:main

Conversation

@TtheBC01

Copy link
Copy Markdown

Description

Tests

Checklist

  • I have formatted and linted my code
  • All new and existing tests pass
  • My commits are signed (required for merge) -- you may need to rebase if you initially pushed unsigned commits
  • I added a changelog fragment for user-facing changes (docs-only changes can skip)

phdargen and others added 16 commits September 22, 2026 20:22
…3126)

* specs(exact): correct Starknet settlement and rescue rules (follow-up to #2849)

Follow-up to #2849, aligning the merged spec with the reference
implementation on the defects found by audit.

Settlement's exactly-one-Transfer criterion is scoped to the payer, so an
honest settlement is not denied when the asset is the STRK fee token (a
real receipt then carries the executor's fee Transfer from the same
contract).

The consumed-nonce rescue was forgeable: a settled SNIP-9 payload is
public, so a facilitator implementing step 5 as written would hand a
fresh success to anyone replaying one off the chain. The rescue now
requires an attempt this facilitator recorded (retained past the guard's
horizon), re-established payload authenticity, an onchain
nonce-consumption precheck, accepted-block finality, and a structural
match identifying the authorization by callee, caller, and nonce - via a
calldata parse or, more robustly, the execution trace, which works across
the direct, forwarder, and Cairo-0 topologies. Crediting one transaction
to at most one authorization per payer is a MUST, with the binding
retained for as long as any rescue could locate the transaction, and the
duplicate guard re-checked immediately before crediting. When the rescue
credits nothing it reports its trigger's own outcome, never an unobserved
nonce_already_used.

Also: domain error codes are scoped to structured contract-level
failures, with transport faults - including a simulation the node could
not complete - failing closed as retryable unexpected_verify_error;
settlement_pending carries an empty transaction when the broadcast itself
timed out; the search window is sized against block time with legacy
unkeyed scans spending their bounded budget newest-first; chainId always
reduces to the felt; and the payment-flow vocabulary from #3053 is
declared. Folds in block-tag pinning, chainId wire forms, an
unsatisfiable-maxTimeoutSeconds rejection, and mandatory pre-hash size
bounds.

* specs(exact): align Starknet settlement_pending with the protocol-wide definition

x402 v2 §9 (#3083) now defines settlement_pending for every scheme and
requires the response to carry the broadcast transaction hash. The Starknet
row and settlement step 4 reference that definition, and the hashless
broadcast-timeout case - which has nothing to reconcile against - returns
unexpected_settle_error while the duplicate guard and attempt record keep
doing the protecting.

* specs(exact): reconcile the Starknet overview block and settle on onchain

The exact overview's Starknet block forbade an executor equal to the
recipient while scheme_exact_starknet.md allows feePayer == payTo for
merchant-sponsored settlement (the SVM precedent); the overview now says
what the per-network document and the implementation do. The standard
codes list names settlement_pending now that x402 v2 §9 defines it, and
the document uses the repository spelling onchain throughout.

* specs(exact): state the block-number gate in settlement step 3

Step 3 now says what the JSON-RPC paragraph and step 5 already relied on: a
receipt counts only once it carries a block_number, for a REVERTED status as
much as for SUCCEEDED. Step 5's nonce_already_used sentence names rule 6 as
the source instead of over-stating it, and the overview block's last
on-chain becomes onchain.

* specs(exact): make a consumed Starknet nonce terminal and reconcile settlement_pending by hash

Drops the consumed-nonce rescue from step 5, together with the authenticity,
attempt-binding, structural-match and one-transaction-one-authorization rules
that only existed to bound it. A settled SNIP-9 payload is public, so a
success-on-consumed-nonce path lets anyone holding the payload obtain a second
success: true for one onchain payment.

settlement_pending stays the one non-terminal outcome: the resource server's
retry with the same payload waits on the broadcast hash instead of
broadcasting again. nonce_already_used and duplicate_settlement are terminal.

Also states the asset transfer method's family instead of referring to SDK
internals, restores the merged signature-bound SHOULD in place of
implementation-specific input limits, and adds a note that Selector enters
the hash as the felt, from a second implementer's interop report on #3021.
* spec(exact-hedera): add transferExecutor asset transfer method

* spec(exact-hedera): address review feedback

* spec(exact-hedera): tighten wording

* spec(exact-hedera): trim the Hedera entry in Critical Validation Requirements

* spec(exact-hedera): publish admitted executors in the quote

Signed-off-by: Piotr Kierzniewski <piotr.kierzniewski@blockydevs.com>

---------

Signed-off-by: Piotr Kierzniewski <piotr.kierzniewski@blockydevs.com>
* fix: protected endpoint price & ERC6492 allowlist

* feat: updated tests

* fix: revert default protected to avoid regression

* fix: format
* fix(python): cap solana <0.40 in svm extra

solana 0.40.0 removed the synchronous solana.rpc.api module that the SVM mechanism imports, so a fresh install of x402[svm] could not import x402.mechanisms.svm. Add an upper bound to the svm extra and the dev group; uv.lock only changes the two specifiers.

Fixes #3552

* chore(python): add changelog fragment
* build(svm): generate payment channels client

Co-authored-by: Ludo Galabru <ludo.galabru@solana.org>
Signed-off-by: Ludo Galabru <ludo.galabru@solana.org>

* refactor(svm): share payment channel lifecycle

Co-authored-by: Ludo Galabru <ludo.galabru@solana.org>
Signed-off-by: Ludo Galabru <ludo.galabru@solana.org>

* feat(svm): add batch settlement scheme

Co-authored-by: Ludo Galabru <ludo.galabru@solana.org>
Signed-off-by: Ludo Galabru <ludo.galabru@solana.org>

* fix(svm): preserve kit 5 codegen compatibility

Co-authored-by: Ludo Galabru <ludo.galabru@solana.org>
Signed-off-by: Ludo Galabru <ludo.galabru@solana.org>

* feat(svm): align batch settlement with authorization flow

Follow-ups from the spec review (#2698):

- resolve batch-settlement to the protocol-default authorization
  payment flow; the deposit open/top_up now broadcasts in the single
  post-handler settle and its voucher commits after confirmation
- stop emitting extra.paymentFlow; accept only absent or
  "authorization" on the wire
- quantify the voucher-expiry margin: advertise a non-default
  extra.settlementBufferSeconds (default 60) and enforce expiry
  against the advertised value instead of private config

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(svm): require non-expiring batch vouchers

Per spec follow-up: voucher expiry added a second clock on top of the
forced-close grace period and could make an accepted voucher
unredeemable while the channel was still open. Clients now always sign
expiresAt = 0, the server and facilitator reject nonzero expiries with
invalid_batch_settlement_svm_voucher_expiry, and the advertised
extra.settlementBufferSeconds is removed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(svm): confirm batch allocations and support topups

* fix(svm): satisfy batch client lint

* fix(svm): recover pending batch allocations

* fix(svm): discard failed recovered batch opens

* fix(svm/batch): record a top-up's escrow in stored channel state

`ChannelState.deposit` was written once, when the channel is provisioned,
and no store update ever changed it. A top-up's escrow therefore never
reached stored state, so `snapshot()` kept reporting the original `open`
amount as `channelState.balance`.

Steady-state vouchers are answered by the server itself (`beforeSettle`
short-circuits to `acceptedResponse`), so that stale balance is what the
client receives — and the client adopts it as its own ceiling, overwriting
the correct optimistic value it had just computed. The next request that
exceeded the original deposit topped up again, and so did every request
after it: the escrow grew on chain while the client believed it never had.
It failed silently, as an ever-growing escrow rather than an error.

The facilitator already reports the freshly-fetched on-chain deposit in
`channelState.balance`, so the data was there and simply discarded.
`readChannelState` now carries it and the deposit settle path adopts it,
taking it as a maximum so a stale or malformed read can never lower a
ceiling the chain has already confirmed.

The regression test fails without the fix with `expected 10000n to be
25000n` — the stale open amount in place of the confirmed escrow.

Found while porting this scheme to Rust, where the mirror of this bug
(double-counting the same top-up on retry) was reported by Greptile on
solana-foundation/pay-kit#301.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(svm/batch): report the real charged amount on a replay

The settle response for a replayed authorization reported
`chargedAmount: "0"`. A replay answers a request that was already
charged the fixed price, so zero tells the client it paid nothing for a
request it did pay for. Report `PaymentRequirements.amount`, which is
what the original response carried and what §4.4 requires of the field.

That was the only use of the `committed` flag, so it goes away with it.

* added examples and few minor fixes

* feat(svm/batch): recover unknown channels and emit the corrective 402

A voucher for a channel this server holds no record of was refused twice
over — in `beforeVerify` and again in `provisionalState` — so a server
that lost its store turned every request on a live, funded channel into
a dead end, leaving the payer's escrow to a forced close.

The facilitator already verifies a voucher against confirmed onchain
state and returns the snapshot. Persist it: the record is rebuilt when
missing and refreshed when present, so `deposit`, `settled` and the
close state stop drifting behind the chain. A rebuilt record charges
from the onchain settled watermark, which is both the most the server
can honestly claim and the only base the program will accept — starting
from zero would take vouchers `settle` can never apply, since it
requires a strictly increasing watermark.

The cumulative rule cannot run before the record exists, so for an
unknown channel it moves after the snapshot lands and reports a mismatch
the corrective 402 then carries. That 402 is new here: `voucherState`
was typed as a whole `BatchVoucher`, was never emitted, and never
consumed. It now has the spec's shape and travels with the snapshot, so
the client can check the base against a signature only its own
authorizer key could have produced — and when the server holds no
voucher the proof is omitted and the client resynchronizes from chain.

* test(svm/batch): cover channel recovery and the corrective 402

Nine cases across both sides: an empty store reaching the facilitator,
the settled watermark as the rebuilt baseline, a closing channel refused,
the corrective 402's snapshot and proof (and its absence when the server
holds no voucher), and on the client, adoption only against its own
signature — a foreign one, a base above the proof, and an unproven base
are all refused.

Also covers the trust boundary: a charge above the advertised price is
refused, a cumulative the client cannot derive leaves local state alone,
and the escrow comes from the deposit the client signed rather than the
balance the server reports.

Writing them turned up two fixes in the hook itself. It keyed the
channel off the request context, which does not exist yet when the
mismatch is caught before verification, so it now derives the channel
from the payload — and only from one that validates, signature included,
since otherwise naming a channel would be enough to read its state.
`applySnapshot` also took two arguments it never read.

* feat(svm/batch): reuse a wallet's channel and drive refunds

The client salted every open with a random u64, so a restart opened a
second channel and left the first one's escrow to a forced close. Salt
now defaults to 0, and before funding anything the client looks for a
channel this wallet already opened: a getProgramAccounts scan filtered on
payer, narrowed to the terms in hand. Every scanned row has its PDA
rederived from its own fields before it is trusted; a filter result never
is. Discovery is an optimization over opening a new channel, never a
precondition for paying, so a failed scan falls through to the open.

The adopted base is the onchain settled watermark, which is where a
server rebuilding its own record starts too, so the two agree.

SVM could also build a payer-signed close but nothing drove it, where EVM
ships refundChannel. refund(url) probes the route for the terms the
channel was opened against, sends the close, and returns the settlement.
It takes no amount — the program has no partial close — and has nothing
to retry against a corrective 402, since a close carries no cumulative to
resynchronize. It falls back to discovery too: a client with no local
record is exactly the one that needs to close a channel it can no longer
pay from.

* fix(svm/batch): simulate redemption batches explicitly

Claim and distribute went out through the rpc-based submitSettle, which
left simulation to the node's preflight. A preflight rejection arrives as
an opaque send error, and a batch packs several channels into one
transaction, so a caller could not tell a poisoned batch from a flaky
node — and would retry the same doomed batch forever.

upto already had the shape this needs, in its own copy of submitSettle.
Rather than add a third, the shared module now carries one signer-routed
implementation and both submission paths build their transaction the same
way. Batch's claim and distribute use it: simulate explicitly, report the
failure as settlement_simulation, then let the signer broadcast with
preflight skipped so the node does not simulate the same bytes twice.

* test(svm/batch): drive the whole channel lifecycle onchain

A local Surfnet forked from devnet serves the deployed payment-channels
program and the devnet USDC mint, and its cheatcodes fund accounts, so
the whole flow runs against a real chain with no pre-provisioned keys.

Seven stages, sequenced as a channel's life: open and serve, a
steady-state voucher with no transaction, a rediscovered client
resynchronizing through a corrective 402 it verifies against its own
signature, a server that lost its store rebuilding from chain, claim and
distribute with the receiver's balance actually rising, a top-up when the
escrow is spent, and the payer-forced close.

Skips with an actionable message when no such RPC is reachable, so CI
without a validator stays green.

* fix(svm/batch): reconcile a broadcast instead of repeating it

Every batch broadcast — deposit, refund, claim, distribute — went out
behind an in-memory duplicate cache and confirmed inline. A confirmation
wait that ends without an answer says nothing about the transaction: it
may still land. The cache freed the lock on the way out, so a retry
rebroadcast, and a restart lost the record entirely — the case the
in-memory cache can never cover.

The signature is now persisted through a PendingSettlementStore before
the confirmation is awaited, so the next attempt reconciles against it
rather than escrowing or redeeming a second time. A definite onchain
failure drops the record and reports a failure; anything else stays
pending, which is the conservative reading. The store defaults to memory
as upto's does, and takes a durable implementation for deployments that
need one.

Top-up and refund also stop signing before they simulate: sigVerify is
off, so the fee payer's signature adds nothing there, and not asking for
it keeps simulation portable across signer backends that will not sign
the same bytes twice.

* feat(svm/batch): redeem stored channels on an interval

Vouchers accumulate offchain and are worth nothing until claimed, so a
server that never redeems forfeits everything it earned the moment a
payer forces a close — the grace period is the whole window. The
facilitator could claim and distribute on request, but nothing drove it.

BatchChannelManager reads the server's own channel store, claims what
has vouchers above its settled watermark, then distributes what those
claims settled. Passes are serialized, so a slow one cannot overlap the
next and submit the same claim twice, and a batch that does not land is
left for the next pass rather than recorded — the onchain watermark is
monotonic, so repeating a claim is harmless. Four channels per
transaction, per the spec, with none dropped from a full batch.

Enumerating needs a store that can list, which is optional on the
interface so a store built only for serving requests need not implement
it.

The onchain suite drives redemption through the manager now: it claims,
distributes, the receiver's balance rises, and a second pass finds
nothing to do. Writing its unit tests turned up a bug — distribute
filtered the snapshot the pass opened with, so a channel claimed in that
same pass was never paid out.

* fix(svm/batch): hold the pending record until the outcome is known

The reconcile path dropped the pending record before confirming the
signature it had just read. A concurrent retry that read the store after
that delete found nothing and would broadcast the work a second time —
and the in-memory duplicate cache that would otherwise catch it is empty
after a restart, which is exactly when a pending record is being
reconciled. Two callers reconciling the same signature is harmless: they
confirm the same transaction and reach the same answer.

The regression test asserts the record is still readable from inside the
confirmation, which is where a racing retry would look; it fails on the
old order.

Reads now go through the facilitator signer too — channel accounts and
the mint's owning token program — so an operator configuring one RPC
does not find reads quietly answered by another. One read remains on its
own client: simulateOpenSettleDistribute is shared with upto and
simulates through an rpc it takes itself.

* fix(svm): reconcile batch settlement with latest main

* test(svm): cover batch settlement lifecycle

* fix(svm): harden batch settlement lifecycle

* fix(svm): address batch settlement review

* feat(svm): add server-signed batch vouchers

* fix(svm): recover batch transactions without new distribution request fields (#13)

* fix(svm/batch): retry channel reads a lagging RPC rejects for the slot floor

The remembered confirmation slot is passed as minContextSlot on every channel
read, but only the postcondition pollers tolerated a rejection. A
load-balanced RPC node behind that slot therefore failed ordinary deposit,
claim and verify-time reads outright. Retry the floored read with the usual
channel-read backoff before surfacing the error; reads without a floor still
fail immediately.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(svm/batch): resolve expired broadcasts and ambiguous payouts instead of holding them pending

Recovery follow-ups from the review of #13:

- A broadcast whose confirmation was never observed is now checked against
  its own blockhash: when the signer reports it invalid and a history lookup
  still finds no record of the signature, the bytes can never land, so the
  operation is reported as transaction_failed and its queue released. The
  validity check precedes the history lookup so a transaction included just
  before expiry is still found. Anything uncertain stays pending. The
  facilitator signer gains an optional isBlockhashValid capability.
- A confirmed sweep whose payout cannot be attributed because the merchant
  is also the refund or treasury beneficiary is answered with
  invalid_batch_settlement_svm_payout_attribution_ambiguous and the
  signature, and the sweep queue is released so later sweeps proceed.
- Wire records written by a broadcast that lost its reservation are dropped.
- Confirmation polling searches transaction history on the first lookup only.
- Drop the unused PreparedDistribution fields and the ignored boolean on the
  claim and refund response builders; move the recovery helpers into
  recovery.ts to stay under the file-size limit.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* test(svm/batch): cover refund, channel-manager and request-close edge paths

Brings @x402/svm branch coverage back over the 90% gate (90.52%).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* ci: rerun flaky TypeScript tests

* fix(examples): repair merged pnpm lockfile

* ci: retry cancelled TypeScript tests

* ci: limit TypeScript test concurrency

* feat(svm/batch): advertise an idle window, wire the SVM e2e route, and close the review partials

Review round of 2026-09-16 on #3164:

- Idle abandon-close policy. Batch vouchers never expire, so an Open channel
  whose payer walks away used to lock sponsored rent forever, and any
  facilitator that cleaned it up privately would race the server on a clock
  the server could not see. The facilitator now advertises
  `extra.maxIdleSecs` (default seven days, `maxIdleSecs` config, `0`
  disables) and rent cleanup abandon-closes an Open channel at its onchain
  settled watermark once it has seen no facilitator-visible lifecycle
  activity for that long. Channel records carry `lastActivityAt`, reset by
  every deposit, claim and distribution the facilitator processes; servers
  copy the window into the 402 and the spec makes the forfeiture explicit
  (Phase 4, §4.1, §4.5, §6.2, §8).
- SVM e2e harness. `/batch-settlement/svm` joins the SVM catalog for the
  TypeScript SDK; the TypeScript client, server and facilitator register the
  batch scheme, the client folds the harness salt to a u64 channel salt and
  drives the existing initial / recovery-refund / full phases, and the
  facilitator's EVM-only deposit read-back is now guarded by network. The
  phase env gains family-neutral names alongside the EVM_ aliases.
- Dual RPC. The facilitator's open-deposit simulation runs through the
  signer's RPC like every other read; the redemption worker accepts an
  injected `rpc`.
- Server `createChannelManager(facilitator, requirements, options)` builds
  the redemption worker over the scheme's store; the example server wires it
  for SVM the way it already does for EVM, and the example client refunds
  both channels in a dual-network run.
- The facilitator's response builders move to `facilitator/responses.ts`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* style(examples): format the batch-settlement client and server wiring

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(svm): tighten server-signed batch vouchers

* feat(svm/batch): make server-signed channels an explicit client trust decision (#23)

* Receiver auth binding (#25)

---------

Signed-off-by: Ludo Galabru <ludo.galabru@solana.org>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Philippe d'Argent <pdargent@icloud.com>
Co-authored-by: Notorious D.E.V. <notoriousd3v@gmail.com>
* Update docs/sdk-features.md

Generated-By: mintlify-agent

Mintlify-Source: dashboard-editor

* Update docs/schemes/batch-settlement.mdx

Generated-By: mintlify-agent

Mintlify-Source: dashboard-editor

* Update docs/schemes/batch-settlement.mdx

Generated-By: mintlify-agent

Mintlify-Source: dashboard-editor

* Update docs/core-concepts/network-and-token-support.mdx

Generated-By: mintlify-agent

Mintlify-Source: dashboard-editor

---------

Co-authored-by: mintlify[bot] <109931778+mintlify[bot]@users.noreply.github.com>
The lnbtc spec is on main. The scheme pages still omitted it.
…3594)

* fix(e2e): build the TypeScript SDK before family selection

ci-select-families loads mechanisms.ts, which imports @x402/cardano.
That package exports dist/cjs/index.js, and dist/ is gitignored, so a
fresh checkout cannot resolve it until the SDK is built. The workflow
treated that crash as missing wallet secrets and skipped the suite.

Build the SDK first, and skip only when family selection exits 2.

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>

* fix(e2e): load @x402/cardano only when resolving server routes

Family selection imports the mechanisms catalog before the SDK is built.
That catalog value-imported @x402/cardano, whose exports point at
gitignored dist/. Pass masumiEscrowAddress in from the TypeScript server
catalog, which starts after the build.

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>

* fix(e2e): drop the catalog note on the cardano helper

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>

---------

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>
* fix(e2e): retry Solana devnet 429s in swig setup

Public devnet returns HTTP 429 during Swig funding when SVM_TESTNET_RPC_URL
is unset. swig-setup treated that as fatal, so the e2e job died after the
account was created.

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>

* fix(e2e): inline public-devnet 429 retry in swig-setup

Keep the HTTP 429 backoff in the setup script and drop the helper module and tests.

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>

---------

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>
Enable dollar-string pricing on Arc mainnet (chain ID 5042) and Arc Testnet
(chain ID 5042002) across the TypeScript, Go and Python SDKs, plus the paywall
faucet link for the testnet.

Arc is Circle's own L1 and uses USDC as its gas token, so USDC at the predeploy
address 0x3600000000000000000000000000000000000000 is the chain-endorsed
stablecoin. The same address serves both networks.

EIP-712 domain values were read from the token contracts and the resulting
domain separators match DOMAIN_SEPARATOR() on both chains. The token exposes
EIP-3009, so no transfer-method override is needed. Decimals stay 6, so the
generated paywall decimals map is unchanged.
* docs(go): fix relative links in the Go client guide

Co-authored-by: Claude <noreply@anthropic.com>

* docs(go): fix relative links in the Go server guide

* docs(go): fix relative links in the Go facilitator guide

* docs(examples): fix builder-code facilitator link (TypeScript)

* docs(examples): fix builder-code facilitator link (Python)

* docs(examples): fix fetch client link in the CloudFront CDK guide

* docs(examples): fix fetch client link in the CloudFront console guide

* docs(examples): fix auth-capture spec links

* docs(extensions): fix package README link in bazaar

* docs(extensions): fix package README links in builder-code

* docs(extensions): fix package README link in payment-identifier

* docs(extensions): fix package README link in sign-in-with-x

* docs(python): fix examples link in the MCP README

* docs(agents): fix CONTRIBUTING link in the contributing skill

* docs(specs): point the transport template to the v2 specification

* docs(specs): link the v1 A2A transport to the v1 specification

* docs(specs): link the v1 MCP transport to the v1 specification

---------

Co-authored-by: Claude <noreply@anthropic.com>
notorious-d-e-v and others added 30 commits November 5, 2026 05:08
…3735)

* fix(python/mcp): preserve approval and settlement recovery

* chore(python): reference MCP pull request in changelog

* fix(python/mcp): preserve submission and cancellation semantics

* fix(python/mcp): preserve verified terms during cancellation

* fix(python/mcp): keep paid results when cancellation or retries fail

- FastMCP catches cancellation failures. When the outcome is unknown it
  returns the tool's error output marked "Payment cancellation outcome
  unknown", with no payment-required offer, instead of raising. The
  async and sync wrappers do the same in cancel_failed_execution.
- A refused corrective retry raises PaymentRequiredError with the earlier
  paid result in `result`; other errors that stop the retry carry it as
  `mcp_result` in all three clients.
- README: limit the Python HTTP comparison to signing the original terms
  and say which clients rerun approval.
* Update docs/advanced-concepts/lifecycle-hooks.mdx

Generated-By: mintlify-agent

Mintlify-Source: dashboard-editor

* Update docs/advanced-concepts/lifecycle-hooks.mdx

Generated-By: mintlify-agent

Mintlify-Source: dashboard-editor

---------

Co-authored-by: mintlify[bot] <109931778+mintlify[bot]@users.noreply.github.com>
* fix(core): forward facilitator abort hook message

Before-verify and before-settle hooks can return an optional message.
Verify copies it to invalidMessage. Settle resolves with success false
and copies it to errorMessage.

Co-authored-by: phdargen <phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>

* fix(ts): drop stale facilitator abort catches

x402Facilitator.settle() returns a failed SettleResponse when a before-settle hook aborts. The TypeScript site, examples, and e2e server already send that response. Their catch blocks looked for a thrown "Settlement aborted:" prefix that settle() no longer throws.

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>

---------

Co-authored-by: phdargen <phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>
Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>
Generated-By: mintlify-agent

Mintlify-Source: dashboard-editor

Co-authored-by: mintlify[bot] <109931778+mintlify[bot]@users.noreply.github.com>
)

* test(python): cover facilitator abort hook messages

Expect V2 before-verify and before-settle aborts to raise VerifyError and
SettleError with the hook message, including a missing or empty message.
Async and sync facilitators both fail these tests until the hook result is
forwarded.

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>

* fix(python): forward facilitator abort hook messages

Before-verify and before-settle aborts pass AbortResult.message through to
VerifyError.invalid_message and SettleError.error_message. A missing message
stays None and an empty message stays empty.

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>

* fix(python): state the facilitator abort exception change

The changelog now says before-hook aborts raise VerifyError and SettleError,
and the async and sync missing-message tests use the same cases.

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>

---------

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>
#3976)

* test(next): expect no bazaar import when already registered

paymentProxyFromHTTPServer and withX402FromHTTPServer still import
@x402/extensions/bazaar after hasExtension("bazaar") returns true.
The import rejects in a bundled Next.js build and logs
"Failed to load bazaar extension". These tests fail until that
import is skipped.

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>

* fix(next): skip bazaar import when the extension is registered

The startup validation import ran even after hasExtension("bazaar")
returned true. Bundled Next.js builds externalize that import, so the
first protected request logged "Failed to load bazaar extension".
Validation now runs only while registering a missing extension.

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>

* test(next): make the bazaar helper an arrow

jsdoc/require-jsdoc applies to function declarations. An arrow keeps
the shared assertion without a docstring that repeats the parameter.

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>

* docs(next): note skipped bazaar validation in the changeset

Pre-registered Next servers no longer get the startup schema check
that ran through the second dynamic import.

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>

* test(next): require bazaar validation when the dynamic import fails

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>

* fix(next): validate bazaar routes through a static import

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>

* docs(next): keep bazaar validation in the changeset

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>

* test(next): drop the duplicate checkIfBazaarNeeded import

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>

* refactor(next): make prepareBazaar an arrow

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>

* fix(next): import bazaar route validation statically

Delete the bazaarStartup re-export and import validateBazaarRouteExtensions from @x402/extensions/bazaar in index.ts.

The tests bind that real validator at load, then make a later import() reject. A pre-registered route still warns and does not call import(). On upstream main the validator import is dynamic, so the same tests log a load failure and skip the warning.

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>

* fix(next): lazy-load bazaar with traceable import

Keep Bazaar registration and route validation in one plain dynamic import so Next can trace the required dependency.

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>

* docs(next): clarify bazaar validation changeset

Describe route validation accurately when the shared lazy import succeeds.

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>

---------

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>
* feat(python): dispatch settlement_preflight_failed cancellation

Settle failures before the facilitator is reached now cancel the verified
payment with reason settlement_preflight_failed in the async and sync
resource servers, and skip on_settle_failure hooks on those paths.
Cancellations also carry verify_result for after_verify_aborted.

Co-authored-by: phdargen <phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>

* docs(python): add changelog fragment for settlement_preflight_failed

Co-authored-by: phdargen <phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>

---------

Co-authored-by: phdargen <phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>
Generated-By: mintlify-agent

Mintlify-Source: dashboard-editor

Co-authored-by: mintlify[bot] <109931778+mintlify[bot]@users.noreply.github.com>
* fix(python): reject non-object JSON in schema helpers

detect_version, get_scheme_and_network and match_payload_to_requirements
read fields from decoded JSON, and from a V2 payload's accepted field,
without checking for an object, so arrays and scalars leaked
AttributeError instead of ValueError.
detect_payment_required_version had the same gap for response bodies.

Signed-off-by: ni <243717149+nniiovoo@users.noreply.github.com>

* fix(python): reject non-object requirements in from_bytes methods

verify_from_bytes and settle_from_bytes checked the payload through
detect_version but forwarded requirements_bytes as parsed, so an array
or scalar went to the facilitator as paymentRequirements. The Go client
already returns an error for these inputs when it decodes them. Raise
ValueError before sending.

Signed-off-by: ni <243717149+nniiovoo@users.noreply.github.com>

---------

Signed-off-by: ni <243717149+nniiovoo@users.noreply.github.com>
Co-authored-by: ni <243717149+nniiovoo@users.noreply.github.com>
…3615)

* fix: update svm spec/implementations per Phantom Lighthouse update

Integrate versioned Lighthouse instruction layouts, preflight/postflight
allowlist plumbing, and aligned Go/TypeScript/Python SVM implementations.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(svm): clarify Lighthouse guard semantics

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(evm): exclude foreign-network channels from batch claims

Shared channel stores can hold vouchers for several chains. Mixing them in one claim batch reverts collection of valid local payments.

* fix(evm): align batch channel management across networks

Persist channel networks and isolate all manager selections while preserving legacy rows. Continue independent claim batches and settle partial successes, exposing errors alongside successful results.
…ocs (#4171)

* Update docs/schemes/exact.mdx

Generated-By: mintlify-agent

Mintlify-Source: dashboard-editor

* Update docs/schemes/exact.mdx

Generated-By: mintlify-agent

Mintlify-Source: dashboard-editor

---------

Co-authored-by: mintlify[bot] <109931778+mintlify[bot]@users.noreply.github.com>
* chore(python): share bytes parsing and drop a redundant await

Verify and settle from bytes take the same version, payload, and
requirements path. FastMCP matching already awaits a coroutine result,
so the extra coroutine-function branch is unused.

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>

* chore(python): shorten comments added with the recent SDK fixes

Drop step narration and the cross-SDK receipt note. Keep the current
receipt and retry rules in two lines.

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>

---------

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>
* fix(go): keep unknown cancellation off the receipt

SettleOnCancel can return ErrCancellationOutcomeUnknown when it cannot
tell whether the authorization was released. That error is not turned
into a failed receipt, so a transport can report it on its own.

Co-authored-by: phdargen <phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>

* fix(go/mcp): report unknown payment cancellation

A failed MCP tool result now includes the Python sentence when cancel
does not produce a receipt, and it does not offer another payment. A
thrown handler with no known receipt returns that error. An upfront
deposit stays attached.

Co-authored-by: phdargen <phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>

---------

Co-authored-by: phdargen <phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>
* fix(mcp): report unknown payment cancellation

When cancel throws after metering or a failed tool result, the MCP server returns a tool error marked "Payment cancellation outcome unknown". The result keeps the tool output, omits a new payment offer, and attaches only a receipt that was already known.

Co-authored-by: phdargen <phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>

* fix(mcp): cancel when the after-execution hook throws

A throwing onAfterExecution hook is a metering failure. Cancel that payment, and report an unknown outcome when the cancel itself throws.

Co-authored-by: phdargen <phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>

---------

Co-authored-by: phdargen <phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>
* fix(go): fail closed when a gate hook returns an error

A thrown beforeVerify or beforeSettle hook now returns a fixed payer-facing error and keeps the hook error in the warning log. Abort results are unchanged.

Co-authored-by: phdargen <phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>

* fix(go): sanitize every thrown gate-hook error

A beforeSettle hook that returns a SettleError now gets the same fixed payer-facing reason as any other thrown error. Abort results still carry the hook's own reason.

Co-authored-by: phdargen <phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>

---------

Co-authored-by: phdargen <phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>
* test(go): expect unknown outcome when cancel facilitator fails

A cancel facilitator error currently comes back as a failed receipt, so MCP
never reports "Payment cancellation outcome unknown". These tests lock that
outcome, and keep a facilitator rejection and a preflight abort as receipts.

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>

* fix(go): report unknown outcome when cancel facilitator fails

settleOnCancelAfterHooks turned a failed cancel facilitator call into a
failed receipt. No scheme returns ErrCancellationOutcomeUnknown, so MCP
never reported that the authorization may still be open. A facilitator
error on the cancel phase now returns that error. A success:false
response and a preflight abort stay failed receipts.

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>

---------

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>
* chore(ts): drop duplicated batch-settlement checks

Collapse one-caller settle-field and provisional-channel helpers, reuse the existing channel-state and JSON readers, and skip the deposit config check that verify already performed.

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>

* chore(ts): drop restated batch-settlement comments

Remove a comment on the wrong branch, unattached duplicate JSDoc, and notes that name other SDKs. Keep the lock-TTL and fail-closed notes.

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>

* test(ts): lock network on the mismatch channel snapshot

The inlined after-verify snapshot must copy requirements.network.
Channel.network is optional, so dropping the field still typechecks.

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>

---------

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>
…#4155)

* specs: return the transaction hash of a reverted Casper exact settlement

A Casper transaction that fails on chain was still submitted and paid
for, so the facilitator returns its hash in `transaction`, as core spec
§5.3 says ("empty string if no transaction was broadcast") and as the
EVM SDKs do for a reverted receipt. `transaction` stays empty only when
nothing was submitted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(casper): return the transaction hash of a failed exact settlement

Once the settlement transaction is submitted, a failure while waiting
for its result (an on-chain revert or a timeout) now reports its hash
in `transaction`, as the exact Casper spec requires. Failures before
submission still return an empty `transaction`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* test(python): cover throwing resource-server gate hooks

Lock the payer-facing outcome when a before-verify or before-settle hook
throws, including HTTP 402 bodies for async and sync servers.

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>

* fix(python): use fixed errors when a gate hook throws

A throwing before-verify hook returns before_verify_hook_error, and a
throwing before-settle hook raises SettleError with before_settle_hook_error.
The exception text stays in the warning log.

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>

* fix(python): type the sync server's async-hook rejection

A TypeError subclass replaces the message-prefix check, so a hook that
raises TypeError still fails closed.

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>

---------

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>
* Update docs/advanced-concepts/lifecycle-hooks.mdx

Generated-By: mintlify-agent

Mintlify-Source: dashboard-editor

* Update docs/advanced-concepts/lifecycle-hooks.mdx

Generated-By: mintlify-agent

Mintlify-Source: dashboard-editor

---------

Co-authored-by: mintlify[bot] <109931778+mintlify[bot]@users.noreply.github.com>
* fix(go): scope batch channel manager to configured network

BatchSettlementChannelManager claim, refund, and selection now ignore
channels bound to another network when sharing a store; server hooks
stamp ChannelSession.Network from request requirements. Claim continues
past failed batches and ClaimAndSettle settles partial successes.

Co-authored-by: Cursor <cursoragent@cursor.com>

* pr feedback: filter claim channels by network before SelectClaimChannels

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
… transaction as SettleError (#4175)

Co-authored-by: Cursor <cursoragent@cursor.com>
…work (#4186)

* fix(python): scope batch-settlement channel manager to configured network

Python channel managers listed shared storage without a network filter, so a
manager on one EVM network could claim or refund channels of another. Port the
Go and TypeScript alignment:

- Channel gains an optional persisted network, stamped by server verify and
  settle hooks from the request requirements.
- Claim, refund, idle-refund, selector input/output and pending-withdrawal
  selection are scoped to the manager network, falling back to the channel-id
  binding for rows without a network.
- A failed claim batch no longer blocks later batches or settlement of
  successful claims; claim and claim_and_settle raise BatchClaimError carrying
  the batch errors, successful results and any completed settle.

Refs #4185

Co-authored-by: phdargen <phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>

* docs(python): name changelog fragment after PR number

Co-authored-by: phdargen <phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>

* test(python): assert the claim selector submits only the local voucher

The selector test checked that the foreign channel's total_claimed stayed
at 100. A claim update looks that channel up under the manager network, so
a submitted foreign voucher leaves the stored total unchanged. Assert the
facilitator claim salts are only the local channel and the result reports
one voucher.

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>

---------

Co-authored-by: phdargen <phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>
Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>
* chore(evm): inline the channel network reader

channelNetwork only returned channel.network. matchesChannelQuery and
settleQueryByScan read the field directly. An empty stored network still
does not fall through to the query filter.

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>

* chore(evm): remove a wrong totalClaimed comment

The missing totalClaimed check runs whether or not a local channel row
exists. The comment said it applied only when there was no local row.

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>

---------

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>
Co-authored-by: PhilBot <PhilBot402@users.noreply.github.com>
… recovers (#4194)

handle_payment_response treated any non-None hook return value as a
recovery signal and stopped running later hooks. The httpx and requests
transports then signed a fresh payload and sent the request again, so a
hook that returned anything (a dict, True, a counter value) paid twice,
even after the first payment had settled.

Only RecoveredResponseResult, or a mapping with recovered=True from an
extension hook, now counts, which matches TypeScript and the MCP
clients. Other return values are ignored and the remaining hooks run.
* docs: list AffixIO under third-party utilities

* docs: remove trailing blank line
* test(svm): compare a replayed distribute response in Go

asMap hid the channels slice type. Callers see SettleResponse, so the
restart must return the same value.

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>

* fix(svm): keep distribute extra.channels as []string

channelIDs already returns []string. Reading the stored settle response
back through encoding/json decoded that array as []interface{}.
decodeDistributionResult loads the pending-store JSON into a struct
whose Channels field is []string, then rebuilds the SettleResponse.
A fresh settlement, the in-flight share, and a reload now match.

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>

* fix(svm): embed SettleResponse in the stored distribute result

storedDistribution copied each settle field by hand, so a new field
would disappear on reload. Embed SettleResponse and let the outer Extra
take the JSON extra object, with Channels as []string.

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>

---------

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>
…dispatch (#4159)

* style(ts): drop restated comments from facilitator, server, and next handlers

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>

* style(python): drop restated comments from MCP and SVM helpers

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>

* style(go): drop stale and restated comments from recent SDK fixes

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>

* style: collapse duplicated facilitator dispatch in the recent SDK fixes

Share the verify/settle facilitator lookup in TypeScript and Python, drop the unused MCP register_schemes helper, and reuse Go's findSupportedKind.

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>

* fix(evm): type the upto invalid-payload payer as optional

getExactEvmPayloadPayer returns string | undefined, and VerifyResponse.payer is already optional. The extracted readUptoPermit2Payload result required payer: string, so the @x402/evm declaration build failed with TS2322.

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>

* docs(python): record removal of register_schemes

x402.mcp.utils.register_schemes shipped in 2.27.0. Callers register schemes on the payment client with register or register_v1.

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>

* docs(ts): correct the new facilitator lookup docs

The lookup docs said the first scheme match wins, and that an exact network beats a wildcard. A scheme match still has to hit that registration's network or pattern, and both hits return the same facilitator. The upto helper returns the whole payload, or either invalid-payload message.

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>

* docs(ts): say the facilitator fallback keeps a non-throwing result

A failed verify or settle response does not throw. Trying the next client after that response could settle twice. The registration note now names resolveSchemeNetworkFacilitator, which is where exact matching runs before the pattern check.

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>

---------

Co-authored-by: phdargen <29732335+phdargen@users.noreply.github.com>
…et (#4207)

* feat(evm): add Robinhood Chain (eip155:4663) with USDG as default asset

Add Robinhood Chain mainnet (chain ID 4663) to the EVM default asset
tables in the TypeScript, Go and Python SDKs, with USDG (Global Dollar,
0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168, 6 decimals) as the default
stablecoin for dollar-string pricing. USDG implements EIP-3009, so the
default exact transfer method applies; its EIP-712 domain is
name "Global Dollar", version "1".

Also list the network in the docs token table and the existing
x402ExactPermit2Proxy / x402UptoPermit2Proxy deployments on the chain
in contracts/evm/README.md.

* address review: release as patch, drop default-asset value tests
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.