Problem
ExtractPaymentDetails unconditionally returns the first option, and its own doc comment says so:
// x402.go:243-250
// ExtractPaymentDetails extracts payment details from a PaymentRequirement.
// Returns the first payment option if multiple are available.
func ExtractPaymentDetails(req *PaymentRequirement) (*PaymentOption, error) {
...
option := req.Accepts[0]
createPaymentPayload then dispatches purely on the client's own chain and never reads option.Network:
// base_client.go:203-205
if bc.isSolana() {
return CreateSolanaPaymentPayload(bc.solanaKey, option, resourceURL, description, extensions, bc.solanaRPCURL)
}
Nothing ever checks that the selected option is one this client can actually pay.
Impact
Multi-option 402s break. If a gateway offers [base, solana] in that order, a Solana client takes the Base option, hands an ERC-20 hex address to CreateSolanaPaymentPayload, and dies at invalid asset mint — even though a payable Solana option was sitting right there in Accepts[1].
The error message actively misleads. A Solana client receiving a Base-only 402 (mis-set BLOCKRUN_SOLANA_API_URL, a WithAPIURL override, or a multi-chain gateway) now fails with:
feePayer is required in payment requirement extra for Solana transactions
That blames the server for a client-side chain mismatch and sends users hunting a gateway bug that doesn't exist. Before #9 the same case stopped earlier at "no wallet is configured" — also wrong, but it at least pointed at the client.
Fix
- Have
ExtractPaymentDetails select the option matching the client's chain rather than always taking [0] (it needs the chain passed in, or the selection moves to createPaymentPayload).
- When no option matches, fail explicitly:
return "", &PaymentError{Message: fmt.Sprintf(
"client is configured for Solana but the 402 only offers %q", option.Network)}
Scope note: this is about selecting a payable option and reporting mismatches, not about capping spend — per-call spend limits are a settled no.
Provenance
Surfaced independently by Codex and the adversarial pass during /review of #9.
Problem
ExtractPaymentDetailsunconditionally returns the first option, and its own doc comment says so:createPaymentPayloadthen dispatches purely on the client's own chain and never readsoption.Network:Nothing ever checks that the selected option is one this client can actually pay.
Impact
Multi-option 402s break. If a gateway offers
[base, solana]in that order, a Solana client takes the Base option, hands an ERC-20 hex address toCreateSolanaPaymentPayload, and dies atinvalid asset mint— even though a payable Solana option was sitting right there inAccepts[1].The error message actively misleads. A Solana client receiving a Base-only 402 (mis-set
BLOCKRUN_SOLANA_API_URL, aWithAPIURLoverride, or a multi-chain gateway) now fails with:That blames the server for a client-side chain mismatch and sends users hunting a gateway bug that doesn't exist. Before #9 the same case stopped earlier at "no wallet is configured" — also wrong, but it at least pointed at the client.
Fix
ExtractPaymentDetailsselect the option matching the client's chain rather than always taking[0](it needs the chain passed in, or the selection moves tocreatePaymentPayload).Scope note: this is about selecting a payable option and reporting mismatches, not about capping spend — per-call spend limits are a settled no.
Provenance
Surfaced independently by Codex and the adversarial pass during
/reviewof #9.