Skip to content
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
ecb9235
feat(bsv): add exact scheme support for BSV
sirdeggen Jul 17, 2026
1dad246
feat(bsv): harden exact scheme verification and replay defense
sirdeggen Jul 21, 2026
bf4f969
feat(bsv): register ttn/tstn networks and refuse bip122 ambiguity
sirdeggen Jul 31, 2026
9808154
Merge main into feat/bsv-exact-scheme
sirdeggen Jul 31, 2026
fa6f8ad
Merge remote-tracking branch 'upstream/main' into feat/bsv-exact-scheme
claude Aug 19, 2026
f8813af
fix(bsv): adapt to upstream SchemeNetworkServer/MoneyParser API changes
claude Aug 19, 2026
53a8fdf
Merge remote-tracking branch 'upstream/main' into feat/bsv-exact-scheme
claude Aug 21, 2026
6cfa20a
Merge remote-tracking branch 'upstream/main' into feat/bsv-exact-scheme
claude Aug 22, 2026
36c1fda
Merge remote-tracking branch 'upstream/main' into feat/bsv-exact-scheme
claude Aug 24, 2026
410e8f9
Merge remote-tracking branch 'upstream/main' into feat/bsv-exact-scheme
claude Aug 25, 2026
d14bff7
style(bsv): fix prettier formatting in constants test
claude Aug 25, 2026
1e03a2e
Merge remote-tracking branch 'upstream/main' into feat/bsv-exact-scheme
claude Aug 26, 2026
9bc80be
Merge remote-tracking branch 'upstream/main' into feat/bsv-exact-scheme
claude Aug 27, 2026
f3dcdbe
Merge remote-tracking branch 'upstream/main' into feat/bsv-exact-scheme
claude Aug 28, 2026
0c38ccb
Merge remote-tracking branch 'upstream/main' into feat/bsv-exact-scheme
claude Sep 1, 2026
9d43df6
Merge remote-tracking branch 'upstream/main' into feat/bsv-exact-scheme
claude Sep 2, 2026
011615b
Merge remote-tracking branch 'foundation/main' into feat/bsv-exact-sc…
sirdeggen Sep 3, 2026
92fc793
docs(bsv): note CAIP-2 bsv namespace is registered
sirdeggen Sep 3, 2026
df5fdb3
Merge remote-tracking branch 'upstream/main' into feat/bsv-exact-scheme
claude Sep 4, 2026
21c0838
Merge remote-tracking branch 'upstream/main' into feat/bsv-exact-scheme
claude Sep 5, 2026
d477c5b
Merge remote-tracking branch 'upstream/main' into feat/bsv-exact-scheme
claude Sep 6, 2026
72a0ca9
Merge remote-tracking branch 'upstream/main' into feat/bsv-exact-scheme
claude Sep 8, 2026
23fb0cb
fix(typescript): regenerate pnpm-lock.yaml after upstream merge
claude Sep 8, 2026
53d7690
Merge remote-tracking branch 'upstream/main' into feat/bsv-exact-scheme
claude Sep 9, 2026
61e1c76
Merge remote-tracking branch 'upstream/main' into feat/bsv-exact-scheme
claude Sep 10, 2026
30a30e8
Merge remote-tracking branch 'upstream/main' into feat/bsv-exact-scheme
claude Sep 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions .github/workflows/publish_npm_scoped_x402_bsv.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
name: Publish @x402/bsv package to NPM

on:
workflow_dispatch:

jobs:
publish-npm-x402-bsv:
if: github.repository == 'x402-foundation/x402' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
environment: npm
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Setup pnpm
uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8
with:
version: 11.1.1

- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: "24"
registry-url: "https://registry.npmjs.org"

- name: Update npm for OIDC trusted publishing
run: npm install -g npm@11.14.1 --ignore-scripts

- name: Configure npm for trusted publishing
run: npm config delete always-auth 2>/dev/null || true

- name: Install and build
working-directory: ./typescript
run: |
pnpm install --frozen-lockfile --ignore-scripts
pnpm -r --filter=@x402/core --filter=@x402/bsv run build

- name: Publish @x402/bsv package
working-directory: ./typescript/packages/mechanisms/bsv
run: |
# Get package information directly
PACKAGE_NAME=$(node -p "require('./package.json').name")
PACKAGE_VERSION=$(node -p "require('./package.json').version")

echo "Package: $PACKAGE_NAME@$PACKAGE_VERSION"

echo "Publishing to NPM (main branch)"
pnpm publish --provenance --access public
60 changes: 60 additions & 0 deletions e2e/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions examples/typescript/clients/advanced/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ cp .env-local .env
and fill required environment variables:

- `APTOS_PRIVATE_KEY` - Aptos Ed25519 private key for Aptos payments (optional; `all-networks`)
- `BSV_WALLET` - set to `true` to pay via a running BRC-100 wallet, e.g. BSV Desktop (optional; `all-networks`; BSV payers use a wallet connection instead of a raw private key)
- `CCD_PRIVATE_KEY` - Concordium Ed25519 private key for Concordium payments (optional; `all-networks`)
- `CCD_ADDRESS` - Concordium account address for Concordium payments (optional; `all-networks`)
- `EVM_PRIVATE_KEY` - Ethereum private key for EVM payments
Expand Down
17 changes: 15 additions & 2 deletions examples/typescript/clients/advanced/all_networks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* optional chain configuration via environment variables.
*
* New chain support should be added here in alphabetic order by network prefix
* (e.g., "algorand" before "aptos" before "ccd" before "eip155" before "hedera" before "near" before "solana" before "stellar" before "tvm" before "xrpl").
* (e.g., "algorand" before "aptos" before "bsv" before "ccd" before "eip155" before "hedera" before "near" before "solana" before "stellar" before "tvm" before "xrpl").
*/

import {
Expand All @@ -20,6 +20,8 @@ import { x402Client, wrapFetchWithPayment, x402HTTPClient } from "@x402/fetch";
import { ExactAptosScheme } from "@x402/aptos/exact/client";
import { toClientAvmSigner } from "@x402/avm";
import { ExactAvmScheme } from "@x402/avm/exact/client";
import { ExactBsvScheme } from "@x402/bsv/exact/client";
import { WalletClient } from "@bsv/sdk";
import { ExactConcordiumScheme } from "@x402/concordium/exact/client";
import { ExactEvmScheme } from "@x402/evm/exact/client";
import { UptoEvmScheme } from "@x402/evm/upto/client";
Expand Down Expand Up @@ -54,6 +56,8 @@ config();
// Configuration - optional per network
const avmPrivateKey = process.env.AVM_PRIVATE_KEY as string | undefined;
const aptosPrivateKey = process.env.APTOS_PRIVATE_KEY as string | undefined;
// BSV payers use a running BRC-100 wallet (e.g. BSV Desktop) instead of a raw private key.
const bsvWalletEnabled = process.env.BSV_WALLET === "true";
const ccdPrivateKey = process.env.CCD_PRIVATE_KEY as string | undefined;
const ccdAddress = process.env.CCD_ADDRESS as string | undefined;
const evmPrivateKey = process.env.EVM_PRIVATE_KEY as `0x${string}` | undefined;
Expand Down Expand Up @@ -109,6 +113,7 @@ async function main(): Promise<void> {
if (
!avmPrivateKey &&
!aptosPrivateKey &&
!bsvWalletEnabled &&
!(ccdPrivateKey && ccdAddress) &&
!evmPrivateKey &&
!keetaMnemonic &&
Expand All @@ -120,7 +125,7 @@ async function main(): Promise<void> {
!xrplSeed
) {
console.error(
"❌ At least one of AVM_PRIVATE_KEY, APTOS_PRIVATE_KEY, CCD_PRIVATE_KEY + CCD_ADDRESS, EVM_PRIVATE_KEY, KEETA_MNEMONIC, NEAR_ACCOUNT_ID + NEAR_PRIVATE_KEY, SVM_PRIVATE_KEY, STELLAR_PRIVATE_KEY, HEDERA_ACCOUNT_ID + HEDERA_PRIVATE_KEY, TVM_PRIVATE_KEY, or XRPL_SEED is required",
"❌ At least one of AVM_PRIVATE_KEY, APTOS_PRIVATE_KEY, BSV_WALLET=true, CCD_PRIVATE_KEY + CCD_ADDRESS, EVM_PRIVATE_KEY, KEETA_MNEMONIC, NEAR_ACCOUNT_ID + NEAR_PRIVATE_KEY, SVM_PRIVATE_KEY, STELLAR_PRIVATE_KEY, HEDERA_ACCOUNT_ID + HEDERA_PRIVATE_KEY, TVM_PRIVATE_KEY, or XRPL_SEED is required",
);
process.exit(1);
}
Expand Down Expand Up @@ -150,6 +155,14 @@ async function main(): Promise<void> {
console.log(`Initialized Aptos account: ${account.accountAddress.toStringLong()}`);
}

// Register BSV scheme if a BRC-100 wallet is enabled
if (bsvWalletEnabled) {
const bsvWallet = new WalletClient();
client.register("bsv:*", new ExactBsvScheme(bsvWallet));
const { publicKey: bsvIdentityKey } = await bsvWallet.getPublicKey({ identityKey: true });
console.log(`Initialized BSV identity: ${bsvIdentityKey}`);
}

// Register Concordium scheme if private key and address are provided
if (ccdPrivateKey && ccdAddress) {
const signer = {
Expand Down
2 changes: 2 additions & 0 deletions examples/typescript/clients/advanced/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@
},
"dependencies": {
"@aptos-labs/ts-sdk": "^5.2.1",
"@bsv/sdk": "^2.1.6",
"@keetanetwork/keetanet-client": "^0.18.1",
"@scure/base": "^1.2.6",
"@x402/aptos": "workspace:*",
"@x402/avm": "workspace:*",
"@x402/bsv": "workspace:*",
"@x402/concordium": "workspace:*",
"@x402/evm": "workspace:*",
"@x402/keeta": "workspace:*",
Expand Down
7 changes: 7 additions & 0 deletions examples/typescript/facilitator/advanced/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ and fill required environment variables:

- `APTOS_PRIVATE_KEY` - Aptos Ed25519 private key for fee payer (optional; `all-networks`)
- `APTOS_RPC_URL` - Aptos RPC URL (optional; `all-networks`)
- `BSV_SERVER_PRIVATE_KEY` - private key for the BSV server wallet that receives payments (optional; `all-networks`; settlement internalizes into this wallet)
- `BSV_WALLET_STORAGE_URL` - BSV wallet storage endpoint (optional; defaults to `https://store-us-1.bsvb.tech`)
- `BSV_NETWORK` - BSV network CAIP-2 (optional; defaults to `bsv:mainnet`; also `bsv:testnet`, `bsv:ttn`, `bsv:tstn`)
- `CCD_FACILITATOR_PRIVATE_KEY` - Concordium Ed25519 private key for sponsor signing (optional; `all-networks`)
- `CCD_FACILITATOR_ADDRESS` - Concordium sponsor account address (optional; `all-networks`)
- `CCD_NETWORK` - Concordium network CAIP-2 (optional; defaults to `ccd:4221332d34e1694168c2a0c0b3fd0f27`)
Expand Down Expand Up @@ -278,6 +281,10 @@ Networks use [CAIP-2](https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/cai
- `eip155:8453` — Base Mainnet
- `solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1` — Solana Devnet
- `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp` — Solana Mainnet
- `bsv:mainnet` — BSV Mainnet
- `bsv:testnet` — BSV Testnet
- `bsv:ttn` — BSV Teranode Test Net (Teratestnet)
- `bsv:tstn` — BSV Teranode Scaling Test Net
- `stellar:testnet` — Stellar Testnet
- `stellar:pubnet` — Stellar Mainnet
- `hedera:testnet` — Hedera Testnet
Expand Down
44 changes: 42 additions & 2 deletions examples/typescript/facilitator/advanced/all_networks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* optional chain configuration via environment variables.
*
* New chain support should be added here in alphabetic order by network prefix
* (e.g., "algorand" before "aptos" before "ccd" before "eip155" before "hedera" before "near" before "solana" before "stellar" before "tvm" before "xrpl").
* (e.g., "algorand" before "aptos" before "bsv" before "ccd" before "eip155" before "hedera" before "near" before "solana" before "stellar" before "tvm" before "xrpl").
*/

import {
Expand All @@ -15,10 +15,12 @@ import {
PrivateKeyVariants,
} from "@aptos-labs/ts-sdk";
import * as KeetaNet from "@keetanetwork/keetanet-client";
import { ServerWallet } from "@bsv/simple/server";
import { toFacilitatorAptosSigner } from "@x402/aptos";
import { ExactAptosScheme } from "@x402/aptos/exact/facilitator";
import { toFacilitatorAvmSigner } from "@x402/avm";
import { ExactAvmScheme } from "@x402/avm/exact/facilitator";
import { ExactBsvScheme } from "@x402/bsv/exact/facilitator";
import { ExactConcordiumScheme } from "@x402/concordium/exact/facilitator";
import {
CONCORDIUM_TESTNET_CAIP2,
Expand Down Expand Up @@ -90,6 +92,11 @@ const PORT = process.env.PORT || "4022";
const avmPrivateKey = process.env.AVM_PRIVATE_KEY as string | undefined;
const aptosPrivateKey = process.env.APTOS_PRIVATE_KEY as string | undefined;
const aptosRpcUrl = process.env.APTOS_RPC_URL as string | undefined;
// BSV settlement internalizes into the recipient's server-side BRC-100 wallet
// (@bsv/simple ServerWallet: private key + wallet storage endpoint).
const bsvServerPrivateKey = process.env.BSV_SERVER_PRIVATE_KEY as string | undefined;
const bsvWalletStorageUrl =
process.env.BSV_WALLET_STORAGE_URL || "https://store-us-1.bsvb.tech";
const ccdFacilitatorPrivateKey = process.env.CCD_FACILITATOR_PRIVATE_KEY as
| string
| undefined;
Expand Down Expand Up @@ -120,6 +127,7 @@ const xrplWsUrl = process.env.XRPL_WS_URL as string | undefined;
if (
!avmPrivateKey &&
!aptosPrivateKey &&
!bsvServerPrivateKey &&
!(ccdFacilitatorPrivateKey && ccdFacilitatorAddress) &&
!evmPrivateKey &&
!keetaMnemonic &&
Expand All @@ -130,14 +138,15 @@ if (
!(hederaAccountId && hederaPrivateKey)
) {
console.error(
"❌ At least one of AVM_PRIVATE_KEY, APTOS_PRIVATE_KEY, CCD_FACILITATOR_PRIVATE_KEY + CCD_FACILITATOR_ADDRESS, EVM_PRIVATE_KEY, KEETA_MNEMONIC, NEAR_RELAYER_ACCOUNT_ID + NEAR_RELAYER_PRIVATE_KEY, SVM_PRIVATE_KEY, STELLAR_PRIVATE_KEY, TVM_PRIVATE_KEY, or HEDERA_ACCOUNT_ID + HEDERA_PRIVATE_KEY is required",
"❌ At least one of AVM_PRIVATE_KEY, APTOS_PRIVATE_KEY, BSV_SERVER_PRIVATE_KEY, CCD_FACILITATOR_PRIVATE_KEY + CCD_FACILITATOR_ADDRESS, EVM_PRIVATE_KEY, KEETA_MNEMONIC, NEAR_RELAYER_ACCOUNT_ID + NEAR_RELAYER_PRIVATE_KEY, SVM_PRIVATE_KEY, STELLAR_PRIVATE_KEY, TVM_PRIVATE_KEY, or HEDERA_ACCOUNT_ID + HEDERA_PRIVATE_KEY is required",
);
process.exit(1);
}

// Network configuration (alphabetic order)
const AVM_NETWORK = "algorand:SGO1GKSzyE7IEPItTxCByw9x8FmnrCDe"; // Algorand Testnet
const APTOS_NETWORK = (process.env.APTOS_NETWORK || "aptos:2") as Network; // Aptos Testnet
const BSV_NETWORK = (process.env.BSV_NETWORK || "bsv:mainnet") as Network; // BSV Mainnet
const CCD_NETWORK = CONCORDIUM_TESTNET_CAIP2; // Concordium Testnet
const EVM_NETWORK = "eip155:84532"; // Base Sepolia
const HEDERA_NETWORK = "hedera:testnet"; // Hedera Testnet
Expand Down Expand Up @@ -195,6 +204,37 @@ if (aptosPrivateKey) {
);
}

// Register BSV scheme if a server wallet key is provided.
// The facilitator settles by internalizing payments into this wallet, so it
// must be the RECIPIENT's wallet — payTo is its identity key.
if (bsvServerPrivateKey) {
// Map CAIP-2 (bsv:*) to ServerWallet network names. Refuse unknown ids —
// do not default bip122/genesis references to BSV.
const bsvWalletNetwork =
BSV_NETWORK === "bsv:mainnet"
? "main"
: BSV_NETWORK === "bsv:testnet"
? "testnet"
: BSV_NETWORK === "bsv:ttn"
? "ttn"
: BSV_NETWORK === "bsv:tstn"
? "tstn"
: undefined;
if (!bsvWalletNetwork) {
throw new Error(
`Unsupported BSV_NETWORK=${BSV_NETWORK}; use bsv:mainnet|bsv:testnet|bsv:ttn|bsv:tstn`,
);
}
const bsvWallet = await ServerWallet.create({
privateKey: bsvServerPrivateKey,
network: bsvWalletNetwork,
storageUrl: bsvWalletStorageUrl,
});
const bsvScheme = await ExactBsvScheme.create({ wallet: bsvWallet.getClient() });
console.info(`BSV Facilitator identity: ${bsvScheme.getSigners(BSV_NETWORK)[0]}`);
facilitator.register(BSV_NETWORK, bsvScheme);
}

// Register Concordium scheme if private key + address are provided (recommended).
// This matches how every other mechanism reads a private key from an env var.
if (ccdFacilitatorPrivateKey && ccdFacilitatorAddress) {
Expand Down
2 changes: 2 additions & 0 deletions examples/typescript/facilitator/advanced/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@
},
"dependencies": {
"@aptos-labs/ts-sdk": "^5.2.1",
"@bsv/simple": "^0.4.0",
"@keetanetwork/keetanet-client": "^0.18.1",
"@scure/base": "^1.2.6",
"@solana/kit": "^6.1.0",
"@x402/aptos": "workspace:*",
"@x402/avm": "workspace:*",
"@x402/bsv": "workspace:*",
"@x402/concordium": "workspace:*",
"@x402/core": "workspace:*",
"@x402/evm": "workspace:*",
Expand Down
Loading
Loading