@xchainjs/xchain-bitcoin 2.1.0 → 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 +8 -4
- package/lib/clientKeystore.d.ts +3 -1
- package/lib/clientLedger.d.ts +13 -1
- package/lib/index.esm.js +83 -17
- package/lib/index.js +82 -16
- package/package.json +2 -2
package/lib/client.d.ts
CHANGED
|
@@ -47,11 +47,12 @@ declare abstract class Client extends UTXOClient {
|
|
|
47
47
|
* @param params Transaction parameters
|
|
48
48
|
* @returns Enhanced transaction build result with PSBT, UTXOs, and inputs
|
|
49
49
|
*/
|
|
50
|
-
buildTxEnhanced({ amount, recipient, memo, feeRate, sender, spendPendingUTXO, utxoSelectionPreferences, }: TxParams & {
|
|
50
|
+
buildTxEnhanced({ amount, recipient, memo, feeRate, sender, spendPendingUTXO, utxoSelectionPreferences, selectedUtxos, }: TxParams & {
|
|
51
51
|
feeRate: FeeRate;
|
|
52
52
|
sender: Address;
|
|
53
53
|
spendPendingUTXO?: boolean;
|
|
54
54
|
utxoSelectionPreferences?: UtxoSelectionPreferences;
|
|
55
|
+
selectedUtxos?: UTXO[];
|
|
55
56
|
}): Promise<{
|
|
56
57
|
psbt: Bitcoin.Psbt;
|
|
57
58
|
utxos: UTXO[];
|
|
@@ -77,13 +78,14 @@ declare abstract class Client extends UTXOClient {
|
|
|
77
78
|
* @param params Send max parameters
|
|
78
79
|
* @returns Transaction details with maximum sendable amount
|
|
79
80
|
*/
|
|
80
|
-
sendMax({ sender, recipient, memo, feeRate, spendPendingUTXO, utxoSelectionPreferences, }: {
|
|
81
|
+
sendMax({ sender, recipient, memo, feeRate, spendPendingUTXO, utxoSelectionPreferences, selectedUtxos, }: {
|
|
81
82
|
sender: Address;
|
|
82
83
|
recipient: Address;
|
|
83
84
|
memo?: string;
|
|
84
85
|
feeRate: FeeRate;
|
|
85
86
|
spendPendingUTXO?: boolean;
|
|
86
87
|
utxoSelectionPreferences?: UtxoSelectionPreferences;
|
|
88
|
+
selectedUtxos?: UTXO[];
|
|
87
89
|
}): Promise<{
|
|
88
90
|
psbt: Bitcoin.Psbt;
|
|
89
91
|
utxos: UTXO[];
|
|
@@ -96,13 +98,14 @@ declare abstract class Client extends UTXOClient {
|
|
|
96
98
|
* @param params Send max parameters
|
|
97
99
|
* @returns Prepared transaction with maximum sendable amount
|
|
98
100
|
*/
|
|
99
|
-
prepareMaxTx({ sender, recipient, memo, feeRate, spendPendingUTXO, utxoSelectionPreferences, }: {
|
|
101
|
+
prepareMaxTx({ sender, recipient, memo, feeRate, spendPendingUTXO, utxoSelectionPreferences, selectedUtxos, }: {
|
|
100
102
|
sender: Address;
|
|
101
103
|
recipient: Address;
|
|
102
104
|
memo?: string;
|
|
103
105
|
feeRate: FeeRate;
|
|
104
106
|
spendPendingUTXO?: boolean;
|
|
105
107
|
utxoSelectionPreferences?: UtxoSelectionPreferences;
|
|
108
|
+
selectedUtxos?: UTXO[];
|
|
106
109
|
}): Promise<PreparedTx & {
|
|
107
110
|
maxAmount: number;
|
|
108
111
|
fee: number;
|
|
@@ -113,11 +116,12 @@ declare abstract class Client extends UTXOClient {
|
|
|
113
116
|
* @param params The transfer options with enhanced UTXO selection preferences.
|
|
114
117
|
* @returns The raw unsigned transaction with enhanced error handling.
|
|
115
118
|
*/
|
|
116
|
-
prepareTxEnhanced({ sender, memo, amount, recipient, spendPendingUTXO, feeRate, utxoSelectionPreferences, }: TxParams & {
|
|
119
|
+
prepareTxEnhanced({ sender, memo, amount, recipient, spendPendingUTXO, feeRate, utxoSelectionPreferences, selectedUtxos, }: TxParams & {
|
|
117
120
|
sender: Address;
|
|
118
121
|
feeRate: FeeRate;
|
|
119
122
|
spendPendingUTXO?: boolean;
|
|
120
123
|
utxoSelectionPreferences?: UtxoSelectionPreferences;
|
|
124
|
+
selectedUtxos?: UTXO[];
|
|
121
125
|
}): Promise<PreparedTx>;
|
|
122
126
|
/**
|
|
123
127
|
* Prepare transfer with enhanced validation and performance.
|
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, UtxoSelectionPreferences } 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
|
/**
|
|
@@ -47,6 +47,7 @@ declare class ClientKeystore extends Client {
|
|
|
47
47
|
transfer(params: TxParams & {
|
|
48
48
|
feeRate?: FeeRate;
|
|
49
49
|
utxoSelectionPreferences?: UtxoSelectionPreferences;
|
|
50
|
+
selectedUtxos?: UTXO[];
|
|
50
51
|
}): Promise<TxHash>;
|
|
51
52
|
/**
|
|
52
53
|
* Transfer the maximum amount of Bitcoin (sweep).
|
|
@@ -66,6 +67,7 @@ declare class ClientKeystore extends Client {
|
|
|
66
67
|
feeRate?: FeeRate;
|
|
67
68
|
walletIndex?: number;
|
|
68
69
|
utxoSelectionPreferences?: UtxoSelectionPreferences;
|
|
70
|
+
selectedUtxos?: UTXO[];
|
|
69
71
|
}): Promise<{
|
|
70
72
|
hash: TxHash;
|
|
71
73
|
maxAmount: number;
|
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, UtxoSelectionPreferences } 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
|
/**
|
|
@@ -21,5 +21,17 @@ declare class ClientLedger extends Client {
|
|
|
21
21
|
feeRate?: FeeRate;
|
|
22
22
|
utxoSelectionPreferences?: UtxoSelectionPreferences;
|
|
23
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
|
+
}>;
|
|
24
36
|
}
|
|
25
37
|
export { ClientLedger };
|
package/lib/index.esm.js
CHANGED
|
@@ -4,7 +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, UtxoError } from '@xchainjs/xchain-utxo';
|
|
7
|
+
import { Client as Client$1, UtxoTransactionValidator, UtxoError } from '@xchainjs/xchain-utxo';
|
|
8
8
|
import { AssetType } from '@xchainjs/xchain-util';
|
|
9
9
|
import { SochainProvider, SochainNetwork, HaskoinProvider, HaskoinNetwork, BlockcypherProvider, BlockcypherNetwork, BitgoProvider } from '@xchainjs/xchain-utxo-providers';
|
|
10
10
|
import AppBtc from '@ledgerhq/hw-app-btc';
|
|
@@ -289,7 +289,7 @@ class Client extends Client$1 {
|
|
|
289
289
|
* @returns Enhanced transaction build result with PSBT, UTXOs, and inputs
|
|
290
290
|
*/
|
|
291
291
|
buildTxEnhanced(_a) {
|
|
292
|
-
return __awaiter(this, arguments, void 0, function* ({ amount, recipient, memo, feeRate, sender, spendPendingUTXO = true, utxoSelectionPreferences, }) {
|
|
292
|
+
return __awaiter(this, arguments, void 0, function* ({ amount, recipient, memo, feeRate, sender, spendPendingUTXO = true, utxoSelectionPreferences, selectedUtxos, }) {
|
|
293
293
|
try {
|
|
294
294
|
// Comprehensive input validation
|
|
295
295
|
this.validateTransactionInputs({
|
|
@@ -299,9 +299,16 @@ class Client extends Client$1 {
|
|
|
299
299
|
sender,
|
|
300
300
|
feeRate,
|
|
301
301
|
});
|
|
302
|
-
//
|
|
303
|
-
|
|
304
|
-
|
|
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
|
+
}
|
|
305
312
|
const compiledMemo = memo ? this.compileMemo(memo) : null;
|
|
306
313
|
const targetValue = amount.amount().toNumber();
|
|
307
314
|
const extraOutputs = 1 + (compiledMemo ? 1 : 0); // recipient + optional memo (change calculated separately)
|
|
@@ -382,7 +389,7 @@ class Client extends Client$1 {
|
|
|
382
389
|
* @returns Transaction details with maximum sendable amount
|
|
383
390
|
*/
|
|
384
391
|
sendMax(_a) {
|
|
385
|
-
return __awaiter(this, arguments, void 0, function* ({ sender, recipient, memo, feeRate, spendPendingUTXO = true, utxoSelectionPreferences, }) {
|
|
392
|
+
return __awaiter(this, arguments, void 0, function* ({ sender, recipient, memo, feeRate, spendPendingUTXO = true, utxoSelectionPreferences, selectedUtxos, }) {
|
|
386
393
|
try {
|
|
387
394
|
// Basic validation (skip amount validation since we're calculating max)
|
|
388
395
|
if (!(recipient === null || recipient === void 0 ? void 0 : recipient.trim())) {
|
|
@@ -395,20 +402,32 @@ class Client extends Client$1 {
|
|
|
395
402
|
throw UtxoError.invalidAddress(sender, this.network);
|
|
396
403
|
}
|
|
397
404
|
// Memo validation is handled by validateTransactionInputs
|
|
398
|
-
//
|
|
399
|
-
|
|
400
|
-
|
|
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
|
+
}
|
|
401
415
|
// Calculate maximum sendable amount
|
|
402
416
|
const maxCalc = this.calculateMaxSendableAmount(utxos, Math.ceil(feeRate), !!memo, utxoSelectionPreferences);
|
|
403
417
|
const compiledMemo = memo ? this.compileMemo(memo) : null;
|
|
404
418
|
const psbt = new Bitcoin.Psbt({ network: btcNetwork(this.network) });
|
|
405
419
|
// Add inputs
|
|
406
420
|
if (this.addressFormat === AddressFormat.P2WPKH) {
|
|
407
|
-
maxCalc.inputs.forEach((utxo) =>
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
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
|
+
});
|
|
412
431
|
}
|
|
413
432
|
else {
|
|
414
433
|
const { pubkey, output } = Bitcoin.payments.p2tr({
|
|
@@ -452,7 +471,7 @@ class Client extends Client$1 {
|
|
|
452
471
|
* @returns Prepared transaction with maximum sendable amount
|
|
453
472
|
*/
|
|
454
473
|
prepareMaxTx(_a) {
|
|
455
|
-
return __awaiter(this, arguments, void 0, function* ({ sender, recipient, memo, feeRate, spendPendingUTXO = true, utxoSelectionPreferences, }) {
|
|
474
|
+
return __awaiter(this, arguments, void 0, function* ({ sender, recipient, memo, feeRate, spendPendingUTXO = true, utxoSelectionPreferences, selectedUtxos, }) {
|
|
456
475
|
try {
|
|
457
476
|
const { psbt, utxos, inputs, maxAmount, fee } = yield this.sendMax({
|
|
458
477
|
sender,
|
|
@@ -461,6 +480,7 @@ class Client extends Client$1 {
|
|
|
461
480
|
feeRate,
|
|
462
481
|
spendPendingUTXO,
|
|
463
482
|
utxoSelectionPreferences,
|
|
483
|
+
selectedUtxos,
|
|
464
484
|
});
|
|
465
485
|
return {
|
|
466
486
|
rawUnsignedTx: psbt.toBase64(),
|
|
@@ -486,7 +506,7 @@ class Client extends Client$1 {
|
|
|
486
506
|
* @returns The raw unsigned transaction with enhanced error handling.
|
|
487
507
|
*/
|
|
488
508
|
prepareTxEnhanced(_a) {
|
|
489
|
-
return __awaiter(this, arguments, void 0, function* ({ sender, memo, amount, recipient, spendPendingUTXO = true, feeRate, utxoSelectionPreferences, }) {
|
|
509
|
+
return __awaiter(this, arguments, void 0, function* ({ sender, memo, amount, recipient, spendPendingUTXO = true, feeRate, utxoSelectionPreferences, selectedUtxos, }) {
|
|
490
510
|
try {
|
|
491
511
|
const { psbt, utxos, inputs } = yield this.buildTxEnhanced({
|
|
492
512
|
sender,
|
|
@@ -496,6 +516,7 @@ class Client extends Client$1 {
|
|
|
496
516
|
memo,
|
|
497
517
|
spendPendingUTXO,
|
|
498
518
|
utxoSelectionPreferences,
|
|
519
|
+
selectedUtxos,
|
|
499
520
|
});
|
|
500
521
|
return { rawUnsignedTx: psbt.toBase64(), utxos, inputs };
|
|
501
522
|
}
|
|
@@ -628,7 +649,7 @@ class ClientKeystore extends Client {
|
|
|
628
649
|
// Merge default preferences with caller-provided preferences
|
|
629
650
|
const mergedUtxoSelectionPreferences = Object.assign({ minimizeFee: true, avoidDust: true, minimizeInputs: false }, params.utxoSelectionPreferences);
|
|
630
651
|
// Prepare the transaction
|
|
631
|
-
const { rawUnsignedTx } = yield this.prepareTxEnhanced(Object.assign(Object.assign({}, params), { sender: this.getAddress(fromAddressIndex), feeRate, utxoSelectionPreferences: mergedUtxoSelectionPreferences }));
|
|
652
|
+
const { rawUnsignedTx } = yield this.prepareTxEnhanced(Object.assign(Object.assign({}, params), { sender: this.getAddress(fromAddressIndex), feeRate, utxoSelectionPreferences: mergedUtxoSelectionPreferences, selectedUtxos: params.selectedUtxos }));
|
|
632
653
|
// Build the PSBT
|
|
633
654
|
const psbt = Bitcoin.Psbt.fromBase64(rawUnsignedTx);
|
|
634
655
|
// Sign all inputs
|
|
@@ -667,6 +688,7 @@ class ClientKeystore extends Client {
|
|
|
667
688
|
memo: params.memo,
|
|
668
689
|
feeRate,
|
|
669
690
|
utxoSelectionPreferences: params.utxoSelectionPreferences,
|
|
691
|
+
selectedUtxos: params.selectedUtxos,
|
|
670
692
|
});
|
|
671
693
|
const btcKeys = this.getBtcKeys(this.phrase, fromAddressIndex);
|
|
672
694
|
psbt.signAllInputs(this.addressFormat === AddressFormat.P2WPKH
|
|
@@ -770,6 +792,50 @@ class ClientLedger extends Client {
|
|
|
770
792
|
return txHash;
|
|
771
793
|
});
|
|
772
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
|
+
}
|
|
773
839
|
}
|
|
774
840
|
|
|
775
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
|
@@ -315,7 +315,7 @@ class Client extends xchainUtxo.Client {
|
|
|
315
315
|
* @returns Enhanced transaction build result with PSBT, UTXOs, and inputs
|
|
316
316
|
*/
|
|
317
317
|
buildTxEnhanced(_a) {
|
|
318
|
-
return __awaiter(this, arguments, void 0, function* ({ amount, recipient, memo, feeRate, sender, spendPendingUTXO = true, utxoSelectionPreferences, }) {
|
|
318
|
+
return __awaiter(this, arguments, void 0, function* ({ amount, recipient, memo, feeRate, sender, spendPendingUTXO = true, utxoSelectionPreferences, selectedUtxos, }) {
|
|
319
319
|
try {
|
|
320
320
|
// Comprehensive input validation
|
|
321
321
|
this.validateTransactionInputs({
|
|
@@ -325,9 +325,16 @@ class Client extends xchainUtxo.Client {
|
|
|
325
325
|
sender,
|
|
326
326
|
feeRate,
|
|
327
327
|
});
|
|
328
|
-
//
|
|
329
|
-
|
|
330
|
-
|
|
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
|
+
}
|
|
331
338
|
const compiledMemo = memo ? this.compileMemo(memo) : null;
|
|
332
339
|
const targetValue = amount.amount().toNumber();
|
|
333
340
|
const extraOutputs = 1 + (compiledMemo ? 1 : 0); // recipient + optional memo (change calculated separately)
|
|
@@ -408,7 +415,7 @@ class Client extends xchainUtxo.Client {
|
|
|
408
415
|
* @returns Transaction details with maximum sendable amount
|
|
409
416
|
*/
|
|
410
417
|
sendMax(_a) {
|
|
411
|
-
return __awaiter(this, arguments, void 0, function* ({ sender, recipient, memo, feeRate, spendPendingUTXO = true, utxoSelectionPreferences, }) {
|
|
418
|
+
return __awaiter(this, arguments, void 0, function* ({ sender, recipient, memo, feeRate, spendPendingUTXO = true, utxoSelectionPreferences, selectedUtxos, }) {
|
|
412
419
|
try {
|
|
413
420
|
// Basic validation (skip amount validation since we're calculating max)
|
|
414
421
|
if (!(recipient === null || recipient === void 0 ? void 0 : recipient.trim())) {
|
|
@@ -421,20 +428,32 @@ class Client extends xchainUtxo.Client {
|
|
|
421
428
|
throw xchainUtxo.UtxoError.invalidAddress(sender, this.network);
|
|
422
429
|
}
|
|
423
430
|
// Memo validation is handled by validateTransactionInputs
|
|
424
|
-
//
|
|
425
|
-
|
|
426
|
-
|
|
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
|
+
}
|
|
427
441
|
// Calculate maximum sendable amount
|
|
428
442
|
const maxCalc = this.calculateMaxSendableAmount(utxos, Math.ceil(feeRate), !!memo, utxoSelectionPreferences);
|
|
429
443
|
const compiledMemo = memo ? this.compileMemo(memo) : null;
|
|
430
444
|
const psbt = new Bitcoin__namespace.Psbt({ network: btcNetwork(this.network) });
|
|
431
445
|
// Add inputs
|
|
432
446
|
if (this.addressFormat === exports.AddressFormat.P2WPKH) {
|
|
433
|
-
maxCalc.inputs.forEach((utxo) =>
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
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
|
+
});
|
|
438
457
|
}
|
|
439
458
|
else {
|
|
440
459
|
const { pubkey, output } = Bitcoin__namespace.payments.p2tr({
|
|
@@ -478,7 +497,7 @@ class Client extends xchainUtxo.Client {
|
|
|
478
497
|
* @returns Prepared transaction with maximum sendable amount
|
|
479
498
|
*/
|
|
480
499
|
prepareMaxTx(_a) {
|
|
481
|
-
return __awaiter(this, arguments, void 0, function* ({ sender, recipient, memo, feeRate, spendPendingUTXO = true, utxoSelectionPreferences, }) {
|
|
500
|
+
return __awaiter(this, arguments, void 0, function* ({ sender, recipient, memo, feeRate, spendPendingUTXO = true, utxoSelectionPreferences, selectedUtxos, }) {
|
|
482
501
|
try {
|
|
483
502
|
const { psbt, utxos, inputs, maxAmount, fee } = yield this.sendMax({
|
|
484
503
|
sender,
|
|
@@ -487,6 +506,7 @@ class Client extends xchainUtxo.Client {
|
|
|
487
506
|
feeRate,
|
|
488
507
|
spendPendingUTXO,
|
|
489
508
|
utxoSelectionPreferences,
|
|
509
|
+
selectedUtxos,
|
|
490
510
|
});
|
|
491
511
|
return {
|
|
492
512
|
rawUnsignedTx: psbt.toBase64(),
|
|
@@ -512,7 +532,7 @@ class Client extends xchainUtxo.Client {
|
|
|
512
532
|
* @returns The raw unsigned transaction with enhanced error handling.
|
|
513
533
|
*/
|
|
514
534
|
prepareTxEnhanced(_a) {
|
|
515
|
-
return __awaiter(this, arguments, void 0, function* ({ sender, memo, amount, recipient, spendPendingUTXO = true, feeRate, utxoSelectionPreferences, }) {
|
|
535
|
+
return __awaiter(this, arguments, void 0, function* ({ sender, memo, amount, recipient, spendPendingUTXO = true, feeRate, utxoSelectionPreferences, selectedUtxos, }) {
|
|
516
536
|
try {
|
|
517
537
|
const { psbt, utxos, inputs } = yield this.buildTxEnhanced({
|
|
518
538
|
sender,
|
|
@@ -522,6 +542,7 @@ class Client extends xchainUtxo.Client {
|
|
|
522
542
|
memo,
|
|
523
543
|
spendPendingUTXO,
|
|
524
544
|
utxoSelectionPreferences,
|
|
545
|
+
selectedUtxos,
|
|
525
546
|
});
|
|
526
547
|
return { rawUnsignedTx: psbt.toBase64(), utxos, inputs };
|
|
527
548
|
}
|
|
@@ -654,7 +675,7 @@ class ClientKeystore extends Client {
|
|
|
654
675
|
// Merge default preferences with caller-provided preferences
|
|
655
676
|
const mergedUtxoSelectionPreferences = Object.assign({ minimizeFee: true, avoidDust: true, minimizeInputs: false }, params.utxoSelectionPreferences);
|
|
656
677
|
// Prepare the transaction
|
|
657
|
-
const { rawUnsignedTx } = yield this.prepareTxEnhanced(Object.assign(Object.assign({}, params), { sender: this.getAddress(fromAddressIndex), feeRate, utxoSelectionPreferences: mergedUtxoSelectionPreferences }));
|
|
678
|
+
const { rawUnsignedTx } = yield this.prepareTxEnhanced(Object.assign(Object.assign({}, params), { sender: this.getAddress(fromAddressIndex), feeRate, utxoSelectionPreferences: mergedUtxoSelectionPreferences, selectedUtxos: params.selectedUtxos }));
|
|
658
679
|
// Build the PSBT
|
|
659
680
|
const psbt = Bitcoin__namespace.Psbt.fromBase64(rawUnsignedTx);
|
|
660
681
|
// Sign all inputs
|
|
@@ -693,6 +714,7 @@ class ClientKeystore extends Client {
|
|
|
693
714
|
memo: params.memo,
|
|
694
715
|
feeRate,
|
|
695
716
|
utxoSelectionPreferences: params.utxoSelectionPreferences,
|
|
717
|
+
selectedUtxos: params.selectedUtxos,
|
|
696
718
|
});
|
|
697
719
|
const btcKeys = this.getBtcKeys(this.phrase, fromAddressIndex);
|
|
698
720
|
psbt.signAllInputs(this.addressFormat === exports.AddressFormat.P2WPKH
|
|
@@ -796,6 +818,50 @@ class ClientLedger extends Client {
|
|
|
796
818
|
return txHash;
|
|
797
819
|
});
|
|
798
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
|
+
}
|
|
799
865
|
}
|
|
800
866
|
|
|
801
867
|
exports.AssetBTC = AssetBTC;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xchainjs/xchain-bitcoin",
|
|
3
|
-
"version": "2.
|
|
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.
|
|
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",
|