← Blog
September 11, 2026 · 12 min

Agentic week: Cardano got into x402 and reset its clocks

Cardano landed in x402 on 9 September after fourteen weeks in review. Getting a chain with probabilistic finality through the door forced the SDK to triple its facilitator timeout, rewrite two middleware adapters, and lengthen the MCP tool timeout from 60 seconds to whatever the seller declared.

Twenty-three pull requests merged into x402-foundation/x402 between 4 and 11 September. Last week the story was outside contributors finding bugs by reading code. This week the outside share fell to five accounts with one PR each, and the largest author was not a person: PhilBot402, which signs its PRs "Automated by @phdargen", merged eight. phdargen merged seven and the docs bot three. The pattern is a maintainer plus an automation that ports every TypeScript change to Go and Python within a day and files an issue when the three SDKs drift.

The other thread this week runs outside the repo. A gateway called X Pay went live on Base mainnet on 11 September and advertises that it returns the paid response before settlement completes. Visa published survey data on 9 September saying that 23 percent of US consumers trust generative AI to handle a payment on their behalf. Both are about the same question the Cardano merge is about: who waits, and for how long, between the signature and the money.

1. Cardano got in, and the clocks moved

PR #2537 by fabianbormann was opened on 1 June and merged on 9 September: 99 files, 18,673 lines added, a new @x402/cardano package, a scheme spec at specs/schemes/exact/scheme_exact_cardano.md, e2e fixtures, and a publish workflow. It is TypeScript only. The docs PR adds cardano:mainnet, cardano:preprod and cardano:preview as network identifiers, with USDM as the mainnet default asset and tUSDM on preprod, both six decimals. As of this writing the package is not yet on npm.

The model is the one we described for XRPL and NEAR in the network bindings post: client-signed, facilitator-submitted. The agent builds and signs a complete transaction, does not broadcast it, and sends the bytes in PAYMENT-SIGNATURE. The facilitator verifies amount, recipient, that the nonce UTXO is unspent, value conservation, the fee floor and the TTL, then broadcasts during settle. The client pays the network fee, so the facilitator needs no funded wallet. The replay primitive is the UTXO named in payload.nonce, which the transaction consumes. The validity window is the transaction's TTL slot, bounded by maxTimeoutSeconds.

Three assetTransferMethod values ship: default for address-to-address, masumi to lock into the Masumi vested_pay escrow with a fixed nineteen-field datum, and script to lock into any server-defined contract with an arbitrary datum that x402 attaches verbatim and cannot validate. The spec spends a paragraph defending why this lives in extra rather than as an extension: extensions are ignorable by construction, and a client that ignored masumi would pay the script address with no datum and strand the funds permanently.

The paragraph that matters for anyone selling through x402 is about finality. Cardano runs Ouroboros Praos, and the spec says a transaction in the mempool or even in a recent block can be rolled back. Granting access on status: "mempool" is "strongly discouraged" for anything with real economic value. Settlement can return the non-terminal settlement_pending with a transaction id, and the resource server retries /settle once with the same payload while the facilitator resumes observing without rebroadcasting. The docs example sets maxTimeoutSeconds: 600.

What reviewing Cardano exposedPR #3392, merged 7 September, says the Hono and Next adapters "were returning the unread Response.clone() branch after processSettlement, so a long settle could deliver an empty or already-read body." The adapters read the body once for extensions, cloned it, and then handed the original stream back to the client after settlement. On a fast chain nobody noticed. On a chain where settle takes long enough for the runtime to drain the clone, the paying client got nothing. The fix consumes the handler body, including streams, before settle, and replies from that buffer only after confirmation. The same PR raised the default HTTPFacilitatorClient timeout from 30,000 to 90,000 milliseconds. Its docstring used to say the 30 seconds matched Go and Python. It no longer does, so the bot ported the 90 seconds to Python and Go on 8 September.

Then PR #3430 on 9 September fixed the MCP client: paid tool calls now time out from the accept's maxTimeoutSeconds, default 300 seconds, instead of a hard-coded 60 that was aborting slow settlements before they finished. The e2e MCP client dropped its manual override. Note the direction of every one of these changes. A single chain integration moved the facilitator budget from 30 to 90 seconds, the tool budget from 60 to 300, and made the seller's declared timeout the source of truth. A rail that was tuned for two-second blocks is being retuned for ten-minute ones, and every buyer SDK inherits the new defaults whether it ever touches Cardano or not.

2. Catalog poisoning through double encoding, fixed in three languages

PR #3213 by ygd58 was opened on 20 August and merged on 9 September. It fixes isValidRouteTemplate in the Bazaar extension, the discovery catalog we covered in the Bazaar post. The function ran a single decodeURIComponent pass before checking the template for .. and ://. But ROUTE_TEMPLATE_REGEX allows a literal percent sign, so a double-encoded payload such as %252e%252e or %253a%252f%252f survived one decode still encoded, the substring checks never saw it, and the function returned true.

The consequence, in the words of the function's own doc comment, is that a malicious route template lets a client cause the facilitator to catalog a payment under an arbitrary URL. The author rejected the obvious patch. Decoding twice only closes the double-encoded case; triple encoding bypasses any fixed count. The fix, fullyDecodeRouteTemplate, decodes until a fixed point where another pass changes nothing, with a budget of five passes, and rejects anything that fails to parse or never converges. Six regression tests cover double and triple traversal, double-encoded scheme injection, a legitimate single-encoded segment that must still pass, and pathologically deep encoding. The Python and Go ports, #3440 and #3441, merged on 11 September. Twenty days from report to TypeScript merge. Two more days to parity.

3. Batch settlement got a deposit hint, and a ceiling on it

PR #3372, merged 7 September, adds an optional extra.minDeposit to the EVM batch-settlement scheme we audited in the deferred payments post. The server announces an atomic amount that must be a positive integer at or above amount. TypeScript servers now always announce it, defaulting to ten times the price. Clients should use a conforming hint as the deposit target. The facilitator must not enforce it. A server may reject a deposit below the hint with invalid_batch_settlement_evm_deposit_below_min_deposit, but only if enforceMinDeposit is switched on, and it is off by default.

The interesting half is the ceiling. Deposits in this scheme can sit in escrow through withdrawDelay, which the PR notes can be up to 30 days. A server-chosen minimum with no client-side cap is a lock-up attack: a 402 response that asks an agent to escrow an unbounded amount. So the client clamps. If the buyer configured spend controls, the deposit is limited to maxAmountPerPayment × depositMultiplier, default multiplier five, minimum three. If the buyer runs with spend controls off or an uncapped asset entry, the deposit is uncapped too. The PR explicitly rejected a global atomic maxDeposit because it cannot work across tokens with different decimals, and rejected putting deposit policy in core spendControls because it is scheme-specific. This is the same reasoning as the spend controls audit: one knob, the cap the buyer already set, reused as the bound on everything downstream. Go and Python do not have it yet. The drift issue #3405, opened by the bot on 8 September, lists it as large.

4. The MCP Python SDK stopped following redirects off-origin

The MCP Python SDK v2.2.0, published 7 September alongside a 1.30.0 maintenance release, changes four defaults that matter for an agent that connects to servers it does not control. Redirects are now followed only within the endpoint's origin: same scheme, host and port, or an http to https upgrade on the same host. Anything else fails with MCPError, and the OAuth providers apply the same rule to their own requests. The follow_redirects setting on a caller-supplied httpx client is no longer honoured for MCP traffic.

The OAuth client now checks the authorization server's issuer on the legacy path as well. Metadata whose issuer is not the server's own origin is rejected with an issuer-mismatch error. A 403 that is not an insufficient_scope challenge is returned instead of retried, and a 5xx or 429 on protected resource metadata stops the flow rather than falling back to legacy endpoints. A new AuthSettings.validate_token_resource lets a server accept only tokens its verifier reports as issued for that server, and 3.0 will default it to true. On the server side, stateful sessions on the pre-2026-07-28 transport now expire after 30 minutes idle and are capped at 10,000 per server. Stateless servers and 2026-07-28 connections are unaffected. Tasks, DPoP and the jwt-bearer grant remain unimplemented, per the release notes. Every one of these closes a path where a compromised or misconfigured server could steer a client somewhere else. They belong to the same family as the client identity work in the MCP authorization post.

5. Demand side: a gateway that ships before it settles, and a trust number

X Pay, from X-Pay Technologies in Amsterdam, announced mainnet on 11 September: settlement in USDC on Base, chain id 8453, pricing at one percent of settled volume, nothing charged on failed calls. The technical claims are standard x402: EIP-3009 authorizations signed as EIP-712 typed data, recovered against the token contract domain, spendable exactly once. One sentence stands out: "The response is released before settlement completes, so a paid call is as fast as a free one."

That is a design choice, and it is the opposite of the one the x402 SDK made twice in eight days. The two-phase gap exists because the seller wants to see the money before releasing bytes. Last week the Java SDK stopped serving content before settlement. This week Hono and Next stopped serving an empty body after a long settlement. X Pay's answer is to hold the seller's response for no one and, presumably, to carry the risk of a failed settle on its own balance sheet in exchange for the one percent. The release does not say who absorbs that failure. For a seller choosing a gateway that is the first question to ask.

Visa's Trust Index, released 9 September from a Harris Poll of 2,065 US consumers fielded 26 to 28 May, reports that 72 percent have used an AI assistant and 23 percent trust generative AI to handle a payment transaction on their behalf. 61 percent said they would trust Visa to handle an agentic transaction. Read as a product finding rather than a headline: the trust gap sits between the model and the money, and the brand people trust is the rail, not the agent. That is the argument for pushing authorization down into the payment layer, where a signed EIP-3009 authorization with a hard cap says exactly what the agent may spend, rather than up into the model, where a prompt says it.

What it means for LLM4Agents

The timeout changes hit us directly. Any seller side built on the reference middleware just had its default facilitator budget go from 30 to 90 seconds. Settlement on Base does not need anything close to that, so a 90-second default is a worst-case ceiling we would rather not inherit silently. The MCP change is the same in reverse: our paid tools declare maxTimeoutSeconds, and that value is now the client's abort timer. If we declare 300 because that is the default, a buyer that expected a 60-second abort will wait five minutes on a stuck settle.

The Hono and Next bug is a reminder that the two-phase gap has two failure modes, not one. Serving before settlement gives the buyer free content. Serving after settlement from a consumed stream gives the buyer nothing after paying. A gateway that streams tokens has to buffer the model's output before settle, because streamed tokens cannot be un-sent, and the same class of bug can appear in any adapter that clones a body for an extension hook.

The catalog poisoning fix is a direct threat model for our discovery entries. If we list endpoints in the Bazaar, a malformed route template from a third party could have cataloged a payment under a URL we own. Fixed now in all three languages, but it took three weeks and it was found by an outside reader, not by the test suite.

Cardano itself is not on our roadmap. What it enables is a settled pattern for chains where finality is probabilistic: settlement_pending, a single bounded retry, and an explicit prohibition on granting access at mempool. That pattern is the one we would need for any chain slower than Base, and it now has a reference implementation.

Staying on the frontier

First, pin the timeouts. Set timeoutMs on our facilitator client explicitly rather than accepting the new 90-second default, and set maxTimeoutSeconds on every accept to the real settlement time on Base plus margin, not the SDK default. Publish both numbers in our docs so buyers can size their own abort timers.

Second, upgrade the middleware and add a regression test for the streaming case: a handler that returns a stream, a settle that takes longer than the runtime's drain, and an assertion that the client receives the full body with PAYMENT-RESPONSE set. The fix is in @x402/hono and @x402/next now, but the test belongs in our suite too.

Third, adopt extra.minDeposit on our side only if we move to batch settlement, and if we do, clamp on the buyer side first. Our own buyer SDK should ship with depositMultiplier set and spend controls on by default, so no 402 can lock more than five times a single payment cap.

Fourth, mirror the MCP Python SDK's redirect and issuer rules in our own MCP client configuration, and set validate_token_resource to true on our server ahead of the 3.0 default.

Fifth, follow the drift bot. Issue #3405 is now the fastest way to know which SDK has a feature and which does not. For a platform whose buyers arrive in Python, TypeScript and Go, the drift list is the compatibility matrix.

Pay per call, settle per call

An OpenAI-compatible gateway where the payment confirms before the tokens leave.

Register an agent