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

@chainsafe/x402-server-sdk

v0.0.0-reserved

Published

Canton x402 payer SDK — with a party key + wallet-sdk ledger access, prepare + sign a Canton Coin transfer and produce the X-PAYMENT payload for a facilitator to settle.

Readme

@chainsafe/x402-server-sdk

Payer-side SDK for x402 on the Canton Network. With a Canton party's Ed25519 key and ledger access (via @canton-network/wallet-sdk), X402Payer.authorize(requirements) prepares and signs a Canton Coin transfer and returns the CantonPaymentPayload a facilitator settles — the payer authorizes, it never executes.

For entities that hold a party key and have ledger access (agents, services, backends). Paying from a user's browser wallet (Loop/Console) is a different, wallet-connection path and out of scope here.

Install

npm install @chainsafe/x402-server-sdk @canton-network/wallet-sdk

Quickstart

import { SDK, type TokenProviderConfig } from "@canton-network/wallet-sdk";
import { CantonX402Payer, devnetConfig } from "@chainsafe/x402-server-sdk";

const net = devnetConfig({ ledgerClientUrl: "https://<your-participant>/…" });

// Your Token Standard registries (asset-specific — you own these). Amulet is the
// validator scan-proxy; other assets (e.g. USDCx) have their own registries.
const amuletRegistry = "https://<your-validator>/api/validator/v0/scan-proxy";

// Auth — see below. DevNet/TestNet/MainNet use OAuth2 client_credentials:
const auth: TokenProviderConfig = {
  method: "client_credentials",
  audience: net.auth.audience,
  scope: net.auth.scope,
  clientId: process.env.CANTON_OAUTH_CLIENT_ID!,
  clientSecret: process.env.CANTON_OAUTH_CLIENT_SECRET!,
};

// 1. Build a wallet-sdk instance with the token namespace.
const sdk = await SDK.create({
  auth,
  ledgerClientUrl: net.ledgerClientUrl,
  token: { auth, registries: [amuletRegistry] },
});

// 2. Construct the payer with your party key + the assets it supports.
const payer = new CantonX402Payer({
  sdk,
  key: { partyId: "payer::1220…", publicKey: "<base64>", privateKey: "<base64 seed>" },
  network: net.network,
  registries: [
    // one entry per supported asset (Amulet here; add USDCx etc. with their registries)
    { instrumentId: { id: "Amulet", admin: "DSO::1220…" }, registryUrl: amuletRegistry },
  ],
});

// 3. On a 402, authorize against the advertised requirements → get the payload.
const payload = await payer.authorize(requirements); // CantonPaymentRequirements from the 402
// hand `payload` to the merchant/facilitator (e.g. via the X-PAYMENT header)

Auth by environment

SDK.create takes a wallet-sdk TokenProviderConfig. Which method depends on the network:

// LocalNet — unsafe self-signed JWT (no real IdP)
const auth: TokenProviderConfig = {
  method: "self_signed",
  issuer: "unsafe-auth",
  audience: net.auth.audience,
  scope: "",
  clientId: "ledger-api-user",
  clientSecret: "unsafe",
};

// DevNet / TestNet / MainNet — OAuth2 client credentials from your validator operator
const auth: TokenProviderConfig = {
  method: "client_credentials",
  audience: net.auth.audience,
  scope: net.auth.scope,
  clientId: process.env.CANTON_OAUTH_CLIENT_ID!,
  clientSecret: process.env.CANTON_OAUTH_CLIENT_SECRET!,
};

Use localnetConfig/devnetConfig/mainnetConfig for the matching network values.

X402Payer

Scheme-agnostic contract (the auto-pay fetch wrapper depends on this, not on the Canton implementation):

interface X402Payer {
  supports(requirements: CantonPaymentRequirements): boolean;   // scheme + network + asset
  authorize(requirements: CantonPaymentRequirements): Promise<CantonPaymentPayload>;
}

CantonX402Payer.authorize():

  • validates the requirement (amount, scheme, network, expiry) via x402-core;
  • builds the Token Standard transfer (sdk.token.transfer.create — routes the transfer factory via the registry);
  • prepares it (sdk.ledger.prepare — interactive submission) to get the hash;
  • Ed25519-signs the hash with the party key (x402-core's signHash);
  • assembles the payload (requirementsHash, required publicKey, required hashingSchemeVersion) without executing — facilitator derives fingerprint from publicKey (fingerprintForPublicKey; no keyFingerprint on the wire).

CantonX402Payer is stateless: double-settle is prevented by the facilitator (it rejects a reused nonce) and Canton (it dedupes execute by a submissionId derived from the prepared-tx hash), so the payer doesn't track in-flight requests.

Auto-pay fetch

createX402Fetch(payer) wraps fetch so payments are transparent: it calls the resource, and on a 402 reads the accepted requirements, pays via the payer, and retries with the X-PAYMENT header. Non-402 responses — and 402s the payer can't satisfy — pass through unchanged.

import { createX402Fetch } from "@chainsafe/x402-server-sdk";

const f = createX402Fetch(payer);                    // one payer
// const f = createX402Fetch([cantonPayer, evmPayer]); // or several
const res = await f("https://api.example/paid");     // 402 → pay → retry, transparently

Accepts one payer or several. With several, each advertised requirement routes to the first payer that supports() it (server accepts[] order wins) — so a mixed-scheme offer (e.g. Canton + EVM) is paid by the right payer. Options: select(accepts, payers) to override the routing, and fetch to wrap a custom fetch. Depends only on the X402Payer contract, so it's scheme-agnostic.

Works for any HTTP method. The paid request is retried, so a request body must be re-readable — a value like a string, JSON, Buffer, or URLSearchParams, not a ReadableStream (and don't pass a Request object whose body the first call consumes).

Config presets

localnetConfig / devnetConfig / mainnetConfig return { network, ledgerClientUrl, auth } to feed SDK.create. LocalNet defaults the localhost ledger URL (network id is per-instance, so pass it); DevNet/MainNet fill the network id from x402-core and take your participant's ledger URL. Every field is overridable. Registries are not here — they're asset-specific (Amulet vs USDCx), so you pass them to CantonX402Payer.

Settling

The payload is settled by a facilitator. FacilitatorClient (from @chainsafe/x402-client) is re-exported for consumers that drive verify/settle themselves:

import { FacilitatorClient } from "@chainsafe/x402-server-sdk";
const facilitator = new FacilitatorClient("https://x402.example.com", { apiKey: "…" });
const result = await facilitator.settle(payload, requirements);