@xchainjs/xchain-bitcoin 2.0.10 → 2.2.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.
- package/lib/client.d.ts +72 -4
- package/lib/clientKeystore.d.ts +28 -2
- package/lib/clientLedger.d.ts +14 -1
- package/lib/index.esm.js +335 -93
- package/lib/index.js +334 -93
- package/package.json +2 -2
package/lib/client.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AssetInfo, FeeRate } from '@xchainjs/xchain-client';
|
|
2
2
|
import { Address } from '@xchainjs/xchain-util';
|
|
3
|
-
import { Client as UTXOClient, PreparedTx, TxParams, UTXO, UtxoClientParams } from '@xchainjs/xchain-utxo';
|
|
3
|
+
import { Client as UTXOClient, PreparedTx, TxParams, UTXO, UtxoClientParams, UtxoSelectionPreferences } from '@xchainjs/xchain-utxo';
|
|
4
4
|
import * as Bitcoin from 'bitcoinjs-lib';
|
|
5
5
|
import { AddressFormat } from './types';
|
|
6
6
|
export declare const defaultBTCParams: UtxoClientParams;
|
|
@@ -43,9 +43,25 @@ declare abstract class Client extends UTXOClient {
|
|
|
43
43
|
*/
|
|
44
44
|
protected getFeeFromUtxos(inputs: UTXO[], feeRate: FeeRate, data?: Buffer | null): number;
|
|
45
45
|
/**
|
|
46
|
-
*
|
|
46
|
+
* Enhanced Bitcoin transaction builder with comprehensive validation and optimal UTXO selection
|
|
47
|
+
* @param params Transaction parameters
|
|
48
|
+
* @returns Enhanced transaction build result with PSBT, UTXOs, and inputs
|
|
49
|
+
*/
|
|
50
|
+
buildTxEnhanced({ amount, recipient, memo, feeRate, sender, spendPendingUTXO, utxoSelectionPreferences, selectedUtxos, }: TxParams & {
|
|
51
|
+
feeRate: FeeRate;
|
|
52
|
+
sender: Address;
|
|
53
|
+
spendPendingUTXO?: boolean;
|
|
54
|
+
utxoSelectionPreferences?: UtxoSelectionPreferences;
|
|
55
|
+
selectedUtxos?: UTXO[];
|
|
56
|
+
}): Promise<{
|
|
57
|
+
psbt: Bitcoin.Psbt;
|
|
58
|
+
utxos: UTXO[];
|
|
59
|
+
inputs: UTXO[];
|
|
60
|
+
}>;
|
|
61
|
+
/**
|
|
62
|
+
* Build a Bitcoin transaction with enhanced validation and performance.
|
|
63
|
+
* Now uses the enhanced logic internally while maintaining the same API.
|
|
47
64
|
* @param param0
|
|
48
|
-
* @deprecated
|
|
49
65
|
*/
|
|
50
66
|
buildTx({ amount, recipient, memo, feeRate, sender, spendPendingUTXO, }: TxParams & {
|
|
51
67
|
feeRate: FeeRate;
|
|
@@ -58,8 +74,60 @@ declare abstract class Client extends UTXOClient {
|
|
|
58
74
|
inputs: UTXO[];
|
|
59
75
|
}>;
|
|
60
76
|
/**
|
|
61
|
-
*
|
|
77
|
+
* Send maximum possible amount (sweep) with optimal fee calculation
|
|
78
|
+
* @param params Send max parameters
|
|
79
|
+
* @returns Transaction details with maximum sendable amount
|
|
80
|
+
*/
|
|
81
|
+
sendMax({ sender, recipient, memo, feeRate, spendPendingUTXO, utxoSelectionPreferences, selectedUtxos, }: {
|
|
82
|
+
sender: Address;
|
|
83
|
+
recipient: Address;
|
|
84
|
+
memo?: string;
|
|
85
|
+
feeRate: FeeRate;
|
|
86
|
+
spendPendingUTXO?: boolean;
|
|
87
|
+
utxoSelectionPreferences?: UtxoSelectionPreferences;
|
|
88
|
+
selectedUtxos?: UTXO[];
|
|
89
|
+
}): Promise<{
|
|
90
|
+
psbt: Bitcoin.Psbt;
|
|
91
|
+
utxos: UTXO[];
|
|
92
|
+
inputs: UTXO[];
|
|
93
|
+
maxAmount: number;
|
|
94
|
+
fee: number;
|
|
95
|
+
}>;
|
|
96
|
+
/**
|
|
97
|
+
* Prepare maximum amount transfer (sweep transaction)
|
|
98
|
+
* @param params Send max parameters
|
|
99
|
+
* @returns Prepared transaction with maximum sendable amount
|
|
100
|
+
*/
|
|
101
|
+
prepareMaxTx({ sender, recipient, memo, feeRate, spendPendingUTXO, utxoSelectionPreferences, selectedUtxos, }: {
|
|
102
|
+
sender: Address;
|
|
103
|
+
recipient: Address;
|
|
104
|
+
memo?: string;
|
|
105
|
+
feeRate: FeeRate;
|
|
106
|
+
spendPendingUTXO?: boolean;
|
|
107
|
+
utxoSelectionPreferences?: UtxoSelectionPreferences;
|
|
108
|
+
selectedUtxos?: UTXO[];
|
|
109
|
+
}): Promise<PreparedTx & {
|
|
110
|
+
maxAmount: number;
|
|
111
|
+
fee: number;
|
|
112
|
+
}>;
|
|
113
|
+
/**
|
|
114
|
+
* Enhanced prepare transfer with comprehensive validation and optimal UTXO selection.
|
|
115
|
+
*
|
|
116
|
+
* @param params The transfer options with enhanced UTXO selection preferences.
|
|
117
|
+
* @returns The raw unsigned transaction with enhanced error handling.
|
|
118
|
+
*/
|
|
119
|
+
prepareTxEnhanced({ sender, memo, amount, recipient, spendPendingUTXO, feeRate, utxoSelectionPreferences, selectedUtxos, }: TxParams & {
|
|
120
|
+
sender: Address;
|
|
121
|
+
feeRate: FeeRate;
|
|
122
|
+
spendPendingUTXO?: boolean;
|
|
123
|
+
utxoSelectionPreferences?: UtxoSelectionPreferences;
|
|
124
|
+
selectedUtxos?: UTXO[];
|
|
125
|
+
}): Promise<PreparedTx>;
|
|
126
|
+
/**
|
|
127
|
+
* Prepare transfer with enhanced validation and performance.
|
|
128
|
+
* Now uses the enhanced logic internally while maintaining the same API.
|
|
62
129
|
*
|
|
130
|
+
* @deprecated Use `prepareTxEnhanced` directly for explicit enhanced UTXO selection.
|
|
63
131
|
* @param {TxParams&Address&FeeRate&boolean} params The transfer options.
|
|
64
132
|
* @returns {PreparedTx} The raw unsigned transaction.
|
|
65
133
|
*/
|
package/lib/clientKeystore.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { FeeRate, TxHash } from '@xchainjs/xchain-client';
|
|
2
2
|
import { Address } from '@xchainjs/xchain-util';
|
|
3
|
-
import { TxParams, UtxoClientParams } from '@xchainjs/xchain-utxo';
|
|
3
|
+
import { TxParams, UTXO, UtxoClientParams, UtxoSelectionPreferences } from '@xchainjs/xchain-utxo';
|
|
4
4
|
import { Client } from './client';
|
|
5
5
|
import { AddressFormat } from './types';
|
|
6
6
|
/**
|
|
@@ -41,11 +41,37 @@ declare class ClientKeystore extends Client {
|
|
|
41
41
|
* Transfer BTC.
|
|
42
42
|
*
|
|
43
43
|
* @param {TxParams&FeeRate} params The transfer options including the fee rate.
|
|
44
|
-
* @returns {Promise<TxHash
|
|
44
|
+
* @returns {Promise<TxHash>} A promise that resolves to the transaction hash.
|
|
45
45
|
* @throws {"memo too long"} Thrown if the memo is longer than 80 characters.
|
|
46
46
|
*/
|
|
47
47
|
transfer(params: TxParams & {
|
|
48
48
|
feeRate?: FeeRate;
|
|
49
|
+
utxoSelectionPreferences?: UtxoSelectionPreferences;
|
|
50
|
+
selectedUtxos?: UTXO[];
|
|
49
51
|
}): Promise<TxHash>;
|
|
52
|
+
/**
|
|
53
|
+
* Transfer the maximum amount of Bitcoin (sweep).
|
|
54
|
+
*
|
|
55
|
+
* Calculates the maximum sendable amount after fees, signs, and broadcasts the transaction.
|
|
56
|
+
* @param {Object} params The transfer parameters.
|
|
57
|
+
* @param {string} params.recipient The recipient address.
|
|
58
|
+
* @param {string} [params.memo] Optional memo for the transaction.
|
|
59
|
+
* @param {FeeRate} [params.feeRate] Optional fee rate. Defaults to 'fast' rate.
|
|
60
|
+
* @param {number} [params.walletIndex] Optional wallet index. Defaults to 0.
|
|
61
|
+
* @param {UtxoSelectionPreferences} [params.utxoSelectionPreferences] Optional UTXO selection preferences.
|
|
62
|
+
* @returns {Promise<{ hash: TxHash; maxAmount: number; fee: number }>} The transaction hash, amount sent, and fee.
|
|
63
|
+
*/
|
|
64
|
+
transferMax(params: {
|
|
65
|
+
recipient: Address;
|
|
66
|
+
memo?: string;
|
|
67
|
+
feeRate?: FeeRate;
|
|
68
|
+
walletIndex?: number;
|
|
69
|
+
utxoSelectionPreferences?: UtxoSelectionPreferences;
|
|
70
|
+
selectedUtxos?: UTXO[];
|
|
71
|
+
}): Promise<{
|
|
72
|
+
hash: TxHash;
|
|
73
|
+
maxAmount: number;
|
|
74
|
+
fee: number;
|
|
75
|
+
}>;
|
|
50
76
|
}
|
|
51
77
|
export { ClientKeystore };
|
package/lib/clientLedger.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import AppBtc from '@ledgerhq/hw-app-btc';
|
|
2
2
|
import { FeeRate, TxHash } from '@xchainjs/xchain-client';
|
|
3
3
|
import { Address } from '@xchainjs/xchain-util';
|
|
4
|
-
import { TxParams, UtxoClientParams } from '@xchainjs/xchain-utxo';
|
|
4
|
+
import { TxParams, UTXO, UtxoClientParams, UtxoSelectionPreferences } from '@xchainjs/xchain-utxo';
|
|
5
5
|
import { Client } from './client';
|
|
6
6
|
import { AddressFormat } from './types';
|
|
7
7
|
/**
|
|
@@ -19,6 +19,19 @@ declare class ClientLedger extends Client {
|
|
|
19
19
|
getAddressAsync(index?: number, verify?: boolean): Promise<Address>;
|
|
20
20
|
transfer(params: TxParams & {
|
|
21
21
|
feeRate?: FeeRate;
|
|
22
|
+
utxoSelectionPreferences?: UtxoSelectionPreferences;
|
|
22
23
|
}): Promise<TxHash>;
|
|
24
|
+
transferMax(params: {
|
|
25
|
+
recipient: Address;
|
|
26
|
+
memo?: string;
|
|
27
|
+
feeRate?: FeeRate;
|
|
28
|
+
walletIndex?: number;
|
|
29
|
+
utxoSelectionPreferences?: UtxoSelectionPreferences;
|
|
30
|
+
selectedUtxos?: UTXO[];
|
|
31
|
+
}): Promise<{
|
|
32
|
+
hash: TxHash;
|
|
33
|
+
maxAmount: number;
|
|
34
|
+
fee: number;
|
|
35
|
+
}>;
|
|
23
36
|
}
|
|
24
37
|
export { ClientLedger };
|
package/lib/index.esm.js
CHANGED
|
@@ -4,8 +4,7 @@ import { getSeed } from '@xchainjs/xchain-crypto';
|
|
|
4
4
|
import { HDKey } from '@scure/bip32';
|
|
5
5
|
import * as Bitcoin from 'bitcoinjs-lib';
|
|
6
6
|
import { ECPairFactory } from 'ecpair';
|
|
7
|
-
import { Client as Client$1 } from '@xchainjs/xchain-utxo';
|
|
8
|
-
import accumulative from 'coinselect/accumulative.js';
|
|
7
|
+
import { Client as Client$1, UtxoTransactionValidator, UtxoError } from '@xchainjs/xchain-utxo';
|
|
9
8
|
import { AssetType } from '@xchainjs/xchain-util';
|
|
10
9
|
import { SochainProvider, SochainNetwork, HaskoinProvider, HaskoinNetwork, BlockcypherProvider, BlockcypherNetwork, BitgoProvider } from '@xchainjs/xchain-utxo-providers';
|
|
11
10
|
import AppBtc from '@ledgerhq/hw-app-btc';
|
|
@@ -160,7 +159,7 @@ const validateAddress = (address, network) => {
|
|
|
160
159
|
Bitcoin.address.toOutputScript(address, btcNetwork(network)); // Try to convert the address to an output script using the specified network
|
|
161
160
|
return true; // If successful, the address is valid
|
|
162
161
|
}
|
|
163
|
-
catch (
|
|
162
|
+
catch (_a) {
|
|
164
163
|
return false; // If an error occurs, the address is invalid
|
|
165
164
|
}
|
|
166
165
|
};
|
|
@@ -285,98 +284,263 @@ class Client extends Client$1 {
|
|
|
285
284
|
return fee > MIN_TX_FEE ? fee : MIN_TX_FEE;
|
|
286
285
|
}
|
|
287
286
|
/**
|
|
288
|
-
*
|
|
287
|
+
* Enhanced Bitcoin transaction builder with comprehensive validation and optimal UTXO selection
|
|
288
|
+
* @param params Transaction parameters
|
|
289
|
+
* @returns Enhanced transaction build result with PSBT, UTXOs, and inputs
|
|
290
|
+
*/
|
|
291
|
+
buildTxEnhanced(_a) {
|
|
292
|
+
return __awaiter(this, arguments, void 0, function* ({ amount, recipient, memo, feeRate, sender, spendPendingUTXO = true, utxoSelectionPreferences, selectedUtxos, }) {
|
|
293
|
+
try {
|
|
294
|
+
// Comprehensive input validation
|
|
295
|
+
this.validateTransactionInputs({
|
|
296
|
+
amount,
|
|
297
|
+
recipient,
|
|
298
|
+
memo,
|
|
299
|
+
sender,
|
|
300
|
+
feeRate,
|
|
301
|
+
});
|
|
302
|
+
// Use provided UTXOs (coin control) or fetch from chain
|
|
303
|
+
let utxos;
|
|
304
|
+
if (selectedUtxos && selectedUtxos.length > 0) {
|
|
305
|
+
UtxoTransactionValidator.validateUtxoSet(selectedUtxos);
|
|
306
|
+
utxos = selectedUtxos;
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
const confirmedOnly = !spendPendingUTXO;
|
|
310
|
+
utxos = yield this.getValidatedUtxos(sender, confirmedOnly);
|
|
311
|
+
}
|
|
312
|
+
const compiledMemo = memo ? this.compileMemo(memo) : null;
|
|
313
|
+
const targetValue = amount.amount().toNumber();
|
|
314
|
+
const extraOutputs = 1 + (compiledMemo ? 1 : 0); // recipient + optional memo (change calculated separately)
|
|
315
|
+
// Enhanced UTXO selection
|
|
316
|
+
const selectionResult = this.selectUtxosForTransaction(utxos, targetValue, Math.ceil(feeRate), extraOutputs, utxoSelectionPreferences);
|
|
317
|
+
const psbt = new Bitcoin.Psbt({ network: btcNetwork(this.network) });
|
|
318
|
+
// Add inputs based on selection
|
|
319
|
+
if (this.addressFormat === AddressFormat.P2WPKH) {
|
|
320
|
+
selectionResult.inputs.forEach((utxo) => {
|
|
321
|
+
if (!utxo.witnessUtxo) {
|
|
322
|
+
throw UtxoError.fromUnknown(new Error(`Missing witnessUtxo for UTXO ${utxo.hash}:${utxo.index}`), 'buildTxPsbt');
|
|
323
|
+
}
|
|
324
|
+
psbt.addInput({
|
|
325
|
+
hash: utxo.hash,
|
|
326
|
+
index: utxo.index,
|
|
327
|
+
witnessUtxo: utxo.witnessUtxo,
|
|
328
|
+
});
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
else {
|
|
332
|
+
const { pubkey, output } = Bitcoin.payments.p2tr({
|
|
333
|
+
address: sender,
|
|
334
|
+
});
|
|
335
|
+
selectionResult.inputs.forEach((utxo) => psbt.addInput({
|
|
336
|
+
hash: utxo.hash,
|
|
337
|
+
index: utxo.index,
|
|
338
|
+
witnessUtxo: { value: utxo.value, script: output },
|
|
339
|
+
tapInternalKey: pubkey,
|
|
340
|
+
}));
|
|
341
|
+
}
|
|
342
|
+
// Add recipient output
|
|
343
|
+
psbt.addOutput({
|
|
344
|
+
address: recipient,
|
|
345
|
+
value: targetValue,
|
|
346
|
+
});
|
|
347
|
+
// Add change output if needed
|
|
348
|
+
if (selectionResult.changeAmount > 0) {
|
|
349
|
+
psbt.addOutput({
|
|
350
|
+
address: sender,
|
|
351
|
+
value: selectionResult.changeAmount,
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
// Add memo output if present
|
|
355
|
+
if (compiledMemo) {
|
|
356
|
+
psbt.addOutput({ script: compiledMemo, value: 0 });
|
|
357
|
+
}
|
|
358
|
+
return { psbt, utxos, inputs: selectionResult.inputs };
|
|
359
|
+
}
|
|
360
|
+
catch (error) {
|
|
361
|
+
if (UtxoError.isUtxoError(error)) {
|
|
362
|
+
throw error;
|
|
363
|
+
}
|
|
364
|
+
throw UtxoError.fromUnknown(error, 'buildTxEnhanced');
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Build a Bitcoin transaction with enhanced validation and performance.
|
|
370
|
+
* Now uses the enhanced logic internally while maintaining the same API.
|
|
289
371
|
* @param param0
|
|
290
|
-
* @deprecated
|
|
291
372
|
*/
|
|
292
373
|
buildTx(_a) {
|
|
293
374
|
return __awaiter(this, arguments, void 0, function* ({ amount, recipient, memo, feeRate, sender, spendPendingUTXO = true, }) {
|
|
294
|
-
//
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
const confirmedOnly = !spendPendingUTXO;
|
|
303
|
-
// Scan UTXOs associated with the sender's address.
|
|
304
|
-
const utxos = yield this.scanUTXOs(sender, confirmedOnly);
|
|
305
|
-
// Throw an error if there are no available UTXOs to cover the transaction.
|
|
306
|
-
if (utxos.length === 0)
|
|
307
|
-
throw new Error('Insufficient Balance for transaction');
|
|
308
|
-
// Round up the fee rate to the nearest integer.
|
|
309
|
-
const feeRateWhole = Math.ceil(feeRate);
|
|
310
|
-
// Compile the memo into a Buffer if provided.
|
|
311
|
-
const compiledMemo = memo ? this.compileMemo(memo) : null;
|
|
312
|
-
// Initialize an array to store the target outputs of the transaction.
|
|
313
|
-
const targetOutputs = [];
|
|
314
|
-
// 1. Add the recipient address and amount to the target outputs.
|
|
315
|
-
targetOutputs.push({
|
|
316
|
-
address: recipient,
|
|
317
|
-
value: amount.amount().toNumber(),
|
|
375
|
+
// Use the enhanced logic internally while maintaining the same API
|
|
376
|
+
return this.buildTxEnhanced({
|
|
377
|
+
amount,
|
|
378
|
+
recipient,
|
|
379
|
+
memo,
|
|
380
|
+
feeRate,
|
|
381
|
+
sender,
|
|
382
|
+
spendPendingUTXO,
|
|
318
383
|
});
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Send maximum possible amount (sweep) with optimal fee calculation
|
|
388
|
+
* @param params Send max parameters
|
|
389
|
+
* @returns Transaction details with maximum sendable amount
|
|
390
|
+
*/
|
|
391
|
+
sendMax(_a) {
|
|
392
|
+
return __awaiter(this, arguments, void 0, function* ({ sender, recipient, memo, feeRate, spendPendingUTXO = true, utxoSelectionPreferences, selectedUtxos, }) {
|
|
393
|
+
try {
|
|
394
|
+
// Basic validation (skip amount validation since we're calculating max)
|
|
395
|
+
if (!(recipient === null || recipient === void 0 ? void 0 : recipient.trim())) {
|
|
396
|
+
throw UtxoError.invalidAddress(recipient, this.network);
|
|
397
|
+
}
|
|
398
|
+
if (!this.validateAddress(recipient)) {
|
|
399
|
+
throw UtxoError.invalidAddress(recipient, this.network);
|
|
400
|
+
}
|
|
401
|
+
if (!this.validateAddress(sender)) {
|
|
402
|
+
throw UtxoError.invalidAddress(sender, this.network);
|
|
403
|
+
}
|
|
404
|
+
// Memo validation is handled by validateTransactionInputs
|
|
405
|
+
// Use provided UTXOs (coin control) or fetch from chain
|
|
406
|
+
let utxos;
|
|
407
|
+
if (selectedUtxos && selectedUtxos.length > 0) {
|
|
408
|
+
UtxoTransactionValidator.validateUtxoSet(selectedUtxos);
|
|
409
|
+
utxos = selectedUtxos;
|
|
410
|
+
}
|
|
411
|
+
else {
|
|
412
|
+
const confirmedOnly = !spendPendingUTXO;
|
|
413
|
+
utxos = yield this.getValidatedUtxos(sender, confirmedOnly);
|
|
414
|
+
}
|
|
415
|
+
// Calculate maximum sendable amount
|
|
416
|
+
const maxCalc = this.calculateMaxSendableAmount(utxos, Math.ceil(feeRate), !!memo, utxoSelectionPreferences);
|
|
417
|
+
const compiledMemo = memo ? this.compileMemo(memo) : null;
|
|
418
|
+
const psbt = new Bitcoin.Psbt({ network: btcNetwork(this.network) });
|
|
419
|
+
// Add inputs
|
|
420
|
+
if (this.addressFormat === AddressFormat.P2WPKH) {
|
|
421
|
+
maxCalc.inputs.forEach((utxo) => {
|
|
422
|
+
if (!utxo.witnessUtxo) {
|
|
423
|
+
throw UtxoError.fromUnknown(new Error(`Missing witnessUtxo for UTXO ${utxo.hash}:${utxo.index}`), 'sendMax');
|
|
424
|
+
}
|
|
425
|
+
psbt.addInput({
|
|
426
|
+
hash: utxo.hash,
|
|
427
|
+
index: utxo.index,
|
|
428
|
+
witnessUtxo: utxo.witnessUtxo,
|
|
429
|
+
});
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
else {
|
|
433
|
+
const { pubkey, output } = Bitcoin.payments.p2tr({
|
|
434
|
+
address: sender,
|
|
435
|
+
});
|
|
436
|
+
maxCalc.inputs.forEach((utxo) => psbt.addInput({
|
|
437
|
+
hash: utxo.hash,
|
|
438
|
+
index: utxo.index,
|
|
439
|
+
witnessUtxo: { value: utxo.value, script: output },
|
|
440
|
+
tapInternalKey: pubkey,
|
|
441
|
+
}));
|
|
442
|
+
}
|
|
443
|
+
// Add recipient output (max amount - no change)
|
|
444
|
+
psbt.addOutput({
|
|
445
|
+
address: recipient,
|
|
446
|
+
value: maxCalc.amount,
|
|
447
|
+
});
|
|
448
|
+
// Add memo output if present
|
|
449
|
+
if (compiledMemo) {
|
|
450
|
+
psbt.addOutput({ script: compiledMemo, value: 0 });
|
|
451
|
+
}
|
|
452
|
+
return {
|
|
453
|
+
psbt,
|
|
454
|
+
utxos,
|
|
455
|
+
inputs: maxCalc.inputs,
|
|
456
|
+
maxAmount: maxCalc.amount,
|
|
457
|
+
fee: maxCalc.fee,
|
|
458
|
+
};
|
|
322
459
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
throw
|
|
328
|
-
// Initialize a new Bitcoin PSBT object.
|
|
329
|
-
const psbt = new Bitcoin.Psbt({ network: btcNetwork(this.network) }); // Network-specific
|
|
330
|
-
if (this.addressFormat === AddressFormat.P2WPKH) {
|
|
331
|
-
// Add inputs to the PSBT from the accumulated inputs.
|
|
332
|
-
inputs.forEach((utxo) => psbt.addInput({
|
|
333
|
-
hash: utxo.hash,
|
|
334
|
-
index: utxo.index,
|
|
335
|
-
witnessUtxo: utxo.witnessUtxo,
|
|
336
|
-
}));
|
|
460
|
+
catch (error) {
|
|
461
|
+
if (UtxoError.isUtxoError(error)) {
|
|
462
|
+
throw error;
|
|
463
|
+
}
|
|
464
|
+
throw UtxoError.fromUnknown(error, 'sendMax');
|
|
337
465
|
}
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
/**
|
|
469
|
+
* Prepare maximum amount transfer (sweep transaction)
|
|
470
|
+
* @param params Send max parameters
|
|
471
|
+
* @returns Prepared transaction with maximum sendable amount
|
|
472
|
+
*/
|
|
473
|
+
prepareMaxTx(_a) {
|
|
474
|
+
return __awaiter(this, arguments, void 0, function* ({ sender, recipient, memo, feeRate, spendPendingUTXO = true, utxoSelectionPreferences, selectedUtxos, }) {
|
|
475
|
+
try {
|
|
476
|
+
const { psbt, utxos, inputs, maxAmount, fee } = yield this.sendMax({
|
|
477
|
+
sender,
|
|
478
|
+
recipient,
|
|
479
|
+
memo,
|
|
480
|
+
feeRate,
|
|
481
|
+
spendPendingUTXO,
|
|
482
|
+
utxoSelectionPreferences,
|
|
483
|
+
selectedUtxos,
|
|
341
484
|
});
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
485
|
+
return {
|
|
486
|
+
rawUnsignedTx: psbt.toBase64(),
|
|
487
|
+
utxos,
|
|
488
|
+
inputs,
|
|
489
|
+
maxAmount,
|
|
490
|
+
fee,
|
|
491
|
+
};
|
|
348
492
|
}
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
//an empty address means this is the change address
|
|
354
|
-
output.address = sender;
|
|
493
|
+
catch (error) {
|
|
494
|
+
if (UtxoError.isUtxoError(error)) {
|
|
495
|
+
console.error('Bitcoin max transaction preparation failed:', error.toJSON());
|
|
496
|
+
throw error;
|
|
355
497
|
}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
498
|
+
throw UtxoError.fromUnknown(error, 'prepareMaxTx');
|
|
499
|
+
}
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* Enhanced prepare transfer with comprehensive validation and optimal UTXO selection.
|
|
504
|
+
*
|
|
505
|
+
* @param params The transfer options with enhanced UTXO selection preferences.
|
|
506
|
+
* @returns The raw unsigned transaction with enhanced error handling.
|
|
507
|
+
*/
|
|
508
|
+
prepareTxEnhanced(_a) {
|
|
509
|
+
return __awaiter(this, arguments, void 0, function* ({ sender, memo, amount, recipient, spendPendingUTXO = true, feeRate, utxoSelectionPreferences, selectedUtxos, }) {
|
|
510
|
+
try {
|
|
511
|
+
const { psbt, utxos, inputs } = yield this.buildTxEnhanced({
|
|
512
|
+
sender,
|
|
513
|
+
recipient,
|
|
514
|
+
amount,
|
|
515
|
+
feeRate,
|
|
516
|
+
memo,
|
|
517
|
+
spendPendingUTXO,
|
|
518
|
+
utxoSelectionPreferences,
|
|
519
|
+
selectedUtxos,
|
|
520
|
+
});
|
|
521
|
+
return { rawUnsignedTx: psbt.toBase64(), utxos, inputs };
|
|
522
|
+
}
|
|
523
|
+
catch (error) {
|
|
524
|
+
if (UtxoError.isUtxoError(error)) {
|
|
525
|
+
console.error('Enhanced Bitcoin transaction preparation failed:', error.toJSON());
|
|
526
|
+
throw error;
|
|
365
527
|
}
|
|
366
|
-
|
|
367
|
-
|
|
528
|
+
throw UtxoError.fromUnknown(error, 'prepareTxEnhanced');
|
|
529
|
+
}
|
|
368
530
|
});
|
|
369
531
|
}
|
|
370
532
|
/**
|
|
371
|
-
* Prepare transfer.
|
|
533
|
+
* Prepare transfer with enhanced validation and performance.
|
|
534
|
+
* Now uses the enhanced logic internally while maintaining the same API.
|
|
372
535
|
*
|
|
536
|
+
* @deprecated Use `prepareTxEnhanced` directly for explicit enhanced UTXO selection.
|
|
373
537
|
* @param {TxParams&Address&FeeRate&boolean} params The transfer options.
|
|
374
538
|
* @returns {PreparedTx} The raw unsigned transaction.
|
|
375
539
|
*/
|
|
376
540
|
prepareTx(_a) {
|
|
377
541
|
return __awaiter(this, arguments, void 0, function* ({ sender, memo, amount, recipient, spendPendingUTXO = true, feeRate, }) {
|
|
378
|
-
//
|
|
379
|
-
|
|
542
|
+
// Use the enhanced logic internally while maintaining the same API
|
|
543
|
+
return this.prepareTxEnhanced({
|
|
380
544
|
sender,
|
|
381
545
|
recipient,
|
|
382
546
|
amount,
|
|
@@ -384,8 +548,6 @@ class Client extends Client$1 {
|
|
|
384
548
|
memo,
|
|
385
549
|
spendPendingUTXO,
|
|
386
550
|
});
|
|
387
|
-
// Return the raw unsigned transaction (PSBT) and associated UTXOs.
|
|
388
|
-
return { rawUnsignedTx: psbt.toBase64(), utxos, inputs };
|
|
389
551
|
});
|
|
390
552
|
}
|
|
391
553
|
}
|
|
@@ -471,7 +633,7 @@ class ClientKeystore extends Client {
|
|
|
471
633
|
* Transfer BTC.
|
|
472
634
|
*
|
|
473
635
|
* @param {TxParams&FeeRate} params The transfer options including the fee rate.
|
|
474
|
-
* @returns {Promise<TxHash
|
|
636
|
+
* @returns {Promise<TxHash>} A promise that resolves to the transaction hash.
|
|
475
637
|
* @throws {"memo too long"} Thrown if the memo is longer than 80 characters.
|
|
476
638
|
*/
|
|
477
639
|
transfer(params) {
|
|
@@ -484,8 +646,10 @@ class ClientKeystore extends Client {
|
|
|
484
646
|
const fromAddressIndex = (params === null || params === void 0 ? void 0 : params.walletIndex) || 0;
|
|
485
647
|
// Get the Bitcoin keys
|
|
486
648
|
const btcKeys = this.getBtcKeys(this.phrase, fromAddressIndex);
|
|
649
|
+
// Merge default preferences with caller-provided preferences
|
|
650
|
+
const mergedUtxoSelectionPreferences = Object.assign({ minimizeFee: true, avoidDust: true, minimizeInputs: false }, params.utxoSelectionPreferences);
|
|
487
651
|
// Prepare the transaction
|
|
488
|
-
const { rawUnsignedTx } = yield this.
|
|
652
|
+
const { rawUnsignedTx } = yield this.prepareTxEnhanced(Object.assign(Object.assign({}, params), { sender: this.getAddress(fromAddressIndex), feeRate, utxoSelectionPreferences: mergedUtxoSelectionPreferences, selectedUtxos: params.selectedUtxos }));
|
|
489
653
|
// Build the PSBT
|
|
490
654
|
const psbt = Bitcoin.Psbt.fromBase64(rawUnsignedTx);
|
|
491
655
|
// Sign all inputs
|
|
@@ -496,18 +660,44 @@ class ClientKeystore extends Client {
|
|
|
496
660
|
psbt.finalizeAllInputs();
|
|
497
661
|
// Extract the transaction hex
|
|
498
662
|
const txHex = psbt.extractTransaction().toHex();
|
|
499
|
-
//
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
663
|
+
// Broadcast the transaction and return the transaction hash
|
|
664
|
+
return yield this.roundRobinBroadcastTx(txHex);
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
/**
|
|
668
|
+
* Transfer the maximum amount of Bitcoin (sweep).
|
|
669
|
+
*
|
|
670
|
+
* Calculates the maximum sendable amount after fees, signs, and broadcasts the transaction.
|
|
671
|
+
* @param {Object} params The transfer parameters.
|
|
672
|
+
* @param {string} params.recipient The recipient address.
|
|
673
|
+
* @param {string} [params.memo] Optional memo for the transaction.
|
|
674
|
+
* @param {FeeRate} [params.feeRate] Optional fee rate. Defaults to 'fast' rate.
|
|
675
|
+
* @param {number} [params.walletIndex] Optional wallet index. Defaults to 0.
|
|
676
|
+
* @param {UtxoSelectionPreferences} [params.utxoSelectionPreferences] Optional UTXO selection preferences.
|
|
677
|
+
* @returns {Promise<{ hash: TxHash; maxAmount: number; fee: number }>} The transaction hash, amount sent, and fee.
|
|
678
|
+
*/
|
|
679
|
+
transferMax(params) {
|
|
680
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
681
|
+
const feeRate = params.feeRate || (yield this.getFeeRates())[FeeOption.Fast];
|
|
682
|
+
checkFeeBounds(this.feeBounds, feeRate);
|
|
683
|
+
const fromAddressIndex = params.walletIndex || 0;
|
|
684
|
+
const sender = yield this.getAddressAsync(fromAddressIndex);
|
|
685
|
+
const { psbt, maxAmount, fee } = yield this.sendMax({
|
|
686
|
+
sender,
|
|
687
|
+
recipient: params.recipient,
|
|
688
|
+
memo: params.memo,
|
|
689
|
+
feeRate,
|
|
690
|
+
utxoSelectionPreferences: params.utxoSelectionPreferences,
|
|
691
|
+
selectedUtxos: params.selectedUtxos,
|
|
692
|
+
});
|
|
693
|
+
const btcKeys = this.getBtcKeys(this.phrase, fromAddressIndex);
|
|
694
|
+
psbt.signAllInputs(this.addressFormat === AddressFormat.P2WPKH
|
|
695
|
+
? btcKeys
|
|
696
|
+
: btcKeys.tweak(Bitcoin.crypto.taggedHash('TapTweak', toXOnly(btcKeys.publicKey))));
|
|
697
|
+
psbt.finalizeAllInputs();
|
|
698
|
+
const txHex = psbt.extractTransaction().toHex();
|
|
699
|
+
const hash = yield this.roundRobinBroadcastTx(txHex);
|
|
700
|
+
return { hash, maxAmount, fee };
|
|
511
701
|
});
|
|
512
702
|
}
|
|
513
703
|
}
|
|
@@ -558,8 +748,16 @@ class ClientLedger extends Client {
|
|
|
558
748
|
checkFeeBounds(this.feeBounds, feeRate);
|
|
559
749
|
// Get sender address
|
|
560
750
|
const sender = yield this.getAddressAsync(fromAddressIndex);
|
|
561
|
-
//
|
|
562
|
-
const
|
|
751
|
+
// Create defaults and merge with caller-provided preferences
|
|
752
|
+
const defaults = {
|
|
753
|
+
minimizeFee: true,
|
|
754
|
+
avoidDust: true,
|
|
755
|
+
minimizeInputs: false,
|
|
756
|
+
};
|
|
757
|
+
const mergedUtxoSelectionPreferences = Object.assign(Object.assign({}, defaults), params.utxoSelectionPreferences);
|
|
758
|
+
// Prepare transaction using enhanced method with optimal UTXO selection
|
|
759
|
+
const { rawUnsignedTx, inputs } = yield this.prepareTxEnhanced(Object.assign(Object.assign({}, params), { sender,
|
|
760
|
+
feeRate, utxoSelectionPreferences: mergedUtxoSelectionPreferences }));
|
|
563
761
|
const psbt = Bitcoin.Psbt.fromBase64(rawUnsignedTx);
|
|
564
762
|
// Prepare Ledger inputs
|
|
565
763
|
const ledgerInputs = inputs.map(({ txHex, hash, index }) => {
|
|
@@ -594,6 +792,50 @@ class ClientLedger extends Client {
|
|
|
594
792
|
return txHash;
|
|
595
793
|
});
|
|
596
794
|
}
|
|
795
|
+
// Transfer max BTC from Ledger (sweep transaction)
|
|
796
|
+
transferMax(params) {
|
|
797
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
798
|
+
const app = yield this.getApp();
|
|
799
|
+
const fromAddressIndex = (params === null || params === void 0 ? void 0 : params.walletIndex) || 0;
|
|
800
|
+
const feeRate = params.feeRate || (yield this.getFeeRates())[FeeOption.Fast];
|
|
801
|
+
checkFeeBounds(this.feeBounds, feeRate);
|
|
802
|
+
const sender = yield this.getAddressAsync(fromAddressIndex);
|
|
803
|
+
const { rawUnsignedTx, inputs, maxAmount, fee } = yield this.prepareMaxTx({
|
|
804
|
+
sender,
|
|
805
|
+
recipient: params.recipient,
|
|
806
|
+
memo: params.memo,
|
|
807
|
+
feeRate,
|
|
808
|
+
utxoSelectionPreferences: params.utxoSelectionPreferences,
|
|
809
|
+
selectedUtxos: params.selectedUtxos,
|
|
810
|
+
});
|
|
811
|
+
const psbt = Bitcoin.Psbt.fromBase64(rawUnsignedTx);
|
|
812
|
+
const ledgerInputs = inputs.map(({ txHex, hash, index }) => {
|
|
813
|
+
if (!txHex) {
|
|
814
|
+
throw Error(`Missing 'txHex' for UTXO (txHash ${hash})`);
|
|
815
|
+
}
|
|
816
|
+
const utxoTx = Bitcoin.Transaction.fromHex(txHex);
|
|
817
|
+
const splittedTx = app.splitTransaction(txHex, utxoTx.hasWitnesses());
|
|
818
|
+
return [splittedTx, index, null, null];
|
|
819
|
+
});
|
|
820
|
+
const associatedKeysets = ledgerInputs.map(() => this.getFullDerivationPath(fromAddressIndex));
|
|
821
|
+
const unsignedHex = psbt.data.globalMap.unsignedTx.toBuffer().toString('hex');
|
|
822
|
+
const newTx = app.splitTransaction(unsignedHex, true);
|
|
823
|
+
const outputScriptHex = app.serializeTransactionOutputs(newTx).toString('hex');
|
|
824
|
+
const txHex = yield app.createPaymentTransaction({
|
|
825
|
+
inputs: ledgerInputs,
|
|
826
|
+
associatedKeysets,
|
|
827
|
+
outputScriptHex,
|
|
828
|
+
segwit: true,
|
|
829
|
+
useTrustedInputForSegwit: true,
|
|
830
|
+
additionals: [this.addressFormat === AddressFormat.P2TR ? 'bech32m' : 'bech32'],
|
|
831
|
+
});
|
|
832
|
+
const hash = yield this.broadcastTx(txHex);
|
|
833
|
+
if (!hash) {
|
|
834
|
+
throw Error('No Tx hash');
|
|
835
|
+
}
|
|
836
|
+
return { hash, maxAmount, fee };
|
|
837
|
+
});
|
|
838
|
+
}
|
|
597
839
|
}
|
|
598
840
|
|
|
599
841
|
export { AddressFormat, AssetBTC, BTCChain, BTC_DECIMAL, BTC_SATOSHI_SYMBOL, BTC_SYMBOL, BitgoProviders, BlockcypherDataProviders, ClientKeystore as Client, ClientLedger, HaskoinDataProviders, LOWER_FEE_BOUND, MIN_TX_FEE, SochainDataProviders, UPPER_FEE_BOUND, blockstreamExplorerProviders, defaultBTCParams, getPrefix, tapRootDerivationPaths, validateAddress };
|
package/lib/index.js
CHANGED
|
@@ -7,7 +7,6 @@ var bip32 = require('@scure/bip32');
|
|
|
7
7
|
var Bitcoin = require('bitcoinjs-lib');
|
|
8
8
|
var ecpair = require('ecpair');
|
|
9
9
|
var xchainUtxo = require('@xchainjs/xchain-utxo');
|
|
10
|
-
var accumulative = require('coinselect/accumulative.js');
|
|
11
10
|
var xchainUtil = require('@xchainjs/xchain-util');
|
|
12
11
|
var xchainUtxoProviders = require('@xchainjs/xchain-utxo-providers');
|
|
13
12
|
var AppBtc = require('@ledgerhq/hw-app-btc');
|
|
@@ -34,7 +33,6 @@ function _interopNamespace(e) {
|
|
|
34
33
|
|
|
35
34
|
var ecc__namespace = /*#__PURE__*/_interopNamespace(ecc);
|
|
36
35
|
var Bitcoin__namespace = /*#__PURE__*/_interopNamespace(Bitcoin);
|
|
37
|
-
var accumulative__default = /*#__PURE__*/_interopDefault(accumulative);
|
|
38
36
|
var AppBtc__default = /*#__PURE__*/_interopDefault(AppBtc);
|
|
39
37
|
|
|
40
38
|
exports.AddressFormat = void 0;
|
|
@@ -187,7 +185,7 @@ const validateAddress = (address, network) => {
|
|
|
187
185
|
Bitcoin__namespace.address.toOutputScript(address, btcNetwork(network)); // Try to convert the address to an output script using the specified network
|
|
188
186
|
return true; // If successful, the address is valid
|
|
189
187
|
}
|
|
190
|
-
catch (
|
|
188
|
+
catch (_a) {
|
|
191
189
|
return false; // If an error occurs, the address is invalid
|
|
192
190
|
}
|
|
193
191
|
};
|
|
@@ -312,98 +310,263 @@ class Client extends xchainUtxo.Client {
|
|
|
312
310
|
return fee > MIN_TX_FEE ? fee : MIN_TX_FEE;
|
|
313
311
|
}
|
|
314
312
|
/**
|
|
315
|
-
*
|
|
313
|
+
* Enhanced Bitcoin transaction builder with comprehensive validation and optimal UTXO selection
|
|
314
|
+
* @param params Transaction parameters
|
|
315
|
+
* @returns Enhanced transaction build result with PSBT, UTXOs, and inputs
|
|
316
|
+
*/
|
|
317
|
+
buildTxEnhanced(_a) {
|
|
318
|
+
return __awaiter(this, arguments, void 0, function* ({ amount, recipient, memo, feeRate, sender, spendPendingUTXO = true, utxoSelectionPreferences, selectedUtxos, }) {
|
|
319
|
+
try {
|
|
320
|
+
// Comprehensive input validation
|
|
321
|
+
this.validateTransactionInputs({
|
|
322
|
+
amount,
|
|
323
|
+
recipient,
|
|
324
|
+
memo,
|
|
325
|
+
sender,
|
|
326
|
+
feeRate,
|
|
327
|
+
});
|
|
328
|
+
// Use provided UTXOs (coin control) or fetch from chain
|
|
329
|
+
let utxos;
|
|
330
|
+
if (selectedUtxos && selectedUtxos.length > 0) {
|
|
331
|
+
xchainUtxo.UtxoTransactionValidator.validateUtxoSet(selectedUtxos);
|
|
332
|
+
utxos = selectedUtxos;
|
|
333
|
+
}
|
|
334
|
+
else {
|
|
335
|
+
const confirmedOnly = !spendPendingUTXO;
|
|
336
|
+
utxos = yield this.getValidatedUtxos(sender, confirmedOnly);
|
|
337
|
+
}
|
|
338
|
+
const compiledMemo = memo ? this.compileMemo(memo) : null;
|
|
339
|
+
const targetValue = amount.amount().toNumber();
|
|
340
|
+
const extraOutputs = 1 + (compiledMemo ? 1 : 0); // recipient + optional memo (change calculated separately)
|
|
341
|
+
// Enhanced UTXO selection
|
|
342
|
+
const selectionResult = this.selectUtxosForTransaction(utxos, targetValue, Math.ceil(feeRate), extraOutputs, utxoSelectionPreferences);
|
|
343
|
+
const psbt = new Bitcoin__namespace.Psbt({ network: btcNetwork(this.network) });
|
|
344
|
+
// Add inputs based on selection
|
|
345
|
+
if (this.addressFormat === exports.AddressFormat.P2WPKH) {
|
|
346
|
+
selectionResult.inputs.forEach((utxo) => {
|
|
347
|
+
if (!utxo.witnessUtxo) {
|
|
348
|
+
throw xchainUtxo.UtxoError.fromUnknown(new Error(`Missing witnessUtxo for UTXO ${utxo.hash}:${utxo.index}`), 'buildTxPsbt');
|
|
349
|
+
}
|
|
350
|
+
psbt.addInput({
|
|
351
|
+
hash: utxo.hash,
|
|
352
|
+
index: utxo.index,
|
|
353
|
+
witnessUtxo: utxo.witnessUtxo,
|
|
354
|
+
});
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
else {
|
|
358
|
+
const { pubkey, output } = Bitcoin__namespace.payments.p2tr({
|
|
359
|
+
address: sender,
|
|
360
|
+
});
|
|
361
|
+
selectionResult.inputs.forEach((utxo) => psbt.addInput({
|
|
362
|
+
hash: utxo.hash,
|
|
363
|
+
index: utxo.index,
|
|
364
|
+
witnessUtxo: { value: utxo.value, script: output },
|
|
365
|
+
tapInternalKey: pubkey,
|
|
366
|
+
}));
|
|
367
|
+
}
|
|
368
|
+
// Add recipient output
|
|
369
|
+
psbt.addOutput({
|
|
370
|
+
address: recipient,
|
|
371
|
+
value: targetValue,
|
|
372
|
+
});
|
|
373
|
+
// Add change output if needed
|
|
374
|
+
if (selectionResult.changeAmount > 0) {
|
|
375
|
+
psbt.addOutput({
|
|
376
|
+
address: sender,
|
|
377
|
+
value: selectionResult.changeAmount,
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
// Add memo output if present
|
|
381
|
+
if (compiledMemo) {
|
|
382
|
+
psbt.addOutput({ script: compiledMemo, value: 0 });
|
|
383
|
+
}
|
|
384
|
+
return { psbt, utxos, inputs: selectionResult.inputs };
|
|
385
|
+
}
|
|
386
|
+
catch (error) {
|
|
387
|
+
if (xchainUtxo.UtxoError.isUtxoError(error)) {
|
|
388
|
+
throw error;
|
|
389
|
+
}
|
|
390
|
+
throw xchainUtxo.UtxoError.fromUnknown(error, 'buildTxEnhanced');
|
|
391
|
+
}
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Build a Bitcoin transaction with enhanced validation and performance.
|
|
396
|
+
* Now uses the enhanced logic internally while maintaining the same API.
|
|
316
397
|
* @param param0
|
|
317
|
-
* @deprecated
|
|
318
398
|
*/
|
|
319
399
|
buildTx(_a) {
|
|
320
400
|
return __awaiter(this, arguments, void 0, function* ({ amount, recipient, memo, feeRate, sender, spendPendingUTXO = true, }) {
|
|
321
|
-
//
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
const confirmedOnly = !spendPendingUTXO;
|
|
330
|
-
// Scan UTXOs associated with the sender's address.
|
|
331
|
-
const utxos = yield this.scanUTXOs(sender, confirmedOnly);
|
|
332
|
-
// Throw an error if there are no available UTXOs to cover the transaction.
|
|
333
|
-
if (utxos.length === 0)
|
|
334
|
-
throw new Error('Insufficient Balance for transaction');
|
|
335
|
-
// Round up the fee rate to the nearest integer.
|
|
336
|
-
const feeRateWhole = Math.ceil(feeRate);
|
|
337
|
-
// Compile the memo into a Buffer if provided.
|
|
338
|
-
const compiledMemo = memo ? this.compileMemo(memo) : null;
|
|
339
|
-
// Initialize an array to store the target outputs of the transaction.
|
|
340
|
-
const targetOutputs = [];
|
|
341
|
-
// 1. Add the recipient address and amount to the target outputs.
|
|
342
|
-
targetOutputs.push({
|
|
343
|
-
address: recipient,
|
|
344
|
-
value: amount.amount().toNumber(),
|
|
401
|
+
// Use the enhanced logic internally while maintaining the same API
|
|
402
|
+
return this.buildTxEnhanced({
|
|
403
|
+
amount,
|
|
404
|
+
recipient,
|
|
405
|
+
memo,
|
|
406
|
+
feeRate,
|
|
407
|
+
sender,
|
|
408
|
+
spendPendingUTXO,
|
|
345
409
|
});
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Send maximum possible amount (sweep) with optimal fee calculation
|
|
414
|
+
* @param params Send max parameters
|
|
415
|
+
* @returns Transaction details with maximum sendable amount
|
|
416
|
+
*/
|
|
417
|
+
sendMax(_a) {
|
|
418
|
+
return __awaiter(this, arguments, void 0, function* ({ sender, recipient, memo, feeRate, spendPendingUTXO = true, utxoSelectionPreferences, selectedUtxos, }) {
|
|
419
|
+
try {
|
|
420
|
+
// Basic validation (skip amount validation since we're calculating max)
|
|
421
|
+
if (!(recipient === null || recipient === void 0 ? void 0 : recipient.trim())) {
|
|
422
|
+
throw xchainUtxo.UtxoError.invalidAddress(recipient, this.network);
|
|
423
|
+
}
|
|
424
|
+
if (!this.validateAddress(recipient)) {
|
|
425
|
+
throw xchainUtxo.UtxoError.invalidAddress(recipient, this.network);
|
|
426
|
+
}
|
|
427
|
+
if (!this.validateAddress(sender)) {
|
|
428
|
+
throw xchainUtxo.UtxoError.invalidAddress(sender, this.network);
|
|
429
|
+
}
|
|
430
|
+
// Memo validation is handled by validateTransactionInputs
|
|
431
|
+
// Use provided UTXOs (coin control) or fetch from chain
|
|
432
|
+
let utxos;
|
|
433
|
+
if (selectedUtxos && selectedUtxos.length > 0) {
|
|
434
|
+
xchainUtxo.UtxoTransactionValidator.validateUtxoSet(selectedUtxos);
|
|
435
|
+
utxos = selectedUtxos;
|
|
436
|
+
}
|
|
437
|
+
else {
|
|
438
|
+
const confirmedOnly = !spendPendingUTXO;
|
|
439
|
+
utxos = yield this.getValidatedUtxos(sender, confirmedOnly);
|
|
440
|
+
}
|
|
441
|
+
// Calculate maximum sendable amount
|
|
442
|
+
const maxCalc = this.calculateMaxSendableAmount(utxos, Math.ceil(feeRate), !!memo, utxoSelectionPreferences);
|
|
443
|
+
const compiledMemo = memo ? this.compileMemo(memo) : null;
|
|
444
|
+
const psbt = new Bitcoin__namespace.Psbt({ network: btcNetwork(this.network) });
|
|
445
|
+
// Add inputs
|
|
446
|
+
if (this.addressFormat === exports.AddressFormat.P2WPKH) {
|
|
447
|
+
maxCalc.inputs.forEach((utxo) => {
|
|
448
|
+
if (!utxo.witnessUtxo) {
|
|
449
|
+
throw xchainUtxo.UtxoError.fromUnknown(new Error(`Missing witnessUtxo for UTXO ${utxo.hash}:${utxo.index}`), 'sendMax');
|
|
450
|
+
}
|
|
451
|
+
psbt.addInput({
|
|
452
|
+
hash: utxo.hash,
|
|
453
|
+
index: utxo.index,
|
|
454
|
+
witnessUtxo: utxo.witnessUtxo,
|
|
455
|
+
});
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
else {
|
|
459
|
+
const { pubkey, output } = Bitcoin__namespace.payments.p2tr({
|
|
460
|
+
address: sender,
|
|
461
|
+
});
|
|
462
|
+
maxCalc.inputs.forEach((utxo) => psbt.addInput({
|
|
463
|
+
hash: utxo.hash,
|
|
464
|
+
index: utxo.index,
|
|
465
|
+
witnessUtxo: { value: utxo.value, script: output },
|
|
466
|
+
tapInternalKey: pubkey,
|
|
467
|
+
}));
|
|
468
|
+
}
|
|
469
|
+
// Add recipient output (max amount - no change)
|
|
470
|
+
psbt.addOutput({
|
|
471
|
+
address: recipient,
|
|
472
|
+
value: maxCalc.amount,
|
|
473
|
+
});
|
|
474
|
+
// Add memo output if present
|
|
475
|
+
if (compiledMemo) {
|
|
476
|
+
psbt.addOutput({ script: compiledMemo, value: 0 });
|
|
477
|
+
}
|
|
478
|
+
return {
|
|
479
|
+
psbt,
|
|
480
|
+
utxos,
|
|
481
|
+
inputs: maxCalc.inputs,
|
|
482
|
+
maxAmount: maxCalc.amount,
|
|
483
|
+
fee: maxCalc.fee,
|
|
484
|
+
};
|
|
349
485
|
}
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
throw
|
|
355
|
-
// Initialize a new Bitcoin PSBT object.
|
|
356
|
-
const psbt = new Bitcoin__namespace.Psbt({ network: btcNetwork(this.network) }); // Network-specific
|
|
357
|
-
if (this.addressFormat === exports.AddressFormat.P2WPKH) {
|
|
358
|
-
// Add inputs to the PSBT from the accumulated inputs.
|
|
359
|
-
inputs.forEach((utxo) => psbt.addInput({
|
|
360
|
-
hash: utxo.hash,
|
|
361
|
-
index: utxo.index,
|
|
362
|
-
witnessUtxo: utxo.witnessUtxo,
|
|
363
|
-
}));
|
|
486
|
+
catch (error) {
|
|
487
|
+
if (xchainUtxo.UtxoError.isUtxoError(error)) {
|
|
488
|
+
throw error;
|
|
489
|
+
}
|
|
490
|
+
throw xchainUtxo.UtxoError.fromUnknown(error, 'sendMax');
|
|
364
491
|
}
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* Prepare maximum amount transfer (sweep transaction)
|
|
496
|
+
* @param params Send max parameters
|
|
497
|
+
* @returns Prepared transaction with maximum sendable amount
|
|
498
|
+
*/
|
|
499
|
+
prepareMaxTx(_a) {
|
|
500
|
+
return __awaiter(this, arguments, void 0, function* ({ sender, recipient, memo, feeRate, spendPendingUTXO = true, utxoSelectionPreferences, selectedUtxos, }) {
|
|
501
|
+
try {
|
|
502
|
+
const { psbt, utxos, inputs, maxAmount, fee } = yield this.sendMax({
|
|
503
|
+
sender,
|
|
504
|
+
recipient,
|
|
505
|
+
memo,
|
|
506
|
+
feeRate,
|
|
507
|
+
spendPendingUTXO,
|
|
508
|
+
utxoSelectionPreferences,
|
|
509
|
+
selectedUtxos,
|
|
368
510
|
});
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
511
|
+
return {
|
|
512
|
+
rawUnsignedTx: psbt.toBase64(),
|
|
513
|
+
utxos,
|
|
514
|
+
inputs,
|
|
515
|
+
maxAmount,
|
|
516
|
+
fee,
|
|
517
|
+
};
|
|
375
518
|
}
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
//an empty address means this is the change address
|
|
381
|
-
output.address = sender;
|
|
519
|
+
catch (error) {
|
|
520
|
+
if (xchainUtxo.UtxoError.isUtxoError(error)) {
|
|
521
|
+
console.error('Bitcoin max transaction preparation failed:', error.toJSON());
|
|
522
|
+
throw error;
|
|
382
523
|
}
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
524
|
+
throw xchainUtxo.UtxoError.fromUnknown(error, 'prepareMaxTx');
|
|
525
|
+
}
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
/**
|
|
529
|
+
* Enhanced prepare transfer with comprehensive validation and optimal UTXO selection.
|
|
530
|
+
*
|
|
531
|
+
* @param params The transfer options with enhanced UTXO selection preferences.
|
|
532
|
+
* @returns The raw unsigned transaction with enhanced error handling.
|
|
533
|
+
*/
|
|
534
|
+
prepareTxEnhanced(_a) {
|
|
535
|
+
return __awaiter(this, arguments, void 0, function* ({ sender, memo, amount, recipient, spendPendingUTXO = true, feeRate, utxoSelectionPreferences, selectedUtxos, }) {
|
|
536
|
+
try {
|
|
537
|
+
const { psbt, utxos, inputs } = yield this.buildTxEnhanced({
|
|
538
|
+
sender,
|
|
539
|
+
recipient,
|
|
540
|
+
amount,
|
|
541
|
+
feeRate,
|
|
542
|
+
memo,
|
|
543
|
+
spendPendingUTXO,
|
|
544
|
+
utxoSelectionPreferences,
|
|
545
|
+
selectedUtxos,
|
|
546
|
+
});
|
|
547
|
+
return { rawUnsignedTx: psbt.toBase64(), utxos, inputs };
|
|
548
|
+
}
|
|
549
|
+
catch (error) {
|
|
550
|
+
if (xchainUtxo.UtxoError.isUtxoError(error)) {
|
|
551
|
+
console.error('Enhanced Bitcoin transaction preparation failed:', error.toJSON());
|
|
552
|
+
throw error;
|
|
392
553
|
}
|
|
393
|
-
|
|
394
|
-
|
|
554
|
+
throw xchainUtxo.UtxoError.fromUnknown(error, 'prepareTxEnhanced');
|
|
555
|
+
}
|
|
395
556
|
});
|
|
396
557
|
}
|
|
397
558
|
/**
|
|
398
|
-
* Prepare transfer.
|
|
559
|
+
* Prepare transfer with enhanced validation and performance.
|
|
560
|
+
* Now uses the enhanced logic internally while maintaining the same API.
|
|
399
561
|
*
|
|
562
|
+
* @deprecated Use `prepareTxEnhanced` directly for explicit enhanced UTXO selection.
|
|
400
563
|
* @param {TxParams&Address&FeeRate&boolean} params The transfer options.
|
|
401
564
|
* @returns {PreparedTx} The raw unsigned transaction.
|
|
402
565
|
*/
|
|
403
566
|
prepareTx(_a) {
|
|
404
567
|
return __awaiter(this, arguments, void 0, function* ({ sender, memo, amount, recipient, spendPendingUTXO = true, feeRate, }) {
|
|
405
|
-
//
|
|
406
|
-
|
|
568
|
+
// Use the enhanced logic internally while maintaining the same API
|
|
569
|
+
return this.prepareTxEnhanced({
|
|
407
570
|
sender,
|
|
408
571
|
recipient,
|
|
409
572
|
amount,
|
|
@@ -411,8 +574,6 @@ class Client extends xchainUtxo.Client {
|
|
|
411
574
|
memo,
|
|
412
575
|
spendPendingUTXO,
|
|
413
576
|
});
|
|
414
|
-
// Return the raw unsigned transaction (PSBT) and associated UTXOs.
|
|
415
|
-
return { rawUnsignedTx: psbt.toBase64(), utxos, inputs };
|
|
416
577
|
});
|
|
417
578
|
}
|
|
418
579
|
}
|
|
@@ -498,7 +659,7 @@ class ClientKeystore extends Client {
|
|
|
498
659
|
* Transfer BTC.
|
|
499
660
|
*
|
|
500
661
|
* @param {TxParams&FeeRate} params The transfer options including the fee rate.
|
|
501
|
-
* @returns {Promise<TxHash
|
|
662
|
+
* @returns {Promise<TxHash>} A promise that resolves to the transaction hash.
|
|
502
663
|
* @throws {"memo too long"} Thrown if the memo is longer than 80 characters.
|
|
503
664
|
*/
|
|
504
665
|
transfer(params) {
|
|
@@ -511,8 +672,10 @@ class ClientKeystore extends Client {
|
|
|
511
672
|
const fromAddressIndex = (params === null || params === void 0 ? void 0 : params.walletIndex) || 0;
|
|
512
673
|
// Get the Bitcoin keys
|
|
513
674
|
const btcKeys = this.getBtcKeys(this.phrase, fromAddressIndex);
|
|
675
|
+
// Merge default preferences with caller-provided preferences
|
|
676
|
+
const mergedUtxoSelectionPreferences = Object.assign({ minimizeFee: true, avoidDust: true, minimizeInputs: false }, params.utxoSelectionPreferences);
|
|
514
677
|
// Prepare the transaction
|
|
515
|
-
const { rawUnsignedTx } = yield this.
|
|
678
|
+
const { rawUnsignedTx } = yield this.prepareTxEnhanced(Object.assign(Object.assign({}, params), { sender: this.getAddress(fromAddressIndex), feeRate, utxoSelectionPreferences: mergedUtxoSelectionPreferences, selectedUtxos: params.selectedUtxos }));
|
|
516
679
|
// Build the PSBT
|
|
517
680
|
const psbt = Bitcoin__namespace.Psbt.fromBase64(rawUnsignedTx);
|
|
518
681
|
// Sign all inputs
|
|
@@ -523,18 +686,44 @@ class ClientKeystore extends Client {
|
|
|
523
686
|
psbt.finalizeAllInputs();
|
|
524
687
|
// Extract the transaction hex
|
|
525
688
|
const txHex = psbt.extractTransaction().toHex();
|
|
526
|
-
//
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
689
|
+
// Broadcast the transaction and return the transaction hash
|
|
690
|
+
return yield this.roundRobinBroadcastTx(txHex);
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
/**
|
|
694
|
+
* Transfer the maximum amount of Bitcoin (sweep).
|
|
695
|
+
*
|
|
696
|
+
* Calculates the maximum sendable amount after fees, signs, and broadcasts the transaction.
|
|
697
|
+
* @param {Object} params The transfer parameters.
|
|
698
|
+
* @param {string} params.recipient The recipient address.
|
|
699
|
+
* @param {string} [params.memo] Optional memo for the transaction.
|
|
700
|
+
* @param {FeeRate} [params.feeRate] Optional fee rate. Defaults to 'fast' rate.
|
|
701
|
+
* @param {number} [params.walletIndex] Optional wallet index. Defaults to 0.
|
|
702
|
+
* @param {UtxoSelectionPreferences} [params.utxoSelectionPreferences] Optional UTXO selection preferences.
|
|
703
|
+
* @returns {Promise<{ hash: TxHash; maxAmount: number; fee: number }>} The transaction hash, amount sent, and fee.
|
|
704
|
+
*/
|
|
705
|
+
transferMax(params) {
|
|
706
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
707
|
+
const feeRate = params.feeRate || (yield this.getFeeRates())[xchainClient.FeeOption.Fast];
|
|
708
|
+
xchainClient.checkFeeBounds(this.feeBounds, feeRate);
|
|
709
|
+
const fromAddressIndex = params.walletIndex || 0;
|
|
710
|
+
const sender = yield this.getAddressAsync(fromAddressIndex);
|
|
711
|
+
const { psbt, maxAmount, fee } = yield this.sendMax({
|
|
712
|
+
sender,
|
|
713
|
+
recipient: params.recipient,
|
|
714
|
+
memo: params.memo,
|
|
715
|
+
feeRate,
|
|
716
|
+
utxoSelectionPreferences: params.utxoSelectionPreferences,
|
|
717
|
+
selectedUtxos: params.selectedUtxos,
|
|
718
|
+
});
|
|
719
|
+
const btcKeys = this.getBtcKeys(this.phrase, fromAddressIndex);
|
|
720
|
+
psbt.signAllInputs(this.addressFormat === exports.AddressFormat.P2WPKH
|
|
721
|
+
? btcKeys
|
|
722
|
+
: btcKeys.tweak(Bitcoin__namespace.crypto.taggedHash('TapTweak', toXOnly(btcKeys.publicKey))));
|
|
723
|
+
psbt.finalizeAllInputs();
|
|
724
|
+
const txHex = psbt.extractTransaction().toHex();
|
|
725
|
+
const hash = yield this.roundRobinBroadcastTx(txHex);
|
|
726
|
+
return { hash, maxAmount, fee };
|
|
538
727
|
});
|
|
539
728
|
}
|
|
540
729
|
}
|
|
@@ -585,8 +774,16 @@ class ClientLedger extends Client {
|
|
|
585
774
|
xchainClient.checkFeeBounds(this.feeBounds, feeRate);
|
|
586
775
|
// Get sender address
|
|
587
776
|
const sender = yield this.getAddressAsync(fromAddressIndex);
|
|
588
|
-
//
|
|
589
|
-
const
|
|
777
|
+
// Create defaults and merge with caller-provided preferences
|
|
778
|
+
const defaults = {
|
|
779
|
+
minimizeFee: true,
|
|
780
|
+
avoidDust: true,
|
|
781
|
+
minimizeInputs: false,
|
|
782
|
+
};
|
|
783
|
+
const mergedUtxoSelectionPreferences = Object.assign(Object.assign({}, defaults), params.utxoSelectionPreferences);
|
|
784
|
+
// Prepare transaction using enhanced method with optimal UTXO selection
|
|
785
|
+
const { rawUnsignedTx, inputs } = yield this.prepareTxEnhanced(Object.assign(Object.assign({}, params), { sender,
|
|
786
|
+
feeRate, utxoSelectionPreferences: mergedUtxoSelectionPreferences }));
|
|
590
787
|
const psbt = Bitcoin__namespace.Psbt.fromBase64(rawUnsignedTx);
|
|
591
788
|
// Prepare Ledger inputs
|
|
592
789
|
const ledgerInputs = inputs.map(({ txHex, hash, index }) => {
|
|
@@ -621,6 +818,50 @@ class ClientLedger extends Client {
|
|
|
621
818
|
return txHash;
|
|
622
819
|
});
|
|
623
820
|
}
|
|
821
|
+
// Transfer max BTC from Ledger (sweep transaction)
|
|
822
|
+
transferMax(params) {
|
|
823
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
824
|
+
const app = yield this.getApp();
|
|
825
|
+
const fromAddressIndex = (params === null || params === void 0 ? void 0 : params.walletIndex) || 0;
|
|
826
|
+
const feeRate = params.feeRate || (yield this.getFeeRates())[xchainClient.FeeOption.Fast];
|
|
827
|
+
xchainClient.checkFeeBounds(this.feeBounds, feeRate);
|
|
828
|
+
const sender = yield this.getAddressAsync(fromAddressIndex);
|
|
829
|
+
const { rawUnsignedTx, inputs, maxAmount, fee } = yield this.prepareMaxTx({
|
|
830
|
+
sender,
|
|
831
|
+
recipient: params.recipient,
|
|
832
|
+
memo: params.memo,
|
|
833
|
+
feeRate,
|
|
834
|
+
utxoSelectionPreferences: params.utxoSelectionPreferences,
|
|
835
|
+
selectedUtxos: params.selectedUtxos,
|
|
836
|
+
});
|
|
837
|
+
const psbt = Bitcoin__namespace.Psbt.fromBase64(rawUnsignedTx);
|
|
838
|
+
const ledgerInputs = inputs.map(({ txHex, hash, index }) => {
|
|
839
|
+
if (!txHex) {
|
|
840
|
+
throw Error(`Missing 'txHex' for UTXO (txHash ${hash})`);
|
|
841
|
+
}
|
|
842
|
+
const utxoTx = Bitcoin__namespace.Transaction.fromHex(txHex);
|
|
843
|
+
const splittedTx = app.splitTransaction(txHex, utxoTx.hasWitnesses());
|
|
844
|
+
return [splittedTx, index, null, null];
|
|
845
|
+
});
|
|
846
|
+
const associatedKeysets = ledgerInputs.map(() => this.getFullDerivationPath(fromAddressIndex));
|
|
847
|
+
const unsignedHex = psbt.data.globalMap.unsignedTx.toBuffer().toString('hex');
|
|
848
|
+
const newTx = app.splitTransaction(unsignedHex, true);
|
|
849
|
+
const outputScriptHex = app.serializeTransactionOutputs(newTx).toString('hex');
|
|
850
|
+
const txHex = yield app.createPaymentTransaction({
|
|
851
|
+
inputs: ledgerInputs,
|
|
852
|
+
associatedKeysets,
|
|
853
|
+
outputScriptHex,
|
|
854
|
+
segwit: true,
|
|
855
|
+
useTrustedInputForSegwit: true,
|
|
856
|
+
additionals: [this.addressFormat === exports.AddressFormat.P2TR ? 'bech32m' : 'bech32'],
|
|
857
|
+
});
|
|
858
|
+
const hash = yield this.broadcastTx(txHex);
|
|
859
|
+
if (!hash) {
|
|
860
|
+
throw Error('No Tx hash');
|
|
861
|
+
}
|
|
862
|
+
return { hash, maxAmount, fee };
|
|
863
|
+
});
|
|
864
|
+
}
|
|
624
865
|
}
|
|
625
866
|
|
|
626
867
|
exports.AssetBTC = AssetBTC;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xchainjs/xchain-bitcoin",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "Custom Bitcoin client and utilities used by XChainJS clients",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"XChain",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"@xchainjs/xchain-client": "2.0.10",
|
|
40
40
|
"@xchainjs/xchain-crypto": "1.0.6",
|
|
41
41
|
"@xchainjs/xchain-util": "2.0.5",
|
|
42
|
-
"@xchainjs/xchain-utxo": "2.0
|
|
42
|
+
"@xchainjs/xchain-utxo": "2.2.0",
|
|
43
43
|
"@xchainjs/xchain-utxo-providers": "2.0.10",
|
|
44
44
|
"bitcoinjs-lib": "^6.1.7",
|
|
45
45
|
"coinselect": "3.1.12",
|