@t402/btc 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/README.md +120 -0
  2. package/dist/cjs/exact/client/index.d.ts +89 -0
  3. package/dist/cjs/exact/client/index.js +145 -0
  4. package/dist/cjs/exact/client/index.js.map +1 -0
  5. package/dist/cjs/exact/facilitator/index.d.ts +114 -0
  6. package/dist/cjs/exact/facilitator/index.js +218 -0
  7. package/dist/cjs/exact/facilitator/index.js.map +1 -0
  8. package/dist/cjs/exact/server/index.d.ts +101 -0
  9. package/dist/cjs/exact/server/index.js +161 -0
  10. package/dist/cjs/exact/server/index.js.map +1 -0
  11. package/dist/cjs/index.d.ts +179 -0
  12. package/dist/cjs/index.js +849 -0
  13. package/dist/cjs/index.js.map +1 -0
  14. package/dist/cjs/lightning/client/index.d.ts +82 -0
  15. package/dist/cjs/lightning/client/index.js +114 -0
  16. package/dist/cjs/lightning/client/index.js.map +1 -0
  17. package/dist/cjs/lightning/facilitator/index.d.ts +93 -0
  18. package/dist/cjs/lightning/facilitator/index.js +211 -0
  19. package/dist/cjs/lightning/facilitator/index.js.map +1 -0
  20. package/dist/cjs/lightning/server/index.d.ts +96 -0
  21. package/dist/cjs/lightning/server/index.js +157 -0
  22. package/dist/cjs/lightning/server/index.js.map +1 -0
  23. package/dist/cjs/signer-B_Z4WGLa.d.ts +64 -0
  24. package/dist/esm/chunk-2DEKJ7ER.mjs +123 -0
  25. package/dist/esm/chunk-2DEKJ7ER.mjs.map +1 -0
  26. package/dist/esm/chunk-3IOPLDQH.mjs +74 -0
  27. package/dist/esm/chunk-3IOPLDQH.mjs.map +1 -0
  28. package/dist/esm/chunk-7IU3Z36R.mjs +103 -0
  29. package/dist/esm/chunk-7IU3Z36R.mjs.map +1 -0
  30. package/dist/esm/chunk-HNFWDITA.mjs +170 -0
  31. package/dist/esm/chunk-HNFWDITA.mjs.map +1 -0
  32. package/dist/esm/chunk-MX3PAUPJ.mjs +65 -0
  33. package/dist/esm/chunk-MX3PAUPJ.mjs.map +1 -0
  34. package/dist/esm/chunk-YJYTK2QQ.mjs +127 -0
  35. package/dist/esm/chunk-YJYTK2QQ.mjs.map +1 -0
  36. package/dist/esm/chunk-YWZC2RR7.mjs +38 -0
  37. package/dist/esm/chunk-YWZC2RR7.mjs.map +1 -0
  38. package/dist/esm/chunk-ZOL5R3HZ.mjs +177 -0
  39. package/dist/esm/chunk-ZOL5R3HZ.mjs.map +1 -0
  40. package/dist/esm/exact/client/index.d.mts +89 -0
  41. package/dist/esm/exact/client/index.mjs +11 -0
  42. package/dist/esm/exact/client/index.mjs.map +1 -0
  43. package/dist/esm/exact/facilitator/index.d.mts +114 -0
  44. package/dist/esm/exact/facilitator/index.mjs +11 -0
  45. package/dist/esm/exact/facilitator/index.mjs.map +1 -0
  46. package/dist/esm/exact/server/index.d.mts +101 -0
  47. package/dist/esm/exact/server/index.mjs +10 -0
  48. package/dist/esm/exact/server/index.mjs.map +1 -0
  49. package/dist/esm/index.d.mts +179 -0
  50. package/dist/esm/index.mjs +77 -0
  51. package/dist/esm/index.mjs.map +1 -0
  52. package/dist/esm/lightning/client/index.d.mts +82 -0
  53. package/dist/esm/lightning/client/index.mjs +11 -0
  54. package/dist/esm/lightning/client/index.mjs.map +1 -0
  55. package/dist/esm/lightning/facilitator/index.d.mts +93 -0
  56. package/dist/esm/lightning/facilitator/index.mjs +11 -0
  57. package/dist/esm/lightning/facilitator/index.mjs.map +1 -0
  58. package/dist/esm/lightning/server/index.d.mts +96 -0
  59. package/dist/esm/lightning/server/index.mjs +10 -0
  60. package/dist/esm/lightning/server/index.mjs.map +1 -0
  61. package/dist/esm/signer-B_Z4WGLa.d.mts +64 -0
  62. package/package.json +142 -0
@@ -0,0 +1,218 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
21
+
22
+ // src/exact/facilitator/index.ts
23
+ var facilitator_exports = {};
24
+ __export(facilitator_exports, {
25
+ ExactBtcScheme: () => ExactBtcScheme,
26
+ registerExactBtcScheme: () => registerExactBtcScheme
27
+ });
28
+ module.exports = __toCommonJS(facilitator_exports);
29
+
30
+ // src/constants.ts
31
+ var BTC_MAINNET = "bip122:000000000019d6689c085ae165831e93";
32
+ var BTC_TESTNET = "bip122:000000000933ea01ad0ee984209779ba";
33
+ var LIGHTNING_MAINNET = "lightning:mainnet";
34
+ var LIGHTNING_TESTNET = "lightning:testnet";
35
+ var BTC_NETWORKS = [BTC_MAINNET, BTC_TESTNET];
36
+ var LIGHTNING_NETWORKS = [LIGHTNING_MAINNET, LIGHTNING_TESTNET];
37
+ var ALL_NETWORKS = [...BTC_NETWORKS, ...LIGHTNING_NETWORKS];
38
+ var DUST_LIMIT = 546;
39
+ var SCHEME_EXACT = "exact";
40
+ var MAINNET_ADDRESS_PREFIXES = ["bc1", "1", "3"];
41
+ var TESTNET_ADDRESS_PREFIXES = ["tb1", "m", "n", "2"];
42
+
43
+ // src/utils.ts
44
+ function validateBitcoinAddress(address) {
45
+ if (!address || address.length < 14 || address.length > 90) {
46
+ return false;
47
+ }
48
+ const allPrefixes = [...MAINNET_ADDRESS_PREFIXES, ...TESTNET_ADDRESS_PREFIXES];
49
+ return allPrefixes.some((prefix) => address.startsWith(prefix));
50
+ }
51
+
52
+ // src/exact/facilitator/scheme.ts
53
+ var ExactBtcScheme = class {
54
+ constructor(signer) {
55
+ this.signer = signer;
56
+ __publicField(this, "scheme", SCHEME_EXACT);
57
+ __publicField(this, "caipFamily", "bip122:*");
58
+ }
59
+ /**
60
+ * Get mechanism-specific extra data for the supported kinds endpoint.
61
+ * Bitcoin on-chain has no extra data.
62
+ */
63
+ getExtra(_network) {
64
+ return void 0;
65
+ }
66
+ /**
67
+ * Get signer addresses used by this facilitator.
68
+ */
69
+ getSigners(_network) {
70
+ return [...this.signer.getAddresses()];
71
+ }
72
+ /**
73
+ * Verifies a payment payload.
74
+ *
75
+ * Validates:
76
+ * 1. Scheme and network matching
77
+ * 2. PSBT structure and signatures
78
+ * 3. Output matches (payTo, amount)
79
+ * 4. Amount above dust limit
80
+ */
81
+ async verify(payload, requirements) {
82
+ const btcPayload = payload.payload;
83
+ if (!btcPayload?.signedPsbt) {
84
+ return {
85
+ isValid: false,
86
+ invalidReason: "invalid_payload_structure",
87
+ payer: void 0
88
+ };
89
+ }
90
+ if (payload.accepted.scheme !== SCHEME_EXACT || requirements.scheme !== SCHEME_EXACT) {
91
+ return {
92
+ isValid: false,
93
+ invalidReason: "unsupported_scheme",
94
+ payer: void 0
95
+ };
96
+ }
97
+ const validNetworks = BTC_NETWORKS;
98
+ if (!validNetworks.includes(requirements.network)) {
99
+ return {
100
+ isValid: false,
101
+ invalidReason: "unsupported_network",
102
+ payer: void 0
103
+ };
104
+ }
105
+ if (payload.accepted.network !== requirements.network) {
106
+ return {
107
+ isValid: false,
108
+ invalidReason: "network_mismatch",
109
+ payer: void 0
110
+ };
111
+ }
112
+ if (!validateBitcoinAddress(requirements.payTo)) {
113
+ return {
114
+ isValid: false,
115
+ invalidReason: "invalid_pay_to_address",
116
+ payer: void 0
117
+ };
118
+ }
119
+ if (BigInt(requirements.amount) < BigInt(DUST_LIMIT)) {
120
+ return {
121
+ isValid: false,
122
+ invalidReason: "amount_below_dust_limit",
123
+ payer: void 0
124
+ };
125
+ }
126
+ try {
127
+ const result = await this.signer.verifyPsbt({
128
+ signedPsbt: btcPayload.signedPsbt,
129
+ expectedPayTo: requirements.payTo,
130
+ expectedAmount: requirements.amount
131
+ });
132
+ if (!result.valid) {
133
+ return {
134
+ isValid: false,
135
+ invalidReason: result.reason || "psbt_verification_failed",
136
+ payer: result.payer
137
+ };
138
+ }
139
+ return {
140
+ isValid: true,
141
+ invalidReason: void 0,
142
+ payer: result.payer
143
+ };
144
+ } catch (error) {
145
+ const errorMessage = error instanceof Error ? error.message : String(error);
146
+ return {
147
+ isValid: false,
148
+ invalidReason: `verification_error: ${errorMessage}`,
149
+ payer: void 0
150
+ };
151
+ }
152
+ }
153
+ /**
154
+ * Settles a payment by finalizing and broadcasting the PSBT.
155
+ */
156
+ async settle(payload, requirements) {
157
+ const btcPayload = payload.payload;
158
+ if (!btcPayload?.signedPsbt) {
159
+ return {
160
+ success: false,
161
+ network: payload.accepted.network,
162
+ transaction: "",
163
+ errorReason: "invalid_payload_structure",
164
+ payer: void 0
165
+ };
166
+ }
167
+ const verifyResult = await this.verify(payload, requirements);
168
+ if (!verifyResult.isValid) {
169
+ return {
170
+ success: false,
171
+ network: payload.accepted.network,
172
+ transaction: "",
173
+ errorReason: verifyResult.invalidReason ?? "verification_failed",
174
+ payer: verifyResult.payer
175
+ };
176
+ }
177
+ try {
178
+ const txId = await this.signer.broadcastPsbt(btcPayload.signedPsbt);
179
+ const confirmation = await this.signer.waitForConfirmation(txId, 1);
180
+ if (!confirmation.confirmed) {
181
+ return {
182
+ success: false,
183
+ errorReason: "transaction_not_confirmed",
184
+ transaction: txId,
185
+ network: payload.accepted.network,
186
+ payer: verifyResult.payer
187
+ };
188
+ }
189
+ return {
190
+ success: true,
191
+ transaction: txId,
192
+ network: payload.accepted.network,
193
+ payer: verifyResult.payer
194
+ };
195
+ } catch (error) {
196
+ console.error("Failed to settle Bitcoin transaction:", error);
197
+ return {
198
+ success: false,
199
+ errorReason: "transaction_failed",
200
+ transaction: "",
201
+ network: payload.accepted.network,
202
+ payer: verifyResult.payer
203
+ };
204
+ }
205
+ }
206
+ };
207
+
208
+ // src/exact/facilitator/register.ts
209
+ function registerExactBtcScheme(facilitator, config) {
210
+ facilitator.register(config.networks, new ExactBtcScheme(config.signer));
211
+ return facilitator;
212
+ }
213
+ // Annotate the CommonJS export names for ESM import in node:
214
+ 0 && (module.exports = {
215
+ ExactBtcScheme,
216
+ registerExactBtcScheme
217
+ });
218
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/exact/facilitator/index.ts","../../../../src/constants.ts","../../../../src/utils.ts","../../../../src/exact/facilitator/scheme.ts","../../../../src/exact/facilitator/register.ts"],"sourcesContent":["export { ExactBtcScheme } from './scheme.js'\nexport type { FacilitatorBtcSigner } from './scheme.js'\nexport { registerExactBtcScheme } from './register.js'\nexport type { BtcFacilitatorConfig } from './register.js'\n","/**\n * Bitcoin & Lightning Network Constants\n *\n * CAIP-2 network identifiers, dust limits, and fee constants.\n */\n\n/**\n * CAIP-2 Network Identifiers for Bitcoin\n * Uses BIP-122 chain genesis block hashes\n */\nexport const BTC_MAINNET = 'bip122:000000000019d6689c085ae165831e93'\nexport const BTC_TESTNET = 'bip122:000000000933ea01ad0ee984209779ba'\n\n/**\n * CAIP-2 Network Identifiers for Lightning Network\n */\nexport const LIGHTNING_MAINNET = 'lightning:mainnet'\nexport const LIGHTNING_TESTNET = 'lightning:testnet'\n\n/**\n * All supported BTC on-chain networks\n */\nexport const BTC_NETWORKS = [BTC_MAINNET, BTC_TESTNET] as const\n\n/**\n * All supported Lightning networks\n */\nexport const LIGHTNING_NETWORKS = [LIGHTNING_MAINNET, LIGHTNING_TESTNET] as const\n\n/**\n * All supported networks (on-chain + Lightning)\n */\nexport const ALL_NETWORKS = [...BTC_NETWORKS, ...LIGHTNING_NETWORKS] as const\n\nexport type BtcNetwork = (typeof BTC_NETWORKS)[number]\nexport type LightningNetwork = (typeof LIGHTNING_NETWORKS)[number]\n\n/**\n * Dust limit in satoshis - minimum viable output value\n * Outputs below this threshold are rejected by Bitcoin nodes\n */\nexport const DUST_LIMIT = 546\n\n/**\n * Minimum relay fee in satoshis\n * Transactions with fees below this are not relayed by default\n */\nexport const MIN_RELAY_FEE = 1000\n\n/**\n * Satoshis per BTC\n */\nexport const SATS_PER_BTC = 100_000_000\n\n/**\n * Scheme identifiers\n */\nexport const SCHEME_EXACT = 'exact'\n\n/**\n * Default timeout for payment validity (in seconds)\n */\nexport const DEFAULT_VALIDITY_DURATION = 3600 // 1 hour\n\n/**\n * Bitcoin address prefixes for basic validation\n */\nexport const MAINNET_ADDRESS_PREFIXES = ['bc1', '1', '3']\nexport const TESTNET_ADDRESS_PREFIXES = ['tb1', 'm', 'n', '2']\n","/**\n * Bitcoin & Lightning Utility Functions\n *\n * Helper functions for address validation, unit conversion,\n * and invoice validation.\n */\n\nimport { SATS_PER_BTC, MAINNET_ADDRESS_PREFIXES, TESTNET_ADDRESS_PREFIXES } from './constants.js'\n\n/**\n * Convert satoshis to BTC\n *\n * @param sats - Amount in satoshis\n * @returns Amount in BTC as string (to avoid floating point issues)\n */\nexport function satoshisToBtc(sats: bigint | number | string): string {\n const satsBigInt = BigInt(sats)\n const whole = satsBigInt / BigInt(SATS_PER_BTC)\n const frac = satsBigInt % BigInt(SATS_PER_BTC)\n\n if (frac === 0n) {\n return whole.toString()\n }\n\n const fracStr = frac.toString().padStart(8, '0')\n return `${whole}.${fracStr}`.replace(/\\.?0+$/, '')\n}\n\n/**\n * Convert BTC to satoshis\n *\n * @param btc - Amount in BTC (string or number)\n * @returns Amount in satoshis as bigint\n */\nexport function btcToSatoshis(btc: string | number): bigint {\n const btcStr = typeof btc === 'number' ? btc.toString() : btc\n const [wholePart, fracPart = ''] = btcStr.split('.')\n const paddedFrac = fracPart.padEnd(8, '0').slice(0, 8)\n const combined = wholePart + paddedFrac\n const result = BigInt(combined.replace(/^0+/, '') || '0')\n return result\n}\n\n/**\n * Validate a Bitcoin address (basic format validation)\n *\n * Checks address prefix against known formats:\n * - Mainnet: bc1 (bech32), 1 (P2PKH), 3 (P2SH)\n * - Testnet: tb1 (bech32), m/n (P2PKH), 2 (P2SH)\n *\n * @param address - Bitcoin address to validate\n * @returns true if the address has a valid format\n */\nexport function validateBitcoinAddress(address: string): boolean {\n if (!address || address.length < 14 || address.length > 90) {\n return false\n }\n\n const allPrefixes = [...MAINNET_ADDRESS_PREFIXES, ...TESTNET_ADDRESS_PREFIXES]\n return allPrefixes.some((prefix) => address.startsWith(prefix))\n}\n\n/**\n * Check if a Bitcoin address is for mainnet\n *\n * @param address - Bitcoin address\n * @returns true if mainnet address\n */\nexport function isMainnetAddress(address: string): boolean {\n return MAINNET_ADDRESS_PREFIXES.some((prefix) => address.startsWith(prefix))\n}\n\n/**\n * Check if a Bitcoin address is for testnet\n *\n * @param address - Bitcoin address\n * @returns true if testnet address\n */\nexport function isTestnetAddress(address: string): boolean {\n return TESTNET_ADDRESS_PREFIXES.some((prefix) => address.startsWith(prefix))\n}\n\n/**\n * Validate a BOLT11 Lightning invoice (basic format validation)\n *\n * BOLT11 invoices follow the format:\n * - lnbc... for mainnet\n * - lntb... for testnet\n * - lnbcrt... for regtest\n *\n * @param invoice - BOLT11 invoice string\n * @returns true if the invoice has a valid format\n */\nexport function validateBolt11Invoice(invoice: string): boolean {\n if (!invoice || invoice.length < 20) {\n return false\n }\n\n const lower = invoice.toLowerCase()\n return lower.startsWith('lnbc') || lower.startsWith('lntb') || lower.startsWith('lnbcrt')\n}\n\n/**\n * Validate a hex-encoded string\n *\n * @param hex - String to validate\n * @param expectedLength - Expected byte length (hex length / 2)\n * @returns true if valid hex of expected length\n */\nexport function isValidHex(hex: string, expectedLength?: number): boolean {\n if (!/^[0-9a-fA-F]+$/.test(hex)) {\n return false\n }\n if (expectedLength !== undefined && hex.length !== expectedLength * 2) {\n return false\n }\n return true\n}\n","/**\n * Bitcoin On-chain Facilitator Scheme Implementation\n *\n * Verifies and settles Bitcoin on-chain payments using the exact scheme.\n * Validates signed PSBTs and broadcasts transactions to the Bitcoin network.\n */\n\nimport type {\n PaymentPayload,\n PaymentRequirements,\n SchemeNetworkFacilitator,\n SettleResponse,\n VerifyResponse,\n} from '@t402/core/types'\nimport type { BtcOnchainPayload } from '../../types.js'\nimport { SCHEME_EXACT, DUST_LIMIT, BTC_NETWORKS } from '../../constants.js'\nimport { validateBitcoinAddress } from '../../utils.js'\n\n/**\n * Facilitator BTC signer interface for verify and settle operations\n */\nexport interface FacilitatorBtcSigner {\n /**\n * Get all addresses this facilitator can use\n */\n getAddresses(): readonly string[]\n\n /**\n * Verify a signed PSBT\n * Checks that outputs match expected values and signatures are valid\n *\n * @param params - Verification parameters\n * @returns Verification result\n */\n verifyPsbt(params: {\n signedPsbt: string\n expectedPayTo: string\n expectedAmount: string\n }): Promise<{\n valid: boolean\n reason?: string\n payer?: string\n }>\n\n /**\n * Finalize and broadcast a signed PSBT\n *\n * @param signedPsbt - Base64-encoded signed PSBT\n * @returns Transaction ID\n */\n broadcastPsbt(signedPsbt: string): Promise<string>\n\n /**\n * Wait for a transaction to be confirmed\n *\n * @param txId - Transaction ID\n * @param confirmations - Number of confirmations to wait for\n * @returns Confirmation result\n */\n waitForConfirmation(\n txId: string,\n confirmations?: number,\n ): Promise<{\n confirmed: boolean\n txId: string\n blockHash?: string\n confirmations: number\n }>\n}\n\n/**\n * Bitcoin facilitator implementation for the Exact payment scheme.\n *\n * Verifies signed PSBTs and broadcasts transactions to settle payments.\n */\nexport class ExactBtcScheme implements SchemeNetworkFacilitator {\n readonly scheme = SCHEME_EXACT\n readonly caipFamily = 'bip122:*'\n\n constructor(private readonly signer: FacilitatorBtcSigner) {}\n\n /**\n * Get mechanism-specific extra data for the supported kinds endpoint.\n * Bitcoin on-chain has no extra data.\n */\n getExtra(_network: string): Record<string, unknown> | undefined {\n return undefined\n }\n\n /**\n * Get signer addresses used by this facilitator.\n */\n getSigners(_network: string): string[] {\n return [...this.signer.getAddresses()]\n }\n\n /**\n * Verifies a payment payload.\n *\n * Validates:\n * 1. Scheme and network matching\n * 2. PSBT structure and signatures\n * 3. Output matches (payTo, amount)\n * 4. Amount above dust limit\n */\n async verify(\n payload: PaymentPayload,\n requirements: PaymentRequirements,\n ): Promise<VerifyResponse> {\n const btcPayload = payload.payload as BtcOnchainPayload | undefined\n\n // Validate payload structure\n if (!btcPayload?.signedPsbt) {\n return {\n isValid: false,\n invalidReason: 'invalid_payload_structure',\n payer: undefined,\n }\n }\n\n // Verify scheme matches\n if (payload.accepted.scheme !== SCHEME_EXACT || requirements.scheme !== SCHEME_EXACT) {\n return {\n isValid: false,\n invalidReason: 'unsupported_scheme',\n payer: undefined,\n }\n }\n\n // Verify network is a valid BTC network\n const validNetworks: readonly string[] = BTC_NETWORKS\n if (!validNetworks.includes(requirements.network)) {\n return {\n isValid: false,\n invalidReason: 'unsupported_network',\n payer: undefined,\n }\n }\n\n // Verify network matches\n if (payload.accepted.network !== requirements.network) {\n return {\n isValid: false,\n invalidReason: 'network_mismatch',\n payer: undefined,\n }\n }\n\n // Validate payTo address\n if (!validateBitcoinAddress(requirements.payTo)) {\n return {\n isValid: false,\n invalidReason: 'invalid_pay_to_address',\n payer: undefined,\n }\n }\n\n // Validate amount above dust limit\n if (BigInt(requirements.amount) < BigInt(DUST_LIMIT)) {\n return {\n isValid: false,\n invalidReason: 'amount_below_dust_limit',\n payer: undefined,\n }\n }\n\n // Verify the PSBT\n try {\n const result = await this.signer.verifyPsbt({\n signedPsbt: btcPayload.signedPsbt,\n expectedPayTo: requirements.payTo,\n expectedAmount: requirements.amount,\n })\n\n if (!result.valid) {\n return {\n isValid: false,\n invalidReason: result.reason || 'psbt_verification_failed',\n payer: result.payer,\n }\n }\n\n return {\n isValid: true,\n invalidReason: undefined,\n payer: result.payer,\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n return {\n isValid: false,\n invalidReason: `verification_error: ${errorMessage}`,\n payer: undefined,\n }\n }\n }\n\n /**\n * Settles a payment by finalizing and broadcasting the PSBT.\n */\n async settle(\n payload: PaymentPayload,\n requirements: PaymentRequirements,\n ): Promise<SettleResponse> {\n const btcPayload = payload.payload as BtcOnchainPayload | undefined\n\n if (!btcPayload?.signedPsbt) {\n return {\n success: false,\n network: payload.accepted.network,\n transaction: '',\n errorReason: 'invalid_payload_structure',\n payer: undefined,\n }\n }\n\n // Re-verify before settling\n const verifyResult = await this.verify(payload, requirements)\n if (!verifyResult.isValid) {\n return {\n success: false,\n network: payload.accepted.network,\n transaction: '',\n errorReason: verifyResult.invalidReason ?? 'verification_failed',\n payer: verifyResult.payer,\n }\n }\n\n try {\n // Broadcast the signed transaction\n const txId = await this.signer.broadcastPsbt(btcPayload.signedPsbt)\n\n // Wait for at least 1 confirmation\n const confirmation = await this.signer.waitForConfirmation(txId, 1)\n\n if (!confirmation.confirmed) {\n return {\n success: false,\n errorReason: 'transaction_not_confirmed',\n transaction: txId,\n network: payload.accepted.network,\n payer: verifyResult.payer,\n }\n }\n\n return {\n success: true,\n transaction: txId,\n network: payload.accepted.network,\n payer: verifyResult.payer,\n }\n } catch (error) {\n console.error('Failed to settle Bitcoin transaction:', error)\n return {\n success: false,\n errorReason: 'transaction_failed',\n transaction: '',\n network: payload.accepted.network,\n payer: verifyResult.payer,\n }\n }\n }\n}\n","import { t402Facilitator } from '@t402/core/facilitator'\nimport { Network } from '@t402/core/types'\nimport { FacilitatorBtcSigner, ExactBtcScheme } from './scheme.js'\n\n/**\n * Configuration options for registering BTC schemes to an t402Facilitator\n */\nexport interface BtcFacilitatorConfig {\n /**\n * The Bitcoin signer for facilitator operations (verify and settle)\n */\n signer: FacilitatorBtcSigner\n\n /**\n * Networks to register (single network or array of networks)\n * Examples: \"bip122:000000000019d6689c085ae165831e93\"\n */\n networks: Network | Network[]\n}\n\n/**\n * Registers Bitcoin exact payment schemes to an t402Facilitator instance.\n *\n * @param facilitator - The t402Facilitator instance to register schemes to\n * @param config - Configuration for BTC facilitator registration\n * @returns The facilitator instance for chaining\n */\nexport function registerExactBtcScheme(\n facilitator: t402Facilitator,\n config: BtcFacilitatorConfig,\n): t402Facilitator {\n facilitator.register(config.networks, new ExactBtcScheme(config.signer))\n return facilitator\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,101 @@
1
+ import { SchemeNetworkServer, MoneyParser, Price, Network, AssetAmount, PaymentRequirements } from '@t402/core/types';
2
+ import { t402ResourceServer } from '@t402/core/server';
3
+
4
+ /**
5
+ * Bitcoin On-chain Server Scheme Implementation
6
+ *
7
+ * Handles price parsing and payment requirement enhancement for
8
+ * Bitcoin on-chain payments using the exact scheme.
9
+ */
10
+
11
+ /**
12
+ * Configuration options for ExactBtcScheme server
13
+ */
14
+ interface ExactBtcSchemeConfig {
15
+ /**
16
+ * The Bitcoin address to receive payments
17
+ */
18
+ payTo: string;
19
+ }
20
+ /**
21
+ * Bitcoin server implementation for the Exact payment scheme.
22
+ * Handles price parsing and converts user-friendly amounts to satoshis.
23
+ *
24
+ * For Bitcoin, the asset is always "BTC" and amounts are in satoshis.
25
+ */
26
+ declare class ExactBtcScheme implements SchemeNetworkServer {
27
+ readonly scheme = "exact";
28
+ private moneyParsers;
29
+ private config;
30
+ constructor(config: ExactBtcSchemeConfig);
31
+ /**
32
+ * Register a custom money parser in the parser chain.
33
+ *
34
+ * @param parser - Custom function to convert amount to AssetAmount (or null to skip)
35
+ * @returns The server instance for chaining
36
+ */
37
+ registerMoneyParser(parser: MoneyParser): ExactBtcScheme;
38
+ /**
39
+ * Parses a price into an asset amount in satoshis.
40
+ *
41
+ * Accepts:
42
+ * - Number: treated as USD, converted to satoshis at a default rate
43
+ * - String with $: treated as USD
44
+ * - AssetAmount: returned directly
45
+ *
46
+ * For Bitcoin, amounts are always in satoshis and the asset is "BTC".
47
+ *
48
+ * @param price - The price to parse
49
+ * @param network - The network to use
50
+ * @returns Promise that resolves to the parsed asset amount
51
+ */
52
+ parsePrice(price: Price, network: Network): Promise<AssetAmount>;
53
+ /**
54
+ * Build payment requirements for this scheme/network combination.
55
+ *
56
+ * @param paymentRequirements - Base payment requirements with amount/asset already set
57
+ * @param supportedKind - The supported kind from facilitator
58
+ * @param extensionKeys - Extensions supported by the facilitator
59
+ * @returns Enhanced payment requirements
60
+ */
61
+ enhancePaymentRequirements(paymentRequirements: PaymentRequirements, supportedKind: {
62
+ t402Version: number;
63
+ scheme: string;
64
+ network: Network;
65
+ extra?: Record<string, unknown>;
66
+ }, extensionKeys: string[]): Promise<PaymentRequirements>;
67
+ /**
68
+ * Parse Money (string | number) to a decimal number.
69
+ */
70
+ private parseMoneyToDecimal;
71
+ /**
72
+ * Default money conversion: treat amount as satoshis.
73
+ * For direct satoshi amounts, the amount is used as-is.
74
+ */
75
+ private defaultMoneyConversion;
76
+ }
77
+
78
+ /**
79
+ * Configuration options for registering BTC schemes to an t402ResourceServer
80
+ */
81
+ interface BtcResourceServerConfig {
82
+ /**
83
+ * Optional specific networks to register
84
+ * If not provided, registers wildcard support (bip122:*)
85
+ */
86
+ networks?: Network[];
87
+ /**
88
+ * Scheme configuration (payTo address, etc.)
89
+ */
90
+ schemeConfig: ExactBtcSchemeConfig;
91
+ }
92
+ /**
93
+ * Registers Bitcoin exact payment schemes to an t402ResourceServer instance.
94
+ *
95
+ * @param server - The t402ResourceServer instance to register schemes to
96
+ * @param config - Configuration for BTC resource server registration
97
+ * @returns The server instance for chaining
98
+ */
99
+ declare function registerExactBtcScheme(server: t402ResourceServer, config: BtcResourceServerConfig): t402ResourceServer;
100
+
101
+ export { type BtcResourceServerConfig, ExactBtcScheme, type ExactBtcSchemeConfig, registerExactBtcScheme };
@@ -0,0 +1,161 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
21
+
22
+ // src/exact/server/index.ts
23
+ var server_exports = {};
24
+ __export(server_exports, {
25
+ ExactBtcScheme: () => ExactBtcScheme,
26
+ registerExactBtcScheme: () => registerExactBtcScheme
27
+ });
28
+ module.exports = __toCommonJS(server_exports);
29
+
30
+ // src/constants.ts
31
+ var BTC_MAINNET = "bip122:000000000019d6689c085ae165831e93";
32
+ var BTC_TESTNET = "bip122:000000000933ea01ad0ee984209779ba";
33
+ var LIGHTNING_MAINNET = "lightning:mainnet";
34
+ var LIGHTNING_TESTNET = "lightning:testnet";
35
+ var BTC_NETWORKS = [BTC_MAINNET, BTC_TESTNET];
36
+ var LIGHTNING_NETWORKS = [LIGHTNING_MAINNET, LIGHTNING_TESTNET];
37
+ var ALL_NETWORKS = [...BTC_NETWORKS, ...LIGHTNING_NETWORKS];
38
+ var SATS_PER_BTC = 1e8;
39
+ var SCHEME_EXACT = "exact";
40
+
41
+ // src/exact/server/scheme.ts
42
+ var ExactBtcScheme = class {
43
+ constructor(config) {
44
+ __publicField(this, "scheme", SCHEME_EXACT);
45
+ __publicField(this, "moneyParsers", []);
46
+ __publicField(this, "config");
47
+ this.config = config;
48
+ }
49
+ /**
50
+ * Register a custom money parser in the parser chain.
51
+ *
52
+ * @param parser - Custom function to convert amount to AssetAmount (or null to skip)
53
+ * @returns The server instance for chaining
54
+ */
55
+ registerMoneyParser(parser) {
56
+ this.moneyParsers.push(parser);
57
+ return this;
58
+ }
59
+ /**
60
+ * Parses a price into an asset amount in satoshis.
61
+ *
62
+ * Accepts:
63
+ * - Number: treated as USD, converted to satoshis at a default rate
64
+ * - String with $: treated as USD
65
+ * - AssetAmount: returned directly
66
+ *
67
+ * For Bitcoin, amounts are always in satoshis and the asset is "BTC".
68
+ *
69
+ * @param price - The price to parse
70
+ * @param network - The network to use
71
+ * @returns Promise that resolves to the parsed asset amount
72
+ */
73
+ async parsePrice(price, network) {
74
+ if (typeof price === "object" && price !== null && "amount" in price) {
75
+ if (!price.asset) {
76
+ throw new Error(`Asset must be specified for AssetAmount on network ${network}`);
77
+ }
78
+ return {
79
+ amount: price.amount,
80
+ asset: price.asset,
81
+ extra: price.extra || {}
82
+ };
83
+ }
84
+ const amount = this.parseMoneyToDecimal(price);
85
+ for (const parser of this.moneyParsers) {
86
+ const result = await parser(amount, network);
87
+ if (result !== null) {
88
+ return result;
89
+ }
90
+ }
91
+ return this.defaultMoneyConversion(amount);
92
+ }
93
+ /**
94
+ * Build payment requirements for this scheme/network combination.
95
+ *
96
+ * @param paymentRequirements - Base payment requirements with amount/asset already set
97
+ * @param supportedKind - The supported kind from facilitator
98
+ * @param extensionKeys - Extensions supported by the facilitator
99
+ * @returns Enhanced payment requirements
100
+ */
101
+ async enhancePaymentRequirements(paymentRequirements, supportedKind, extensionKeys) {
102
+ void supportedKind;
103
+ void extensionKeys;
104
+ return {
105
+ ...paymentRequirements,
106
+ payTo: paymentRequirements.payTo || this.config.payTo,
107
+ asset: paymentRequirements.asset || "BTC"
108
+ };
109
+ }
110
+ /**
111
+ * Parse Money (string | number) to a decimal number.
112
+ */
113
+ parseMoneyToDecimal(money) {
114
+ if (typeof money === "number") {
115
+ if (!Number.isFinite(money)) {
116
+ throw new Error(`Invalid money value: ${money}`);
117
+ }
118
+ return money;
119
+ }
120
+ const cleanMoney = money.replace(/^\$/, "").trim();
121
+ const amount = parseFloat(cleanMoney);
122
+ if (isNaN(amount)) {
123
+ throw new Error(`Invalid money format: ${money}`);
124
+ }
125
+ return amount;
126
+ }
127
+ /**
128
+ * Default money conversion: treat amount as satoshis.
129
+ * For direct satoshi amounts, the amount is used as-is.
130
+ */
131
+ defaultMoneyConversion(amount) {
132
+ const sats = Math.floor(amount * SATS_PER_BTC);
133
+ return {
134
+ amount: sats.toString(),
135
+ asset: "BTC",
136
+ extra: {
137
+ symbol: "BTC",
138
+ decimals: 8
139
+ }
140
+ };
141
+ }
142
+ };
143
+
144
+ // src/exact/server/register.ts
145
+ function registerExactBtcScheme(server, config) {
146
+ const scheme = new ExactBtcScheme(config.schemeConfig);
147
+ if (config.networks && config.networks.length > 0) {
148
+ config.networks.forEach((network) => {
149
+ server.register(network, scheme);
150
+ });
151
+ } else {
152
+ server.register("bip122:*", scheme);
153
+ }
154
+ return server;
155
+ }
156
+ // Annotate the CommonJS export names for ESM import in node:
157
+ 0 && (module.exports = {
158
+ ExactBtcScheme,
159
+ registerExactBtcScheme
160
+ });
161
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/exact/server/index.ts","../../../../src/constants.ts","../../../../src/exact/server/scheme.ts","../../../../src/exact/server/register.ts"],"sourcesContent":["export { ExactBtcScheme } from './scheme.js'\nexport type { ExactBtcSchemeConfig } from './scheme.js'\nexport { registerExactBtcScheme } from './register.js'\nexport type { BtcResourceServerConfig } from './register.js'\n","/**\n * Bitcoin & Lightning Network Constants\n *\n * CAIP-2 network identifiers, dust limits, and fee constants.\n */\n\n/**\n * CAIP-2 Network Identifiers for Bitcoin\n * Uses BIP-122 chain genesis block hashes\n */\nexport const BTC_MAINNET = 'bip122:000000000019d6689c085ae165831e93'\nexport const BTC_TESTNET = 'bip122:000000000933ea01ad0ee984209779ba'\n\n/**\n * CAIP-2 Network Identifiers for Lightning Network\n */\nexport const LIGHTNING_MAINNET = 'lightning:mainnet'\nexport const LIGHTNING_TESTNET = 'lightning:testnet'\n\n/**\n * All supported BTC on-chain networks\n */\nexport const BTC_NETWORKS = [BTC_MAINNET, BTC_TESTNET] as const\n\n/**\n * All supported Lightning networks\n */\nexport const LIGHTNING_NETWORKS = [LIGHTNING_MAINNET, LIGHTNING_TESTNET] as const\n\n/**\n * All supported networks (on-chain + Lightning)\n */\nexport const ALL_NETWORKS = [...BTC_NETWORKS, ...LIGHTNING_NETWORKS] as const\n\nexport type BtcNetwork = (typeof BTC_NETWORKS)[number]\nexport type LightningNetwork = (typeof LIGHTNING_NETWORKS)[number]\n\n/**\n * Dust limit in satoshis - minimum viable output value\n * Outputs below this threshold are rejected by Bitcoin nodes\n */\nexport const DUST_LIMIT = 546\n\n/**\n * Minimum relay fee in satoshis\n * Transactions with fees below this are not relayed by default\n */\nexport const MIN_RELAY_FEE = 1000\n\n/**\n * Satoshis per BTC\n */\nexport const SATS_PER_BTC = 100_000_000\n\n/**\n * Scheme identifiers\n */\nexport const SCHEME_EXACT = 'exact'\n\n/**\n * Default timeout for payment validity (in seconds)\n */\nexport const DEFAULT_VALIDITY_DURATION = 3600 // 1 hour\n\n/**\n * Bitcoin address prefixes for basic validation\n */\nexport const MAINNET_ADDRESS_PREFIXES = ['bc1', '1', '3']\nexport const TESTNET_ADDRESS_PREFIXES = ['tb1', 'm', 'n', '2']\n","/**\n * Bitcoin On-chain Server Scheme Implementation\n *\n * Handles price parsing and payment requirement enhancement for\n * Bitcoin on-chain payments using the exact scheme.\n */\n\nimport type {\n AssetAmount,\n Network,\n PaymentRequirements,\n Price,\n SchemeNetworkServer,\n MoneyParser,\n} from '@t402/core/types'\nimport { SCHEME_EXACT, SATS_PER_BTC } from '../../constants.js'\n\n/**\n * Configuration options for ExactBtcScheme server\n */\nexport interface ExactBtcSchemeConfig {\n /**\n * The Bitcoin address to receive payments\n */\n payTo: string\n}\n\n/**\n * Bitcoin server implementation for the Exact payment scheme.\n * Handles price parsing and converts user-friendly amounts to satoshis.\n *\n * For Bitcoin, the asset is always \"BTC\" and amounts are in satoshis.\n */\nexport class ExactBtcScheme implements SchemeNetworkServer {\n readonly scheme = SCHEME_EXACT\n private moneyParsers: MoneyParser[] = []\n private config: ExactBtcSchemeConfig\n\n constructor(config: ExactBtcSchemeConfig) {\n this.config = config\n }\n\n /**\n * Register a custom money parser in the parser chain.\n *\n * @param parser - Custom function to convert amount to AssetAmount (or null to skip)\n * @returns The server instance for chaining\n */\n registerMoneyParser(parser: MoneyParser): ExactBtcScheme {\n this.moneyParsers.push(parser)\n return this\n }\n\n /**\n * Parses a price into an asset amount in satoshis.\n *\n * Accepts:\n * - Number: treated as USD, converted to satoshis at a default rate\n * - String with $: treated as USD\n * - AssetAmount: returned directly\n *\n * For Bitcoin, amounts are always in satoshis and the asset is \"BTC\".\n *\n * @param price - The price to parse\n * @param network - The network to use\n * @returns Promise that resolves to the parsed asset amount\n */\n async parsePrice(price: Price, network: Network): Promise<AssetAmount> {\n // If already an AssetAmount, return it directly\n if (typeof price === 'object' && price !== null && 'amount' in price) {\n if (!price.asset) {\n throw new Error(`Asset must be specified for AssetAmount on network ${network}`)\n }\n return {\n amount: price.amount,\n asset: price.asset,\n extra: price.extra || {},\n }\n }\n\n // Parse Money to decimal number\n const amount = this.parseMoneyToDecimal(price)\n\n // Try each custom money parser in order\n for (const parser of this.moneyParsers) {\n const result = await parser(amount, network)\n if (result !== null) {\n return result\n }\n }\n\n // Default: treat amount as satoshis directly\n return this.defaultMoneyConversion(amount)\n }\n\n /**\n * Build payment requirements for this scheme/network combination.\n *\n * @param paymentRequirements - Base payment requirements with amount/asset already set\n * @param supportedKind - The supported kind from facilitator\n * @param extensionKeys - Extensions supported by the facilitator\n * @returns Enhanced payment requirements\n */\n async enhancePaymentRequirements(\n paymentRequirements: PaymentRequirements,\n supportedKind: {\n t402Version: number\n scheme: string\n network: Network\n extra?: Record<string, unknown>\n },\n extensionKeys: string[],\n ): Promise<PaymentRequirements> {\n void supportedKind\n void extensionKeys\n\n return {\n ...paymentRequirements,\n payTo: paymentRequirements.payTo || this.config.payTo,\n asset: paymentRequirements.asset || 'BTC',\n }\n }\n\n /**\n * Parse Money (string | number) to a decimal number.\n */\n private parseMoneyToDecimal(money: string | number): number {\n if (typeof money === 'number') {\n if (!Number.isFinite(money)) {\n throw new Error(`Invalid money value: ${money}`)\n }\n return money\n }\n\n const cleanMoney = money.replace(/^\\$/, '').trim()\n const amount = parseFloat(cleanMoney)\n\n if (isNaN(amount)) {\n throw new Error(`Invalid money format: ${money}`)\n }\n\n return amount\n }\n\n /**\n * Default money conversion: treat amount as satoshis.\n * For direct satoshi amounts, the amount is used as-is.\n */\n private defaultMoneyConversion(amount: number): AssetAmount {\n // Amount is treated as satoshis directly\n const sats = Math.floor(amount * SATS_PER_BTC)\n\n return {\n amount: sats.toString(),\n asset: 'BTC',\n extra: {\n symbol: 'BTC',\n decimals: 8,\n },\n }\n }\n}\n","import { t402ResourceServer } from '@t402/core/server'\nimport { Network } from '@t402/core/types'\nimport { ExactBtcScheme, ExactBtcSchemeConfig } from './scheme.js'\n\n/**\n * Configuration options for registering BTC schemes to an t402ResourceServer\n */\nexport interface BtcResourceServerConfig {\n /**\n * Optional specific networks to register\n * If not provided, registers wildcard support (bip122:*)\n */\n networks?: Network[]\n\n /**\n * Scheme configuration (payTo address, etc.)\n */\n schemeConfig: ExactBtcSchemeConfig\n}\n\n/**\n * Registers Bitcoin exact payment schemes to an t402ResourceServer instance.\n *\n * @param server - The t402ResourceServer instance to register schemes to\n * @param config - Configuration for BTC resource server registration\n * @returns The server instance for chaining\n */\nexport function registerExactBtcScheme(\n server: t402ResourceServer,\n config: BtcResourceServerConfig,\n): t402ResourceServer {\n const scheme = new ExactBtcScheme(config.schemeConfig)\n\n if (config.networks && config.networks.length > 0) {\n config.networks.forEach((network) => {\n server.register(network, scheme)\n })\n } else {\n server.register('bip122:*', scheme)\n }\n\n return server\n}\n"],"mappings":"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","names":[]}