npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

x402-conformance-svm

v1.6.0

Published

Resource-server checks for x402 V2: a facilitator double with fault modes and a probe that proves your paid route never serves content on a /settle timeout, a broken answer, settlement_pending or a duplicate (R0-R10), binds each payment to the request it

Readme

x402-conformance-svm

The resource-server probe of x402-conformance. It starts a facilitator double on loopback, points your server at it through X402_FACILITATOR_URL, and pays your paid route with test payloads on Solana devnet or Base Sepolia (exact; EVM via EIP-3009) under each fault below. The name is historical: it grades Base Sepolia too. The double never touches a chain, a key or anything beyond loopback.

npx -y x402-conformance-svm probe --resource http://127.0.0.1:4020/paid --start "node server.js" --out results.json
npx -y x402-conformance-svm resource --url https://your.api/paid --out answer.json   # P1-P7: one unpaid GET, any network
npx -y x402-conformance-svm demo                                                     # R0-R10 against a bundled mock, no wallet
npx -y x402-conformance-svm --help
npx -y x402-conformance-svm --version

demo --fault serve_on_any_settle shows a failing run and its fix hints. --share on probe and resource also writes an anonymised corpus case next to --out (or to ./x402-conformance-case.json) and, when a check failed, prints a link to a pre-filled GitHub issue; nothing is sent. X402CONF_VIA names the entry point in the report and the User-Agent. resource and demo exit 0 when every check passes, 1 when one fails and 2 on a usage error.

| id | what the double does on /settle | your server must | |---|---|---| | R0 | (unpaid request) | answer 402 with a PAYMENT-REQUIRED header offering exact on Solana devnet or Base Sepolia; if it offers neither, the result is unsupported | | R1 | settles | serve the content, after calling the double (otherwise the probe stops: your server isn't using X402_FACILITATOR_URL) | | R2 | settles once, then duplicate_settlement | serve the same payment at most once (F3) | | R3 | never answers | not serve (F6) | | R4 | HTML 502 | not serve (F6) | | R5 | 503 with a body that reads as success | not serve (F9) | | R6 | 200 with an HTML page | not serve (F9) | | R7 | 200 with half a success body | not serve (F9) | | R8 | settlement_pending with a transaction | not serve before the payment is final (F5) | | R9 | invalid_transaction_state | not serve (F8) | | R10 | (the R1 response) | carry a PAYMENT-RESPONSE header that decodes to success: true |

Your server. Any x402 resource server works if it reads its facilitator URL from X402_FACILITATOR_URL and offers one of the two test networks. With the upstream Express middleware, unchanged:

import express from "express";
import { paymentMiddleware, x402ResourceServer } from "@x402/express";
import { HTTPFacilitatorClient } from "@x402/core/server";
import { ExactEvmScheme } from "@x402/evm/exact/server";   // or ExactSvmScheme from "@x402/svm/exact/server"

const network = "eip155:84532";                              // or "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1"
const server = new x402ResourceServer(new HTTPFacilitatorClient({ url: process.env.X402_FACILITATOR_URL }))
  .register(network, new ExactEvmScheme());
const app = express();
app.use(paymentMiddleware({ "GET /paid": { accepts: [{ scheme: "exact", price: "$0.001", network,
  payTo: "0xYourAddress" }], description: "paid route" } }, server));
app.get("/paid", (_req, res) => res.json({ content: "paid content" }));
app.listen(Number(process.env.PORT ?? 4020), "127.0.0.1");

This server passes R0-R10 in this repo's CI on both networks (example/express-server.ts).

Options. --port sets the double's loopback port (default 4402). --start runs your server with X402_FACILITATOR_URL set; the probe refuses to run if something else already answers on your server's port, and stops with an error if your server exits. --network picks auto (default), solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1 or eip155:84532. --out writes the results JSON, on errors too. Each R0-R10 request has a 60 s deadline, and each R14 request 30 s; a server still holding a request then has served nothing.

R11, payment-to-request binding. Give the routes to bind, with or without --resource: --cheap and --expensive are two paid routes at different prices (R11, R11a, R11b), --by-method a paid URL priced differently for GET and POST (R11c), --by-query two paid URLs that differ only in the query, comma-separated (R11d), --param a paid parameterised route with {id} where the parameter goes (R11e), and --siwx-cheap and --siwx-expensive two paid routes at different prices that declare sign-in-with-x, on an EVM (eip155:) network such as Base Sepolia (R11f). R11f needs @x402/extensions and viem installed next to the probe; without them it is reported unsupported, not failed.

R14, a crash between /settle and the response (--crash). After the other checks, and only when R1 passed (otherwise R14 is left out of the results), the probe kills your server twice, once while /settle is answering and once as the response starts, restarts it and presents the same payment (with a payment-identifier id if your route advertises that extension). Your server must not call /settle again or answer 402, and must either deliver once from its idempotency record or refuse and name the transaction or payment id so it can be reconciled. With --start the probe restarts that command itself; for a server it didn't start, pass --crash-kill "<command>" and --crash-restart "<command>". The idempotency record has to survive the restart: see example/crash-server.ts and FIXES.md#r14.

Retry double-pay (R20). --retry adds R20 on --resource: the double holds /settle past the client's 400 ms timeout, the probe gives up and retries once the first payment has settled. R20a retries with a fresh payment under the same payment-identifier id (and Idempotency-Key); R20b resends the same signed payment. Either way the request must be settled once and the retry must get the earlier result or a PAYMENT-RESPONSE naming the first transaction. A stock @x402/express server passes when it keeps an idempotency record keyed by the request (example/retry-server.ts); with no record it pays twice. checkClientRetry(client, url, double) grades a client's own retry the same way: it must resend the payment or keep the payment id.

From code only (1.5.0): R12, R13, R15, R16, R16a, R22, R23, R24 and C1 to C3. These checks are exports of the package, not commands: the CLI, the Action, runConformance, the pytest plugin and the MCP server don't run them. Each takes the facilitator double from startDouble(), except probeUnpaidHead, and your server or client must use the double's url as its facilitator. Each returns the same Result[] as probe, so makeReport and explain work on them.

| id | export | what your server or client must do | |---|---|---| | R12 | probeParallel(url, double, { parallel? }) | settle one payment-identifier id once when eight requests carry it at the same time, each with a fresh payment, and name that settlement in every served answer | | R13 | probeReplicas(urlA, urlB, double) | settle one payment-identifier id once across two replicas: sent to one, then the other, then both at once | | R15 | probeDelivery({ error, redirect, throws }, double) | not settle, and send no PAYMENT-RESPONSE, when the paid handler answers 5xx, redirects or throws | | R16 | probeHead(url, double) | not settle a paid HEAD: no 2xx, no /settle call, no PAYMENT-RESPONSE | | R16a | probeUnpaidHead(url) | answer an unpaid HEAD with the same 402 and PAYMENT-REQUIRED offer as GET | | R17 | probeCacheLeak(url, double) | mark the paid response private or no-store, or vary it on the payment header, so a shared cache in front of the route never serves the paid content to an unpaid request (RFC 9111) | | R18 | probePending(url, double) | never serve while a settlement is pending, late or unanswered, serve once after it is final, and surface the transaction on every refusal (V2 section 9) | | R19 | probeFlows(url, double) | on an upfront or escrow offer, settle before the resource executes and reject an unsupported flow (V2 section 6.1) | | R22 | probeStatus(routes, double), each route { url, status, headers? } | keep the handler's 2xx status and headers on the paid response: a 201 stays a 201 with its Location | | R23 | probeAbort(url, double) | not charge a client that disconnects while /settle is in flight, or charge it once and give a resend of the same payment the settled result | | R24 | probeMalformedHeader(url, double) | answer five malformed PAYMENT-SIGNATURE values with 402 or 400 and a structured error, never a 5xx, a dropped connection or the content | | C1 | checkClientHandles402(client, url, double) | never hand a 402 to its caller as a success: pay and get a 2xx, or reject with an error | | C2 | checkClientOfferPolicy(client, url, double, { networks, maxAmount }) | pay one of several offers on its configured networks and within its maximum, and never sign for a network outside its set | | C3 | checkClientKeepsV2(client, url, double) | pay with PAYMENT-SIGNATURE at x402Version 2 when the 402 also carries a V1 body |

A client check takes your client as a function that fetches a URL, pays and returns the final Response. The controls and the faulty builds each check catches are in example/ and test/, and docs/USING.md has a complete example.

In tests. x402-conformance-svm/testing runs R0-R10 against a server you start in-process. runConformance({ start: (facilitatorUrl) => ({ url, close? }), network?, deadlineMs?, via? }) returns a report and never throws for a failing server; start may also return a promise. expect.extend(x402Matchers) adds toPassX402Conformance() and toBeValidX402Challenge() for vitest and jest, and assertX402Conformance(report) throws the same message for node:test. saveReport(report, { dir?, share?, stack? }) writes the report to dir, else X402CONF_REPORT_DIR, else ./x402-conformance, with share: true also an anonymised corpus case, and returns the paths and the share link. checkPaymentRequired(url) is exported here too.

Exit codes. 0 all pass; 1 a check failed; 2 usage error, or the run couldn't complete (a port in use, your server exited or never answered); 3 unsupported: your server offers no network the probe grades, or a check could not run (R11f without @x402/extensions and viem); an unsupported result takes precedence over a failed one. --help and --version exit 0.

In GitHub Actions use the action at the repository root:

- uses: atsp-protocol/x402-conformance@v1
  with:
    resource-url: http://127.0.0.1:4020/paid
    start: node server.js

Apache-2.0. Ryan Gillespie / Optitransfer.