@erudite-intelligence/x402-btc
v1.0.0
Published
x402 Payment Protocol V2 - Bitcoin Network Plugin. PSBT-based BTC payments for AI agents and web services.
Downloads
11
Maintainers
Readme
@erudite-intelligence/x402-btc
x402 Payment Protocol V2 — Bitcoin Network Plugin
The first x402 V2 implementation for Bitcoin (BTC), enabling PSBT-based native Bitcoin payments for AI agents and web services.
Built by Erudite Intelligence LLC.
Overview
This package implements the exact payment scheme for Bitcoin using BIP-174 Partially Signed Bitcoin Transactions (PSBTs). It provides client-side payment creation, server-side verification with 11 security checks, and settlement via transaction broadcasting.
Key features:
- Native Bitcoin payments (no tokens, no wrapped assets)
- PSBT-based — strongest trust model in x402 (facilitator cannot alter any parameter)
- Client pays miner fees (embedded in transaction)
- Supports mainnet, testnet, and signet
- CAIP-2 / BIP-122 network identifiers
- Esplora-compatible API client (Blockstream & Mempool.space)
- 191 tests across 5 test suites including adversarial, stress, and exploit regression testing
- Security hardened through 7 rounds of adversarial auditing (15 vulnerabilities found and fixed)
Address support: This package enforces native segwit (P2WPKH /
bc1q…) addresses only. Legacy (P2PKH/P2SH) and taproot (P2TR) addresses are not currently supported.
Installation
npm install @erudite-intelligence/x402-btcQuick Start
Client: Create a Payment
import { createPayment, encodePaymentHeader } from "@erudite-intelligence/x402-btc";
const payment = await createPayment({
privateKey: "YOUR_PRIVATE_KEY_HEX",
to: "tb1q_MERCHANT_ADDRESS",
amount: "50000", // satoshis
network: "testnet",
});
// Send as X-PAYMENT header
const header = encodePaymentHeader(payment);
fetch(resourceUrl, {
headers: { "X-PAYMENT": header },
});Client: Pay from a 402 Response
import { payFor, encodePaymentHeader } from "@erudite-intelligence/x402-btc";
// Parse the 402 response body
const paymentRequired = await response.json();
const payment = await payFor(paymentRequired, "YOUR_PRIVATE_KEY");
if (payment) {
const header = encodePaymentHeader(payment);
// Retry the request with payment
}Server: Verify a Payment
import { verifyPayment } from "@erudite-intelligence/x402-btc";
const result = await verifyPayment(
paymentPayload, // from X-PAYMENT header
"tb1q_MERCHANT_ADDR", // expected payTo
"50000", // expected amount in satoshis
"bip122:000000000933ea01ad0ee984209779ba", // CAIP-2 network
{ network: "testnet" },
);
if (result.isValid) {
console.log(`Payment verified from ${result.payer}`);
console.log(`Fee rate: ${result.details.feeRate} sat/vB`);
} else {
console.log(`Rejected: ${result.invalidReason} - ${result.invalidMessage}`);
}Server: Settle (Broadcast) a Payment
import { settlePayment } from "@erudite-intelligence/x402-btc";
const result = await settlePayment(
paymentPayload,
"bip122:000000000933ea01ad0ee984209779ba",
{ network: "testnet" },
);
if (result.success) {
console.log(`Settled: ${result.transaction}`);
} else {
console.log(`Failed: ${result.errorReason}`);
}Register with x402 Facilitator
import { registerExactBtcScheme } from "@erudite-intelligence/x402-btc";
registerExactBtcScheme(facilitatorServer, {
network: "mainnet",
requiredConfirmations: 0, // 0 = mempool acceptance
maxFeeRate: 500, // sat/vB safety cap
});Networks
| Network | CAIP-2 Identifier |
|---------|------------------|
| Mainnet | bip122:000000000019d6689c085ae165831e93 |
| Testnet | bip122:000000000933ea01ad0ee984209779ba |
| Signet | bip122:00000008819873e925422c1ff0f99f7c |
Verification Checks
The facilitator runs 11 verification checks on every PSBT, plus additional security hardening:
- PSBT format validity — BIP-174 magic bytes and structure
- Signature completeness — all inputs fully signed and finalizable 2b. Cryptographic signature verification — ECDSA sighash + pubkey-to-script match + BIP-146 Low-S + SIGHASH_ALL enforcement + CLEANSTACK (exactly 2 witness items)
- Payment output verification — payTo address and exact amount match (order-independent)
- UTXO existence — inputs reference real, unspent outputs with on-chain value and script verification 4b. Duplicate input detection — no UTXO referenced more than once
- No mempool conflicts — inputs not already spent
- Miner fee adequacy — positive fee within min/max rate bounds
- Amount conservation — total inputs ≥ total outputs
- Timelock checks — nLockTime must be 0, no active BIP-68 relative locks (sequence=0 allowed)
- Transaction size — within relay weight limits
- Network correctness — CAIP-2 identifier matches expected network
- Dust output check — all outputs above dust thresholds (OP_RETURN exempt)
Trust Model
Bitcoin PSBT has the strongest trust model in x402:
- Client constructs and fully signs the transaction
- Facilitator cannot modify any parameter (inputs, outputs, amounts, fee)
- Any modification invalidates all input signatures
- Facilitator can only broadcast or discard — nothing else
Configuration
interface BtcSchemeConfig {
network?: "mainnet" | "testnet" | "signet";
apiProvider?: "blockstream" | "mempool" | string;
apiBaseUrl?: string; // custom Esplora-compatible endpoint
requiredConfirmations?: number; // 0 = mempool, 1+ = block confirmations
maxFeeRate?: number; // sat/vB safety cap (default: 1000)
minFeeRate?: number; // sat/vB minimum (default: 1)
apiTimeout?: number; // ms (default: 30000)
}API Client
The package includes a full Esplora-compatible API client:
import {
getUTXOs,
getRawTransaction,
isUTXOUnspent,
getUTXOValue,
broadcastTransaction,
getRecommendedFeeRate,
getTransactionStatus,
} from "@erudite-intelligence/x402-btc";
const utxos = await getUTXOs("tb1q...", { network: "testnet" });
const feeRate = await getRecommendedFeeRate(6, { network: "testnet" });
const utxoData = await getUTXOValue(txid, vout, { network: "testnet" });
// Returns: { value: number, spent: boolean, scriptPubKey: string }Testing
npm test
# Runs 5 test suites:
# verify.test.js - 22 core verification tests
# adversarial.test.js - 53 adversarial/attack tests
# client-roundtrip.test.js - 48 client + round-trip tests
# kitchen-sink.test.js - 40 edge case + stress tests
# exploit-regression.test.js - 28 exploit regression tests (14 attack vectors)
# Total: 191 testsSecurity
This package was hardened through 7 rounds of adversarial security auditing. The following attack vectors are covered by permanent regression tests:
| # | Attack Vector | Fix |
|---|---|---|
| 1 | Fake/garbage signatures | Full ECDSA verification (sighash + pubkey + DER decode) |
| 2 | UTXO API fail-open | Fail-closed by default |
| 3 | Phantom funds (fake input value) | On-chain value comparison |
| 4 | Script spoofing (fake scriptPubKey) | On-chain scriptPubKey comparison |
| 5 | High-S signatures (BIP-146) | ecc.verify(..., true) strict mode |
| 6 | CLEANSTACK violation | witness.length !== 2 enforcement |
| 7 | SIGHASH_NONE / SINGLE / ANYONECANPAY | hashType !== 0x01 enforcement |
| 8 | Duplicate inputs (double-counting) | Outpoint Set deduplication |
| 9 | Block-height nLockTime bypass | nLockTime > 0 rejection |
| 10 | sequence=0 false rejection | BIP-68 lower-31-bits check |
| 11 | Payment output order-dependence | Full scan for exact match |
| 12 | P2WSH/non-P2WPKH script bypass | P2WPKH-only enforcement |
| 13 | Type confusion on amount | Strict regex + type check |
| 14 | Wrong-key witness injection | HASH160(pubkey) vs script match |
Production note: Replay protection uses an in-memory Map. Multi-instance deployments must replace this with Redis SETEX or a database with TTL.
Related Packages
- @erudite-intelligence/x402-tron — x402 for Tron (TRC-20 USDT)
- @erudite-intelligence/x402-tron-v2 — x402 V2 for Tron
License
MIT — Erudite Intelligence LLC
