@dynamic-labs-sdk/bitcoin 0.5.0 → 0.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 (71) hide show
  1. package/dist/{addBitcoinInjectedWalletsExtension-BjijCDrr.esm.js → addBitcoinInjectedWalletsExtension-B2EklxuR.esm.js} +2 -2
  2. package/dist/{addBitcoinInjectedWalletsExtension-BjijCDrr.esm.js.map → addBitcoinInjectedWalletsExtension-B2EklxuR.esm.js.map} +1 -1
  3. package/dist/{addBitcoinInjectedWalletsExtension-DNUJc7N7.cjs.js → addBitcoinInjectedWalletsExtension-CVEuujG-.cjs.js} +3 -2
  4. package/dist/{addBitcoinInjectedWalletsExtension-DNUJc7N7.cjs.js.map → addBitcoinInjectedWalletsExtension-CVEuujG-.cjs.js.map} +1 -1
  5. package/dist/buildPsbt/buildPsbt.d.ts +24 -0
  6. package/dist/buildPsbt/buildPsbt.d.ts.map +1 -0
  7. package/dist/buildPsbt/index.d.ts +2 -0
  8. package/dist/buildPsbt/index.d.ts.map +1 -0
  9. package/dist/errors/InsufficientFundsError.d.ts +12 -0
  10. package/dist/errors/InsufficientFundsError.d.ts.map +1 -0
  11. package/dist/errors/InvalidAmountError.d.ts +11 -0
  12. package/dist/errors/InvalidAmountError.d.ts.map +1 -0
  13. package/dist/errors/NoUTXOsFoundError.d.ts +10 -0
  14. package/dist/errors/NoUTXOsFoundError.d.ts.map +1 -0
  15. package/dist/errors/PublicKeyNotFoundError.d.ts +5 -0
  16. package/dist/errors/PublicKeyNotFoundError.d.ts.map +1 -0
  17. package/dist/errors/SegwitOutputScriptError.d.ts +5 -0
  18. package/dist/errors/SegwitOutputScriptError.d.ts.map +1 -0
  19. package/dist/errors/TaprootAddressNotSupportedError.d.ts +5 -0
  20. package/dist/errors/TaprootAddressNotSupportedError.d.ts.map +1 -0
  21. package/dist/exports/waas.d.ts +2 -0
  22. package/dist/exports/waas.d.ts.map +1 -1
  23. package/dist/index.cjs.js +636 -40
  24. package/dist/index.cjs.js.map +1 -1
  25. package/dist/index.esm.js +609 -42
  26. package/dist/index.esm.js.map +1 -1
  27. package/dist/injected.cjs.js +2 -1
  28. package/dist/injected.cjs.js.map +1 -1
  29. package/dist/injected.esm.js +1 -1
  30. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  31. package/dist/waas/WaasBitcoinWalletProvider.types.d.ts +24 -5
  32. package/dist/waas/WaasBitcoinWalletProvider.types.d.ts.map +1 -1
  33. package/dist/waas/utils/addInputsToPsbt/addInputsToPsbt.d.ts +21 -0
  34. package/dist/waas/utils/addInputsToPsbt/addInputsToPsbt.d.ts.map +1 -0
  35. package/dist/waas/utils/addInputsToPsbt/index.d.ts +2 -0
  36. package/dist/waas/utils/addInputsToPsbt/index.d.ts.map +1 -0
  37. package/dist/waas/utils/addOutputsToPsbt/addOutputsToPsbt.d.ts +24 -0
  38. package/dist/waas/utils/addOutputsToPsbt/addOutputsToPsbt.d.ts.map +1 -0
  39. package/dist/waas/utils/addOutputsToPsbt/index.d.ts +2 -0
  40. package/dist/waas/utils/addOutputsToPsbt/index.d.ts.map +1 -0
  41. package/dist/waas/utils/broadcastTransaction/broadcastTransaction.d.ts.map +1 -1
  42. package/dist/waas/utils/buildPsbt/buildPsbt.d.ts +26 -0
  43. package/dist/waas/utils/buildPsbt/buildPsbt.d.ts.map +1 -0
  44. package/dist/waas/utils/buildPsbt/index.d.ts +2 -0
  45. package/dist/waas/utils/buildPsbt/index.d.ts.map +1 -0
  46. package/dist/waas/utils/calculateFeeAndChange/calculateFeeAndChange.d.ts +24 -0
  47. package/dist/waas/utils/calculateFeeAndChange/calculateFeeAndChange.d.ts.map +1 -0
  48. package/dist/waas/utils/calculateFeeAndChange/index.d.ts +2 -0
  49. package/dist/waas/utils/calculateFeeAndChange/index.d.ts.map +1 -0
  50. package/dist/waas/utils/calculateUTXOTotal/calculateUTXOTotal.d.ts +9 -0
  51. package/dist/waas/utils/calculateUTXOTotal/calculateUTXOTotal.d.ts.map +1 -0
  52. package/dist/waas/utils/calculateUTXOTotal/index.d.ts +2 -0
  53. package/dist/waas/utils/calculateUTXOTotal/index.d.ts.map +1 -0
  54. package/dist/waas/utils/createWalletProviderForWaasBitcoin/createWalletProviderForWaasBitcoin.d.ts.map +1 -1
  55. package/dist/waas/utils/estimateTransactionFee/estimateTransactionFee.d.ts +10 -4
  56. package/dist/waas/utils/estimateTransactionFee/estimateTransactionFee.d.ts.map +1 -1
  57. package/dist/waas/utils/getPublicKeyForWalletAccount/getPublicKeyForWalletAccount.d.ts +6 -0
  58. package/dist/waas/utils/getPublicKeyForWalletAccount/getPublicKeyForWalletAccount.d.ts.map +1 -0
  59. package/dist/waas/utils/selectUTXOsLargestFirst/index.d.ts +2 -0
  60. package/dist/waas/utils/selectUTXOsLargestFirst/index.d.ts.map +1 -0
  61. package/dist/waas/utils/selectUTXOsLargestFirst/selectUTXOsLargestFirst.d.ts +16 -0
  62. package/dist/waas/utils/selectUTXOsLargestFirst/selectUTXOsLargestFirst.d.ts.map +1 -0
  63. package/dist/waas/utils/validateAndSelectUTXOs/index.d.ts +2 -0
  64. package/dist/waas/utils/validateAndSelectUTXOs/index.d.ts.map +1 -0
  65. package/dist/waas/utils/validateAndSelectUTXOs/validateAndSelectUTXOs.d.ts +22 -0
  66. package/dist/waas/utils/validateAndSelectUTXOs/validateAndSelectUTXOs.d.ts.map +1 -0
  67. package/dist/waas/utils/validateNotTaproot/index.d.ts +2 -0
  68. package/dist/waas/utils/validateNotTaproot/index.d.ts.map +1 -0
  69. package/dist/waas/utils/validateNotTaproot/validateNotTaproot.d.ts +9 -0
  70. package/dist/waas/utils/validateNotTaproot/validateNotTaproot.d.ts.map +1 -0
  71. package/package.json +6 -4
package/dist/index.esm.js CHANGED
@@ -1,10 +1,554 @@
1
- import { a as name, i as getMempoolApiUrl, n as InvalidPsbtError, o as version, r as registerBitcoinNetworkProviderBuilder, t as addBitcoinInjectedWalletsExtension } from "./addBitcoinInjectedWalletsExtension-BjijCDrr.esm.js";
1
+ import { a as name, i as getMempoolApiUrl, n as InvalidPsbtError, o as version, r as registerBitcoinNetworkProviderBuilder, t as addBitcoinInjectedWalletsExtension } from "./addBitcoinInjectedWalletsExtension-B2EklxuR.esm.js";
2
2
  import { assertPackageVersion } from "@dynamic-labs-sdk/assert-package-version";
3
- import { MethodNotImplementedError, WalletProviderPriority, assertDefined, formatWalletProviderGroupKey, formatWalletProviderKey, getActiveNetworkIdFromLastKnownRegistry, getCore, getDefaultClient, getWalletProviderFromWalletAccount, getWalletProviderRegistry, hasExtension, registerExtension, switchActiveNetworkInLastKnownRegistry } from "@dynamic-labs-sdk/client/core";
3
+ import { MethodNotImplementedError, WalletProviderPriority, assertDefined, consumeMfaTokenIfRequiredForAction, formatWalletProviderGroupKey, formatWalletProviderKey, getActiveNetworkIdFromLastKnownRegistry, getBuffer, getCore, getDefaultClient, getSignedSessionId, getWalletProviderFromWalletAccount, getWalletProviderRegistry, hasExtension, registerExtension, switchActiveNetworkInLastKnownRegistry } from "@dynamic-labs-sdk/client/core";
4
4
  import { BaseError, getDefaultClient as getDefaultClient$1 } from "@dynamic-labs-sdk/client";
5
- import { WalletProviderEnum } from "@dynamic-labs/sdk-api-core";
5
+ import { MFAAction, WalletProviderEnum } from "@dynamic-labs/sdk-api-core";
6
+ import { Psbt, address, networks, payments } from "bitcoinjs-lib";
6
7
  import { DYNAMIC_WAAS_METADATA, createWaasProvider, getAllUserWaasAddressesForChain } from "@dynamic-labs-sdk/client/waas/core";
8
+ import ecc from "@bitcoinerlab/secp256k1";
9
+ import { ECPairFactory } from "ecpair";
7
10
 
11
+ //#region src/errors/TransactionBroadcastFailedError.ts
12
+ var TransactionBroadcastFailedError = class extends BaseError {
13
+ response;
14
+ constructor({ response }) {
15
+ super({
16
+ cause: null,
17
+ code: "transaction_broadcast_failed_error",
18
+ details: `Status: ${response.status} ${response.statusText}`,
19
+ docsUrl: null,
20
+ name: "TransactionBroadcastFailedError",
21
+ shortMessage: "Failed to broadcast transaction to mempool"
22
+ });
23
+ this.response = response;
24
+ }
25
+ };
26
+
27
+ //#endregion
28
+ //#region src/errors/TransactionRequiredError.ts
29
+ var TransactionRequiredError = class extends BaseError {
30
+ constructor() {
31
+ super({
32
+ cause: null,
33
+ code: "transaction_required_error",
34
+ docsUrl: null,
35
+ name: "TransactionRequiredError",
36
+ shortMessage: "No transaction specified for broadcast"
37
+ });
38
+ }
39
+ };
40
+
41
+ //#endregion
42
+ //#region src/waas/constants.ts
43
+ /**
44
+ * Mempool.space API URL for mainnet
45
+ */
46
+ const MEMPOOL_API_URL = "https://mempool.space/api";
47
+ /**
48
+ * Number of satoshis per Bitcoin
49
+ */
50
+ const SATOSHIS_PER_BTC = 1e8;
51
+ /**
52
+ * Bitcoin's dust limit in satoshis
53
+ * Outputs below this value are considered "dust" and will be rejected by nodes
54
+ */
55
+ const DUST_LIMIT = 546;
56
+ /**
57
+ * Accurate vSize constants for Native SegWit (P2WPKH) transactions
58
+ * Used for precise fee estimation
59
+ */
60
+ const VSIZE_OVERHEAD = 10.5;
61
+ const VSIZE_INPUT_P2WPKH = 68;
62
+ const VSIZE_OUTPUT_P2WPKH = 31;
63
+ /**
64
+ * Minimum relay fee in satoshis
65
+ * Added to fee estimate to ensure transaction propagation
66
+ */
67
+ const MIN_RELAY_FEE = 111;
68
+ /**
69
+ * Conservative default fee estimate in satoshis
70
+ * Used as fallback when fee estimation fails
71
+ */
72
+ const DEFAULT_FEE_ESTIMATE = 1e3;
73
+ /**
74
+ * RBF (Replace-By-Fee) sequence number
75
+ * 0xfffffffd = 4294967293 (enables RBF, not final)
76
+ */
77
+ const RBF_SEQUENCE = 4294967293;
78
+
79
+ //#endregion
80
+ //#region src/waas/utils/broadcastTransaction/broadcastTransaction.ts
81
+ /**
82
+ * Sends a raw Bitcoin transaction to the mempool
83
+ *
84
+ * @param rawTransaction - The raw transaction in hex format
85
+ * @returns The transaction ID
86
+ * @throws {TransactionRequiredError} if no transaction is specified
87
+ * @throws {TransactionBroadcastFailedError} if broadcasting fails
88
+ */
89
+ const broadcastTransaction = async (rawTransaction) => {
90
+ if (!rawTransaction) throw new TransactionRequiredError();
91
+ const response = await fetch(`${MEMPOOL_API_URL}/tx`, {
92
+ body: rawTransaction,
93
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
94
+ method: "POST"
95
+ });
96
+ if (!response.ok) throw new TransactionBroadcastFailedError({ response });
97
+ return response.text();
98
+ };
99
+
100
+ //#endregion
101
+ //#region src/errors/InsufficientFundsError.ts
102
+ var InsufficientFundsError = class extends BaseError {
103
+ availableSatoshis;
104
+ requiredSatoshis;
105
+ constructor({ availableSatoshis, requiredSatoshis }) {
106
+ const availableBtc = availableSatoshis / SATOSHIS_PER_BTC;
107
+ const requiredBtc = requiredSatoshis / SATOSHIS_PER_BTC;
108
+ super({
109
+ cause: null,
110
+ code: "insufficient_funds_error",
111
+ details: `Available: ${availableBtc} BTC (${availableSatoshis} satoshis), Required: ${requiredBtc} BTC (${requiredSatoshis} satoshis)`,
112
+ docsUrl: null,
113
+ name: "InsufficientFundsError",
114
+ shortMessage: "Insufficient funds for transaction"
115
+ });
116
+ this.availableSatoshis = availableSatoshis;
117
+ this.requiredSatoshis = requiredSatoshis;
118
+ }
119
+ };
120
+
121
+ //#endregion
122
+ //#region src/errors/InvalidAmountError.ts
123
+ var InvalidAmountError = class extends BaseError {
124
+ amountInSatoshis;
125
+ constructor({ amountInSatoshis, reason }) {
126
+ super({
127
+ cause: null,
128
+ code: "invalid_amount_error",
129
+ details: `Amount: ${amountInSatoshis} satoshis`,
130
+ docsUrl: null,
131
+ name: "InvalidAmountError",
132
+ shortMessage: reason
133
+ });
134
+ this.amountInSatoshis = amountInSatoshis;
135
+ }
136
+ };
137
+
138
+ //#endregion
139
+ //#region src/errors/NoUTXOsFoundError.ts
140
+ var NoUTXOsFoundError = class extends BaseError {
141
+ address;
142
+ constructor({ address: address$1 }) {
143
+ super({
144
+ cause: null,
145
+ code: "no_utxos_found_error",
146
+ details: `Address: ${address$1}`,
147
+ docsUrl: null,
148
+ name: "NoUTXOsFoundError",
149
+ shortMessage: "No UTXOs found for this address"
150
+ });
151
+ this.address = address$1;
152
+ }
153
+ };
154
+
155
+ //#endregion
156
+ //#region src/errors/SegwitOutputScriptError.ts
157
+ var SegwitOutputScriptError = class extends BaseError {
158
+ constructor() {
159
+ super({
160
+ cause: null,
161
+ code: "segwit_output_script_error",
162
+ docsUrl: null,
163
+ name: "SegwitOutputScriptError",
164
+ shortMessage: "Failed to create segwit output script"
165
+ });
166
+ }
167
+ };
168
+
169
+ //#endregion
170
+ //#region src/waas/utils/addInputsToPsbt/addInputsToPsbt.ts
171
+ /**
172
+ * Adds inputs to PSBT from selected UTXOs
173
+ *
174
+ * @param options.network - Bitcoin network configuration (mainnet or testnet)
175
+ * @param options.psbt - The PSBT instance to add inputs to
176
+ * @param options.publicKeyPair - Key pair containing the public key for witness script
177
+ * @param options.selectedUTXOs - Array of UTXOs to add as inputs
178
+ */
179
+ const addInputsToPsbt = (options) => {
180
+ const { network, psbt, publicKeyPair, selectedUTXOs } = options;
181
+ for (const utxo of selectedUTXOs) {
182
+ const outputScript = payments.p2wpkh({
183
+ network,
184
+ pubkey: publicKeyPair.publicKey
185
+ }).output;
186
+ if (!outputScript) throw new SegwitOutputScriptError();
187
+ const txidBuffer = new Uint8Array(getBuffer().from(utxo.txid, "hex").reverse());
188
+ psbt.addInput({
189
+ hash: txidBuffer,
190
+ index: utxo.vout,
191
+ sequence: RBF_SEQUENCE,
192
+ witnessUtxo: {
193
+ script: outputScript,
194
+ value: BigInt(utxo.value)
195
+ }
196
+ });
197
+ }
198
+ };
199
+
200
+ //#endregion
201
+ //#region src/waas/utils/addOutputsToPsbt/addOutputsToPsbt.ts
202
+ /**
203
+ * Adds outputs to PSBT (recipient and optionally change)
204
+ *
205
+ * @param options.accountAddress - The sender's address for receiving change
206
+ * @param options.amountInSatoshis - Amount to send to the recipient in satoshis
207
+ * @param options.changeAmount - Amount to return as change in satoshis
208
+ * @param options.hasChangeOutput - Whether to include a change output
209
+ * @param options.network - Bitcoin network configuration (mainnet or testnet)
210
+ * @param options.psbt - The PSBT instance to add outputs to
211
+ * @param options.recipientAddress - The recipient's Bitcoin address
212
+ */
213
+ const addOutputsToPsbt = (options) => {
214
+ const { accountAddress, amountInSatoshis, changeAmount, hasChangeOutput, network, psbt, recipientAddress } = options;
215
+ if (amountInSatoshis < DUST_LIMIT) throw new InvalidAmountError({
216
+ amountInSatoshis,
217
+ reason: `Amount is below dust limit of ${DUST_LIMIT} satoshis (${DUST_LIMIT / SATOSHIS_PER_BTC} BTC)`
218
+ });
219
+ psbt.addOutput({
220
+ script: address.toOutputScript(recipientAddress, network),
221
+ value: BigInt(amountInSatoshis)
222
+ });
223
+ if (hasChangeOutput) psbt.addOutput({
224
+ script: address.toOutputScript(accountAddress, network),
225
+ value: BigInt(changeAmount)
226
+ });
227
+ };
228
+
229
+ //#endregion
230
+ //#region src/errors/FeeRecommendationsFetchError.ts
231
+ var FeeRecommendationsFetchError = class extends BaseError {
232
+ response;
233
+ constructor({ response }) {
234
+ super({
235
+ cause: null,
236
+ code: "fee_recommendations_fetch_error",
237
+ details: `Status: ${response.status} ${response.statusText}`,
238
+ docsUrl: null,
239
+ name: "FeeRecommendationsFetchError",
240
+ shortMessage: "Failed to fetch fee recommendations from mempool"
241
+ });
242
+ this.response = response;
243
+ }
244
+ };
245
+
246
+ //#endregion
247
+ //#region src/waas/utils/getFeeRecommendations/getFeeRecommendations.ts
248
+ /**
249
+ * Gets fee recommendations from mempool.space API
250
+ *
251
+ * @returns Fee recommendation data with rates in sat/vB
252
+ * @throws FeeRecommendationsFetchError if fetching fee recommendations fails
253
+ */
254
+ const getFeeRecommendations = async () => {
255
+ const response = await fetch(`${MEMPOOL_API_URL}/v1/fees/recommended`);
256
+ if (!response.ok) throw new FeeRecommendationsFetchError({ response });
257
+ return response.json();
258
+ };
259
+
260
+ //#endregion
261
+ //#region src/waas/utils/estimateTransactionFee/estimateTransactionFee.ts
262
+ /**
263
+ * Estimates transaction fees based on number of inputs and outputs using accurate vSize
264
+ *
265
+ * @param options.feePriority - Priority level for fee estimation ('low', 'medium', or 'high')
266
+ * @param options.numInputs - Number of transaction inputs (UTXOs being spent)
267
+ * @param options.numOutputs - Number of transaction outputs
268
+ * @returns Estimated fee in satoshis
269
+ */
270
+ const estimateTransactionFee = async ({ feePriority = "medium", numInputs, numOutputs }) => {
271
+ try {
272
+ const feeData = await getFeeRecommendations();
273
+ let feePerByte;
274
+ if (feePriority === "high") feePerByte = feeData.fastestFee ?? feeData.halfHourFee ?? 1;
275
+ else if (feePriority === "low") feePerByte = feeData.economyFee ?? feeData.hourFee ?? 1;
276
+ else feePerByte = feeData.halfHourFee ?? feeData.hourFee ?? feeData.economyFee ?? 1;
277
+ const vSize = VSIZE_OVERHEAD + numInputs * VSIZE_INPUT_P2WPKH + numOutputs * VSIZE_OUTPUT_P2WPKH;
278
+ return Math.ceil(feePerByte * vSize) + MIN_RELAY_FEE;
279
+ } catch {
280
+ return DEFAULT_FEE_ESTIMATE;
281
+ }
282
+ };
283
+
284
+ //#endregion
285
+ //#region src/waas/utils/calculateFeeAndChange/calculateFeeAndChange.ts
286
+ /**
287
+ * Calculates fee estimate and change amount, handling dust limit
288
+ *
289
+ * @param options.amountInSatoshis - Amount to send in satoshis
290
+ * @param options.feePriority - Priority level for fee estimation ('low', 'medium', or 'high')
291
+ * @param options.selectedTotalValue - Total value of selected UTXOs in satoshis
292
+ * @param options.selectedUTXOs - Array of selected UTXOs for the transaction
293
+ * @returns Object with feeEstimate, changeAmountNumber, and hasChangeOutput
294
+ */
295
+ const calculateFeeAndChange = async ({ amountInSatoshis, feePriority, selectedTotalValue, selectedUTXOs }) => {
296
+ let feeEstimate = await estimateTransactionFee({
297
+ feePriority,
298
+ numInputs: selectedUTXOs.length,
299
+ numOutputs: 1
300
+ });
301
+ let maxToSpend = selectedTotalValue - feeEstimate;
302
+ let changeAmount = BigInt(maxToSpend) - amountInSatoshis;
303
+ if (changeAmount > 0 && Number(changeAmount) >= DUST_LIMIT) {
304
+ feeEstimate = await estimateTransactionFee({
305
+ feePriority,
306
+ numInputs: selectedUTXOs.length,
307
+ numOutputs: 2
308
+ });
309
+ maxToSpend = selectedTotalValue - feeEstimate;
310
+ changeAmount = BigInt(maxToSpend) - amountInSatoshis;
311
+ }
312
+ const finalChangeAmountNumber = Number(changeAmount);
313
+ const hasChangeOutput = changeAmount > 0 && finalChangeAmountNumber >= DUST_LIMIT;
314
+ if (changeAmount > 0 && finalChangeAmountNumber < DUST_LIMIT) feeEstimate += finalChangeAmountNumber;
315
+ return {
316
+ changeAmountNumber: finalChangeAmountNumber,
317
+ feeEstimate,
318
+ hasChangeOutput
319
+ };
320
+ };
321
+
322
+ //#endregion
323
+ //#region src/waas/utils/calculateUTXOTotal/calculateUTXOTotal.ts
324
+ /**
325
+ * Calculates the total value of UTXOs
326
+ *
327
+ * @param utxos - Array of UTXOs
328
+ * @returns Total value in satoshis
329
+ */
330
+ const calculateUTXOTotal = (utxos) => utxos.reduce((total, utxo) => total + utxo.value, 0);
331
+
332
+ //#endregion
333
+ //#region src/errors/UTXOsFetchError.ts
334
+ var UTXOsFetchError = class extends BaseError {
335
+ address;
336
+ response;
337
+ constructor({ address: address$1, response }) {
338
+ super({
339
+ cause: null,
340
+ code: "utxos_fetch_error",
341
+ details: `Address: ${address$1}, Status: ${response.status} ${response.statusText}`,
342
+ docsUrl: null,
343
+ name: "UTXOsFetchError",
344
+ shortMessage: "Failed to fetch UTXOs from mempool"
345
+ });
346
+ this.address = address$1;
347
+ this.response = response;
348
+ }
349
+ };
350
+
351
+ //#endregion
352
+ //#region src/waas/utils/getUTXOs/getUTXOs.ts
353
+ /**
354
+ * Gets UTXOs for a Bitcoin address from mempool.space API
355
+ *
356
+ * @param address - The Bitcoin address to get UTXOs for
357
+ * @returns Array of UTXOs
358
+ * @throws UTXOsFetchError if fetching UTXOs fails
359
+ */
360
+ const getUTXOs = async (address$1) => {
361
+ const response = await fetch(`${MEMPOOL_API_URL}/address/${address$1}/utxo`);
362
+ if (!response.ok) throw new UTXOsFetchError({
363
+ address: address$1,
364
+ response
365
+ });
366
+ return response.json();
367
+ };
368
+
369
+ //#endregion
370
+ //#region src/waas/utils/selectUTXOsLargestFirst/selectUTXOsLargestFirst.ts
371
+ /**
372
+ * Selects UTXOs using Largest-First (Accumulator) strategy
373
+ * Sorts UTXOs by value (descending) and selects until we have enough to cover amount + fees
374
+ *
375
+ * @param options.targetAmount - Target amount in satoshis (amount + fees + dust limit)
376
+ * @param options.utxos - Array of available UTXOs to select from
377
+ * @returns Selected UTXOs
378
+ */
379
+ const selectUTXOsLargestFirst = (options) => {
380
+ const { targetAmount, utxos } = options;
381
+ const sortedUTXOs = [...utxos].sort((a, b) => b.value - a.value);
382
+ const selected = [];
383
+ let total = 0;
384
+ for (const utxo of sortedUTXOs) {
385
+ selected.push(utxo);
386
+ total += utxo.value;
387
+ if (total >= targetAmount) break;
388
+ }
389
+ return selected;
390
+ };
391
+
392
+ //#endregion
393
+ //#region src/waas/utils/validateAndSelectUTXOs/validateAndSelectUTXOs.ts
394
+ /**
395
+ * Validates and ensures sufficient funds for the transaction
396
+ *
397
+ * @param options.allUTXOs - Complete array of available UTXOs for the address
398
+ * @param options.amountInSatoshis - Amount to send in satoshis
399
+ * @param options.feeEstimate - Estimated transaction fee in satoshis
400
+ * @param options.selectedTotal - Total value of initially selected UTXOs in satoshis
401
+ * @param options.selectedUTXOs - Array of initially selected UTXOs
402
+ * @returns Validated selected UTXOs
403
+ * @throws InsufficientFundsError if insufficient funds
404
+ */
405
+ const validateAndSelectUTXOs = (options) => {
406
+ const { allUTXOs, amountInSatoshis, feeEstimate, selectedTotal, selectedUTXOs } = options;
407
+ const requiredAmount = amountInSatoshis + feeEstimate;
408
+ if (selectedTotal >= requiredAmount) return selectedUTXOs;
409
+ if (selectedUTXOs.length < allUTXOs.length) {
410
+ const allTotal = calculateUTXOTotal(allUTXOs);
411
+ if (allTotal < requiredAmount) throw new InsufficientFundsError({
412
+ availableSatoshis: allTotal,
413
+ requiredSatoshis: amountInSatoshis
414
+ });
415
+ return allUTXOs;
416
+ }
417
+ throw new InsufficientFundsError({
418
+ availableSatoshis: selectedTotal,
419
+ requiredSatoshis: amountInSatoshis
420
+ });
421
+ };
422
+
423
+ //#endregion
424
+ //#region src/errors/TaprootAddressNotSupportedError.ts
425
+ var TaprootAddressNotSupportedError = class extends BaseError {
426
+ constructor() {
427
+ super({
428
+ cause: null,
429
+ code: "taproot_address_not_supported",
430
+ docsUrl: null,
431
+ name: "TaprootAddressNotSupportedError",
432
+ shortMessage: "Taproot addresses are not supported for PSBT building. Only Native SegWit (P2WPKH) addresses are allowed."
433
+ });
434
+ }
435
+ };
436
+
437
+ //#endregion
438
+ //#region src/waas/utils/validateNotTaproot/validateNotTaproot.ts
439
+ /**
440
+ * Validates that the address is not a Taproot address
441
+ * Only Native SegWit (P2WPKH) is supported for PSBT building
442
+ *
443
+ * @param accountAddress - The account address to check
444
+ * @throws TaprootAddressNotSupportedError if address is Taproot
445
+ */
446
+ const validateNotTaproot = (accountAddress) => {
447
+ if (accountAddress.toLowerCase().startsWith("bc1p") || accountAddress.toLowerCase().startsWith("tb1p")) throw new TaprootAddressNotSupportedError();
448
+ };
449
+
450
+ //#endregion
451
+ //#region src/waas/utils/buildPsbt/buildPsbt.ts
452
+ /**
453
+ * Builds a PSBT for a Bitcoin transaction with real UTXOs
454
+ * Uses Largest-First UTXO selection strategy with accurate vSize fee estimation
455
+ *
456
+ * @param options.accountAddress - The sender's Bitcoin address
457
+ * @param options.amountInSatoshis - Amount to send in satoshis
458
+ * @param options.feePriority - Priority level for fee estimation ('low', 'medium', or 'high')
459
+ * @param options.network - Bitcoin network configuration (mainnet or testnet)
460
+ * @param options.publicKeyHex - The sender's public key in hexadecimal format
461
+ * @param options.recipientAddress - The recipient's Bitcoin address
462
+ * @returns A PSBT in Base64 format
463
+ * @throws Error if insufficient funds, no UTXOs, or other errors
464
+ */
465
+ const buildPsbt = async ({ accountAddress, amountInSatoshis, feePriority = "medium", network, publicKeyHex, recipientAddress }) => {
466
+ if (amountInSatoshis <= BigInt(0)) throw new InvalidAmountError({
467
+ amountInSatoshis: Number(amountInSatoshis),
468
+ reason: "Amount must be greater than 0"
469
+ });
470
+ validateNotTaproot(accountAddress);
471
+ const allUTXOs = await getUTXOs(accountAddress);
472
+ if (allUTXOs.length === 0) throw new NoUTXOsFoundError({ address: accountAddress });
473
+ const publicKeyBuffer = new Uint8Array(getBuffer().from(publicKeyHex, "hex"));
474
+ const publicKeyPair = ECPairFactory(ecc).fromPublicKey(publicKeyBuffer, { compressed: true });
475
+ const amountInSatoshisNumber = Number(amountInSatoshis);
476
+ const initialFeeEstimate = await estimateTransactionFee({
477
+ feePriority,
478
+ numInputs: 1,
479
+ numOutputs: 1
480
+ });
481
+ let selectedUTXOs = selectUTXOsLargestFirst({
482
+ targetAmount: amountInSatoshisNumber + initialFeeEstimate + DUST_LIMIT,
483
+ utxos: allUTXOs
484
+ });
485
+ selectedUTXOs = validateAndSelectUTXOs({
486
+ allUTXOs,
487
+ amountInSatoshis: amountInSatoshisNumber,
488
+ feeEstimate: initialFeeEstimate,
489
+ selectedTotal: calculateUTXOTotal(selectedUTXOs),
490
+ selectedUTXOs
491
+ });
492
+ const selectedTotalValue = calculateUTXOTotal(selectedUTXOs);
493
+ const { changeAmountNumber, feeEstimate, hasChangeOutput } = await calculateFeeAndChange({
494
+ amountInSatoshis,
495
+ feePriority,
496
+ selectedTotalValue,
497
+ selectedUTXOs
498
+ });
499
+ const maxToSpend = selectedTotalValue - feeEstimate;
500
+ if (maxToSpend < amountInSatoshisNumber) throw new InsufficientFundsError({
501
+ availableSatoshis: maxToSpend,
502
+ requiredSatoshis: amountInSatoshisNumber
503
+ });
504
+ const psbt = new Psbt({ network });
505
+ addInputsToPsbt({
506
+ network,
507
+ psbt,
508
+ publicKeyPair,
509
+ selectedUTXOs
510
+ });
511
+ addOutputsToPsbt({
512
+ accountAddress,
513
+ amountInSatoshis: amountInSatoshisNumber,
514
+ changeAmount: changeAmountNumber,
515
+ hasChangeOutput,
516
+ network,
517
+ psbt,
518
+ recipientAddress
519
+ });
520
+ return psbt.toBase64();
521
+ };
522
+
523
+ //#endregion
524
+ //#region src/errors/PublicKeyNotFoundError.ts
525
+ var PublicKeyNotFoundError = class extends BaseError {
526
+ constructor(address$1) {
527
+ super({
528
+ cause: null,
529
+ code: "public_key_not_found_error",
530
+ docsUrl: null,
531
+ name: "PublicKeyNotFoundError",
532
+ shortMessage: `No public key found for address ${address$1}`
533
+ });
534
+ }
535
+ };
536
+
537
+ //#endregion
538
+ //#region src/waas/utils/getPublicKeyForWalletAccount/getPublicKeyForWalletAccount.ts
539
+ /**
540
+ * Gets the public key for a wallet account from the user's verified credentials
541
+ */
542
+ const getPublicKeyForWalletAccount = (walletAccount, client) => {
543
+ const user = getCore(client).state.get().user;
544
+ for (const credential of user?.verifiedCredentials ?? []) {
545
+ const additionalAddress = credential.walletAdditionalAddresses?.find((addr) => addr.address === walletAccount.address);
546
+ if (additionalAddress?.publicKey) return additionalAddress.publicKey;
547
+ }
548
+ throw new PublicKeyNotFoundError(walletAccount.address);
549
+ };
550
+
551
+ //#endregion
8
552
  //#region src/waas/utils/createWalletProviderForWaasBitcoin/createWalletProviderForWaasBitcoin.ts
9
553
  const createWalletProviderForWaasBitcoin = (sdkClient) => {
10
554
  const chain = "BTC";
@@ -30,23 +574,77 @@ const createWalletProviderForWaasBitcoin = (sdkClient) => {
30
574
  const getConnectedAddresses = async () => {
31
575
  return { addresses: getAllUserWaasAddressesForChain({ chain }, sdkClient) };
32
576
  };
33
- const signPsbt$1 = async ({ request: _request, walletAccount }) => {
577
+ const signPsbt$1 = async ({ request, walletAccount }) => {
34
578
  assertDefined(walletAccount, "Wallet account is required");
35
- throw new MethodNotImplementedError("signPsbt");
579
+ const waasClient = await waasProvider.getWaasClient();
580
+ const { signature: signedSessionId } = await getSignedSessionId(sdkClient);
581
+ const mfaToken = await consumeMfaTokenIfRequiredForAction({ mfaAction: MFAAction.WalletWaasSign }, sdkClient);
582
+ return { signedPsbt: await waasClient.signTransaction({
583
+ authToken: sdkClient.token ?? void 0,
584
+ mfaToken,
585
+ senderAddress: walletAccount.address,
586
+ signedSessionId,
587
+ transaction: request.unsignedPsbtBase64
588
+ }) };
36
589
  };
37
- const signPsbts$1 = async ({ requests: _requests, walletAccount: _walletAccount }) => {
38
- throw new MethodNotImplementedError("signPsbts");
590
+ const signPsbts$1 = async ({ requests, walletAccount }) => {
591
+ assertDefined(walletAccount, "Wallet account is required");
592
+ return { signedPsbts: await Promise.all(requests.map(async (request) => {
593
+ const { signedPsbt } = await signPsbt$1({
594
+ request,
595
+ walletAccount
596
+ });
597
+ return signedPsbt;
598
+ })) };
39
599
  };
40
- const sendBitcoin$1 = async ({ transaction: _transaction, walletAccount }) => {
600
+ const signMessage = async ({ message, walletAccount }) => {
41
601
  assertDefined(walletAccount, "Wallet account is required");
42
- throw new MethodNotImplementedError("sendBitcoin");
602
+ const waasClient = await waasProvider.getWaasClient();
603
+ const { signature: signedSessionId } = await getSignedSessionId(sdkClient);
604
+ const mfaToken = await consumeMfaTokenIfRequiredForAction({ mfaAction: MFAAction.WalletWaasSign }, sdkClient);
605
+ return { signature: await waasClient.signMessage({
606
+ accountAddress: walletAccount.address,
607
+ authToken: sdkClient.token ?? void 0,
608
+ bitcoinConfig: { network: "mainnet" },
609
+ message,
610
+ mfaToken,
611
+ signedSessionId
612
+ }) };
43
613
  };
44
- const signMessage = async ({ message: _message, walletAccount, addressType: _addressType, protocol: _protocol }) => {
614
+ const buildPsbt$1 = async ({ transaction, walletAccount }) => {
45
615
  assertDefined(walletAccount, "Wallet account is required");
46
- throw new MethodNotImplementedError("signMessage");
616
+ const publicKeyHex = getPublicKeyForWalletAccount(walletAccount, sdkClient);
617
+ return buildPsbt({
618
+ accountAddress: walletAccount.address,
619
+ amountInSatoshis: transaction.amount,
620
+ feePriority: transaction.feePriority ?? "medium",
621
+ network: networks.bitcoin,
622
+ publicKeyHex,
623
+ recipientAddress: transaction.recipientAddress
624
+ });
625
+ };
626
+ const sendBitcoin$1 = async ({ transaction, walletAccount }) => {
627
+ assertDefined(walletAccount, "Wallet account is required");
628
+ const { signedPsbt } = await signPsbt$1({
629
+ request: {
630
+ allowedSighash: [],
631
+ unsignedPsbtBase64: await buildPsbt$1({
632
+ transaction: {
633
+ amount: transaction.amount,
634
+ recipientAddress: transaction.recipientAddress
635
+ },
636
+ walletAccount
637
+ })
638
+ },
639
+ walletAccount
640
+ });
641
+ const psbt = Psbt.fromBase64(signedPsbt, { network: networks.bitcoin });
642
+ psbt.finalizeAllInputs();
643
+ return { transactionId: await broadcastTransaction(psbt.extractTransaction().toHex()) };
47
644
  };
48
645
  return {
49
646
  ...waasProvider,
647
+ buildPsbt: buildPsbt$1,
50
648
  chain,
51
649
  getActiveNetworkId,
52
650
  getConnectedAddresses,
@@ -105,37 +703,6 @@ const addBitcoinExtension = (client = getDefaultClient()) => {
105
703
  addWaasBitcoinExtension(client);
106
704
  };
107
705
 
108
- //#endregion
109
- //#region src/errors/TransactionBroadcastFailedError.ts
110
- var TransactionBroadcastFailedError = class extends BaseError {
111
- response;
112
- constructor({ response }) {
113
- super({
114
- cause: null,
115
- code: "transaction_broadcast_failed_error",
116
- details: `Status: ${response.status} ${response.statusText}`,
117
- docsUrl: null,
118
- name: "TransactionBroadcastFailedError",
119
- shortMessage: "Failed to broadcast transaction to mempool"
120
- });
121
- this.response = response;
122
- }
123
- };
124
-
125
- //#endregion
126
- //#region src/errors/TransactionRequiredError.ts
127
- var TransactionRequiredError = class extends BaseError {
128
- constructor() {
129
- super({
130
- cause: null,
131
- code: "transaction_required_error",
132
- docsUrl: null,
133
- name: "TransactionRequiredError",
134
- shortMessage: "No transaction specified for broadcast"
135
- });
136
- }
137
- };
138
-
139
706
  //#endregion
140
707
  //#region src/isBitcoinNetworkProvider/isBitcoinNetworkProvider.ts
141
708
  /**