<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: mypocketsmells</title>
    <description>The latest articles on DEV Community by mypocketsmells (@mypocketsmells).</description>
    <link>https://dev.to/mypocketsmells</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4075797%2Fdabdda6b-617c-4e42-8688-7896bc47000e.png</url>
      <title>DEV Community: mypocketsmells</title>
      <link>https://dev.to/mypocketsmells</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mypocketsmells"/>
    <language>en</language>
    <item>
      <title>X402 Battle Scars</title>
      <dc:creator>mypocketsmells</dc:creator>
      <pubDate>Thu, 13 Aug 2026 07:21:28 +0000</pubDate>
      <link>https://dev.to/mypocketsmells/x402-battle-scars-1l35</link>
      <guid>https://dev.to/mypocketsmells/x402-battle-scars-1l35</guid>
      <description>&lt;h1&gt;
  
  
  Shipping x402 on Solana: three failures the docs don't mention
&lt;/h1&gt;

&lt;p&gt;I put a paper-trading lab's telemetry up for sale over &lt;a href="https://docs.x402.org" rel="noopener noreferrer"&gt;x402&lt;/a&gt; — 8 endpoints on a Cloudflare Worker, $0.01–$0.02 per request, USDC on Solana mainnet, listed in the CDP Bazaar. The happy path took an afternoon. Getting real settlements to work took three genuinely undocumented failures. This is the writeup I wish had existed.&lt;/p&gt;

&lt;p&gt;Quick primer if you haven't touched x402 v2: a paid endpoint answers &lt;code&gt;402&lt;/code&gt; with a &lt;code&gt;PAYMENT-REQUIRED&lt;/code&gt; header (base64 JSON payment requirements). The client signs a payment against those requirements and retries with a &lt;code&gt;PAYMENT-SIGNATURE&lt;/code&gt; header. A &lt;strong&gt;facilitator&lt;/strong&gt; (I use Coinbase's CDP facilitator; PayAI also settles Solana) verifies and settles on-chain, and the &lt;code&gt;200&lt;/code&gt; comes back with a settlement receipt in &lt;code&gt;PAYMENT-RESPONSE&lt;/code&gt;. On EVM chains this flow is forgiving. On Solana, three of its assumptions break.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Your payout address needs a USDC token account &lt;em&gt;before&lt;/em&gt; the first sale
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; every buy attempt returns &lt;code&gt;402&lt;/code&gt; again with &lt;code&gt;transaction_simulation_failed&lt;/code&gt;. No other detail. The buyer's wallet is funded, the requirements decode cleanly, the facilitator is reachable — it just refuses, identically, every time.&lt;/p&gt;

&lt;p&gt;I burned three attempts theorizing about wallet bugs and payload corruption before checking the obvious-in-hindsight thing: the payout address had never held USDC, so it had &lt;strong&gt;no associated token account (ATA)&lt;/strong&gt; for the USDC mint (&lt;code&gt;EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v&lt;/code&gt;). The facilitator &lt;em&gt;simulates&lt;/em&gt; the transfer during verification; a transfer into a nonexistent ATA fails simulation; the facilitator rejects before anything hits the chain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; send your own payout address a few cents of USDC once. The transfer creates the ATA and every subsequent sale settles. (Nothing is lost during the failures — verification rejects before any money moves.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson:&lt;/strong&gt; on EVM, any address can receive tokens. On Solana, &lt;em&gt;receiving&lt;/em&gt; is a stateful capability you set up. If you're building an x402 seller with a fresh payout wallet, fund its ATA as part of deployment, not as a debugging epiphany.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. CDP rotates the Solana feePayer — and your serverless isolates each cache their own
&lt;/h2&gt;

&lt;p&gt;This one is the reason this post exists.&lt;/p&gt;

&lt;p&gt;On Solana, the facilitator sponsors transaction fees, and its fee-payer pubkey is embedded in the payment requirements your server hands out (&lt;code&gt;extra.feePayer&lt;/code&gt;). Your resource server learns that value from the facilitator's &lt;code&gt;/supported&lt;/code&gt; endpoint.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; every purchase through the CDP facilitator bounced with verification failures, while the exact same code settled fine through PayAI. Six consecutive &lt;code&gt;curl&lt;/code&gt;s against my Worker's 402 endpoint showed &lt;strong&gt;three different feePayers&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;What's happening: CDP rotates the feePayer across &lt;code&gt;/supported&lt;/code&gt; calls, PayAI's is static. And on Cloudflare Workers (or Lambda, or any serverless runtime), &lt;em&gt;each isolate&lt;/em&gt; fetches and caches &lt;code&gt;/supported&lt;/code&gt; independently. So isolate A builds your 402 with feePayer X, the buyer signs a transaction naming X, the retry lands on isolate B which knows feePayer Y — and verification fails. It's a distributed-cache coherence bug wearing a payments costume, and it's invisible until you have both a rotating facilitator &lt;em&gt;and&lt;/em&gt; more than one isolate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; pin one &lt;code&gt;/supported&lt;/code&gt; snapshot in shared storage (KV) so every isolate serves the same feePayer. TTL comfortably above the payment-proof validity window (mine: 20 min pin vs 300 s proof window):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CachedFacilitatorClient&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;HTTPFacilitatorClient&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FacilitatorConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;kv&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;KVNamespace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;cacheKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;override&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;getSupported&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;SupportedResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cached&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;SupportedResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cacheKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cached&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;cached&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fresh&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSupported&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cacheKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fresh&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;expirationTtl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1200&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;fresh&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the pin: six requests, one feePayer, purchase settled on-chain in under four seconds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson:&lt;/strong&gt; treat &lt;code&gt;/supported&lt;/code&gt; as &lt;em&gt;coordination state&lt;/em&gt;, not as a config fetch. If your facilitator rotates anything and your runtime has more than one instance, challenge and verify must read the same snapshot.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Solana blockhashes give you ~90 seconds — slow payment flows are dead flows
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; a payment flow that involved carrying the signed payment token between tools (wallet signs in one place, redeem happens in another) always came back &lt;code&gt;BlockhashNotFound&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Solana transactions reference a recent blockhash and expire after roughly 60–90 seconds. An EVM payment authorization is a signature over typed data — it keeps for the whole &lt;code&gt;maxTimeoutSeconds&lt;/code&gt; window, so relaxed flows (sign here, paste there, redeem later) work. The Solana equivalent embeds a blockhash, so &lt;strong&gt;the wall-clock budget from "402 received" to "redeem submitted" is about a minute&lt;/strong&gt;, regardless of what the x402 timeout field says.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; make the client one process. Fetch → receive 402 → sign → retry, no humans and no copy-paste in the loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;wrapFetchWithPayment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;x402Client&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@x402/fetch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;registerExactSvmScheme&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@x402/svm/exact/client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;x402Client&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nf"&gt;registerExactSvmScheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;signer&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;      &lt;span class="c1"&gt;// @solana/kit signer&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;payFetch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;wrapFetchWithPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;payFetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://whodeployed.wtf/v1/leaderboard&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// 402 → sign → settle → 200, ~2-4s total, receipt in PAYMENT-RESPONSE&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Lesson:&lt;/strong&gt; design Solana x402 clients for a 60-second total budget. Any architecture where the token rests — approval queues, human-in-the-loop wallets, multi-tool relays — needs the signing window to open &lt;em&gt;after&lt;/em&gt; the human decision, not before.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lightning round
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bazaar indexing rides on settlements, not registrations.&lt;/strong&gt; The CDP Bazaar catalogs your endpoint when a &lt;em&gt;compliant&lt;/em&gt; settlement carries the discovery declaration (&lt;code&gt;declareDiscoveryExtension&lt;/code&gt; in your route config, echoed by the client). Wallet gateways may strip it — my endpoints only indexed after buys from a spec-compliant &lt;code&gt;@x402/fetch&lt;/code&gt; client.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;workers.dev&lt;/code&gt; bot protection 403s unusual clients.&lt;/strong&gt; Cloudflare's untunable browser-integrity check on &lt;code&gt;workers.dev&lt;/code&gt; rejects exotic user agents (error 1010) — including some agent frameworks. A custom domain puts those protections under your control. Related: the moment you add &lt;code&gt;routes&lt;/code&gt; to &lt;code&gt;wrangler.jsonc&lt;/code&gt;, workers.dev silently turns off unless you set &lt;code&gt;"workers_dev": true&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify with the right &lt;code&gt;Accept&lt;/code&gt; header.&lt;/strong&gt; My landing serves HTML to browsers and JSON to everything else; I spent ten minutes "debugging" a deploy because &lt;code&gt;curl&lt;/code&gt; without &lt;code&gt;Accept: text/html&lt;/code&gt; was grepping the JSON representation.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The stack, for reference
&lt;/h2&gt;

&lt;p&gt;Cloudflare Worker + Hono, &lt;code&gt;@x402/hono&lt;/code&gt; v2 middleware, EVM + SVM schemes registered from one shared resource server, KV-cached snapshots, paid MCP tools on the same Worker (&lt;code&gt;/mcp&lt;/code&gt;), CDP facilitator → official Bazaar. Total revenue at time of writing: cents — this is an experiment in agent commerce, not a business plan. But every failure above cost hours, and each fix is three lines once you know it exists.&lt;/p&gt;

&lt;p&gt;The store this came from: &lt;a href="https://whodeployed.wtf" rel="noopener noreferrer"&gt;whodeployed.wtf&lt;/a&gt; — 780+ paper-trading bots' telemetry (Kalshi/Polymarket/crypto/equities), every response self-flagging its staleness. It's also a deliberately cheap, always-on target if you're testing an x402 v2 client: $0.01 gets you real JSON and a settlement receipt; free dry run at &lt;a href="https://whodeployed.wtf/v1/arena" rel="noopener noreferrer"&gt;&lt;code&gt;/v1/arena&lt;/code&gt;&lt;/a&gt;. Not investment advice — the data is simulated paper dollars from a research lab that tells you when it's lying.&lt;/p&gt;

</description>
      <category>api</category>
      <category>blockchain</category>
      <category>crypto</category>
      <category>web3</category>
    </item>
  </channel>
</rss>
