Conversation
…rse than exact Under exact the wallet signs a fixed pre-call quote, so a discount the gateway only learns after the call (a DeepSeek prompt-cache hit) can never reach an x402 caller. When a 402 also offers `upto`, LLMClient / AsyncLLMClient chat calls (non-stream and stream) now sign a Permit2 PermitWitnessTransferFrom for the ceiling and the gateway settles the actual amount. Taken only when it cannot be worse than exact: an EVM upto offer with extra.facilitatorAddress for USDC on a network in EVM_NETWORKS, a USDC balance covering the ceiling, and either a Permit2 allowance covering it or a declared eip2612GasSponsoring extension (then a gasless EIP-2612 permit for exactly the ceiling rides along, so a wallet with no ETH works). Any RPC or signing error, or a ceiling over a spend limit, signs exact as before. A payment rejected before anything is served is retried once with exact and the client stays on exact for that network. One gas-sponsored permit per wallet+network in flight, tracked by the USDC nonce it signs over, since a second permit over the same nonce reverts on-chain. Signing is byte-identical to the official @x402/evm 2.28.0 client: the tests pin a vector that client generated (scripts/gen-upto-vector.mjs). A ceiling is not a charge: ChatResponse / chunk payment_scheme and cost_is_ceiling, get_spending()["ceiling_usd"], cost_basis on cost-log rows, and a marker in transactions.log. A settled amount from PAYMENT-RESPONSE is booked when reported. Opt out with payment_scheme="exact" or BLOCKRUN_PAYMENT_SCHEME=exact.
… calls sign one permit Three concurrent calls in one client all read the same USDC nonce before any recorded a pending permit and each signed one over it; live, one settled and two reverted. A call that might sign a permit now claims a per wallet+network preflight marker (under a lock, no await inside) before its read. A call that finds it taken never signs a permit: upto only if its own read shows the allowance covers the ceiling, else exact. Released when the preflight ends; a signed permit continues as the nonce record.
…2s, book stream ceilings - A rejection that follows a 502/503 replay of the same upto payment is no longer answered with an exact payment. Upto settles after the call is served, so the replay's "verification failed" can mean the first send settled (Permit2 nonce used). Paying exact on top would charge twice. This applies to non-stream and stream, sync and async. - A 402 that offers only upto, when upto cannot be used, raises PaymentError. extract_payment_details falls back to accepts[0], so it was being signed as an EIP-3009 transfer for the whole ceiling. - Streams book the upto ceiling whatever their pre-settlement PAYMENT-RESPONSE carries. - Exact spend limits cap on the signed (header) amount, which includes the tx fee, not the body's lower base price. - An upto rejection keeps the client on exact for 10 minutes, not for its whole life. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… reported as a charge Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…quote; note per-process guards The exact quote prices output at a tenth of max_tokens (OUTPUT_QUOTE_FACTOR), so a long answer settles for more under upto than exact would have charged. The "never worse than exact" claim was false, and the docs now say so. They also say the in-flight permit guards are per process. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…s in the shared signer - The upto->exact fallback now needs a definite verification failure: the gateway's "Payment verification failed" body, a verify-failure code the chat route emits (PAYMENT_INVALID / PAYMENT_UNFUNDED), or a fresh payment-required challenge. A first-send PAYMENT_REPLAY, or a body naming an earlier paid use (recoverable / poll_url / job_id), means that authorization was already served and paid for (e.g. a duplicated send); paying exact on top charged twice. Applies to sync/async, stream/non-stream (shared _may_fall_back_to_exact). _is_payment_rejection is unchanged. - A PAYMENT_REPLAY now raises a PaymentError with the gateway's message and poll_url instead of "Check your wallet balance". - extract_payment_details raises ValueError for an upto-only 402 (chat opts in with allow_upto=True and keeps its own clearer PaymentError), and create_payment_payload refuses a non-exact scheme; every signer passes the requirement's scheme. No non-chat endpoint signs an upto ceiling as EIP-3009. - The chat upto-only refusal says so when upto was sent and the gateway refused it, instead of listing only local reasons.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
blockrun.ai settles every x402 chat call with
exact: the wallet signs a fixed pre-call quote, so discounts the gateway only learns after the call can never reach x402 callers. A DeepSeek prompt-cache hit is one example ($0.028/M cached input vs $0.14/M). The gateway can offeruptoasaccepts[1], withexactstaying ataccepts[0](BILLING_UPTO_DEEPSEEK/BILLING_UPTO_ALL). Withuptoit settles the actual amount, which is never more than the signed ceiling.What
blockrun_llm/x402_upto.py(new) and chat wiring inLLMClient/AsyncLLMClient, covering non-stream and stream calls in both sync and async.Signing follows the official
@x402/evm2.28.0UptoEvmScheme/trySignEip2612PermitExtension:PermitWitnessTransferFrom: spender0x4020…0002, witness{to: payTo, facilitator: extra.facilitatorAddress, validAfter: 0}, deadlinenow + maxTimeoutSeconds, random 256-bit nonce.Permit2612AmountMismatchotherwise, and CDP rejects MaxUint256. It is attached asextensions.eip2612GasSponsoring.info, merged with@x402/coresemantics.scripts/gen-upto-vector.mjs). The payload, both signatures and both EIP-712 digests match it byte for byte.Selection: never worse than
exact.uptois used only when all of these hold:extra.facilitatorAddress, for USDC on a network inEVM_NETWORKS;eip2612GasSponsoring(a wallet with no ETH works);In every other case, and on any RPC or signing error, the SDK signs
exactas before and logs at debug level only. Other rules:exact. An allowance that already covers the ceiling means upto with no permit.get_balance()throughEVM_NETWORKS, with a 3 s timeout each. The async client reads without blocking the event loop.payment_scheme="exact"orBLOCKRUN_PAYMENT_SCHEME=exact. Solana,AnthropicClientand the non-chat endpoints are unchanged.Accounting: a ceiling is not a charge.
PAYMENT-RESPONSEwhen the gateway reports one. Otherwise it books the ceiling and labels it.ChatResponse.payment_schemeandcost_is_ceiling(also on stream chunks),get_spending()["ceiling_usd"],cost_basison cost-log rows, and(upto ceiling)intransactions.log.priceis then the upto ceiling.Verification
pytest tests/uniton 3.13: 1063 passed. The CI 3.9 command: 952 passed, 11 skipped.black --check .andruff check .are clean. mypy has no new errors (249 now vs 253 on main).tests/unit/test_x402_upto.py: the reference vector, selection fallbacks, the sponsored permit being attached or skipped, the nonce guard, rejection with one exact retry, accounting, streaming, and chain reads.blockrun.ai402 fordeepseek/deepseek-chatdoes not offer upto yet, so against production this change currently signsexact, exactly as today.Open points
PAYMENT-RESPONSEhas no settledamountyet, so non-stream upto calls book the labeled ceiling until the server adds it. Streams always will, because their header is sent before settlement.🤖 Generated with Claude Code