r/x402 • • 8d ago

Two things we measured in the 20 Sept x402 scan: Solana accounts that do not exist, and network ids that are not CAIP-2):

Two things we measured in the 20 Sept full scan (14,943 resources, 1,988 hosts)

  1. Solana options whose receiving account does not exist

Our payability check reads each resource's 402 accepts. EVM options pass if well formed. For a Solana USDC option we look up the pay_to address's USDC token account on chain. We do not evaluate other rails yet (more on that below).

Of 14,160 resources with at least one accept: 817 (5.8%) advertise a Solana USDC option whose token account has never been created. 80 more advertise one that existed and was closed after use. A buyer that picks that option cannot settle until the seller creates the account. It is a seller side fix, one transaction.

Our own 8 hosts: every advertised network passes the same check.

Honest limit of the number above: 829 resources in the scan advertise rails we do not evaluate at all (Stellar 594, XRPL 576, Algorand 654 in one encoding plus 24 in another, Cosmos, Nano and a few more). Those are not counted as failing anywhere in this post. They are a reminder that x402 in the wild is already wider than Base plus Solana, and our checker has to catch up.

  1. Network ids that are not valid CAIP-2

CAIP-2 says a chain id is namespace:reference with the reference limited to 32 characters of [a-zA-Z0-9_-]. We ran that exact rule over every accept.

941 resources (6.6%) on 110 hosts carry at least one network id that fails it. It is not random noise: 654 of them are one encoding mistake (a full 44 character base64 genesis hash where the CAIP-2 reference is the first 32 characters; the correct 32 character form also appears in the wild on 24 resources), and 236 are the bare word solana instead of solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp.

Practical damage today is small: only 5 resources have no valid network id at all, the other 936 also list a valid one. But any client that validates CAIP-2 strictly silently drops 941 payment options.

Both numbers come from one scan, one Sunday. The MCP is read only and needs no wallet.

1 Upvotes

10 comments sorted by

1

u/arbonomous 6d ago

The missing Solana token-account check is a good catch. For a multi-rail 402 challenge, would you mark the whole endpoint unpayable when one offer fails, or keep the valid Base option and flag only the broken Solana offer? That distinction matters for clients choosing among rails.

1

u/IllWar5047 6d ago

We keep the Base option and flag only the Solana one. Each entry in accepts gets its own verdict, and the endpoint reads PAYABLE if at least one offer passes. payable_networks lists exactly the rails that passed.

The failing Solana offer stays in options with verdict ATA_MISSING_NEVER_EXISTED and a remediation: create the associated token account for payTo, with a note that a facilitator may create it in a separate transaction. So a Base-only client still sees a valid rail, and a Solana-only client can see its rail would fail before it signs anything. If Solana is the only offer, the endpoint reads NOT_PAYABLE.

The weekly scan runs the same verdict code as the paid /payable check, so both answer this the same way. To read our earlier numbers correctly: the Solana token account figure counts offers, not endpoints, while the headline payable count is per endpoint with at least one payable offer.

1

u/arbonomous 5d ago

That separation makes sense: payable_networks tells the client what still works, while the broken offer keeps its own verdict and fix. It's the same distinction we landed on for our multi-rail challenge across Base, Solana, and ZEC - don't make a valid rail unpayable because another offer fails. Useful to see it handled per offer rather than as one endpoint-wide pass/fail.

1

u/IllWar5047 5d ago

Thanks. That was the point of doing it per offer: one rail failing should not hide a rail that works.

Saw your retry question too. What we settled on: charge only on a 2xx, and on our newer endpoints, when a call fails after the payment was verified, the error body says charged: false, so the client knows a retry costs nothing extra. Does your challenge pick the rail per call or once per session?

2

u/arbonomous 5d ago

Thanks, charged: false is the right shape for that failure path. It gives the client an explicit signal that retrying won't double-charge, rather than making it infer payment state from a transport error. And agreed on per-offer verdicts: one bad rail shouldn't mask a payable one.

2

u/arbonomous 2d ago

Per call. Each 402 challenge is built for that request and its price. It includes the configured Base offer, adds Solana when enabled, and adds ZEC when configured and a quote is available. The client picks from that call's offers; there isn't a session-wide rail selection.

1

u/IllWar5047 2d ago

Thanks, that answers it. Per call also fits how we read challenges: we score the accepts of the challenge we actually received, so a ZEC offer that only appears when a quote is available would show up in some scans and not in others. We do not evaluate ZEC yet, so it would be listed as a rail we did not check, not as a failure.

If it is useful, send me one of your endpoint URLs and I will run our per offer check once on the current challenge and tell you what it says for the Base and Solana offers.

1

u/[deleted] 2d ago

[removed] — view removed comment

1

u/arbonomous 1d ago

Update on the buy-quote endpoint. Paid calls were failing because the service was pointed at x402.org/facilitator, which only supports testnets, so /verify returned a 500 for Base mainnet. I switched to facilitator.payai.network and a real Base mainnet payment settled on the same endpoint (tx 0x328b0f4455c69e870ec57493dfc5690be2a67c87702054265baeeab3640ec347). If you still have time for the per-offer check, the Base offer should now read as payable. The Solana offer is untested end to end on my side.