@xchainjs/xchain-doge 2.0.9 → 2.1.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 +56 -1
- package/lib/clientKeystore.d.ts +28 -3
- package/lib/index.esm.js +203 -14
- package/lib/index.js +202 -13
- package/package.json +5 -5
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 Dogecoin from 'bitcoinjs-lib';
|
|
5
5
|
import { LedgerTxInfo, LedgerTxInfoParams } from './types/ledger';
|
|
6
6
|
/**
|
|
@@ -62,6 +62,7 @@ declare abstract class Client extends UTXOClient {
|
|
|
62
62
|
* Asynchronously prepares a transaction for transfer.
|
|
63
63
|
*
|
|
64
64
|
* Builds a transaction (PSBT) with the specified transfer options.
|
|
65
|
+
* @deprecated Use `prepareTxEnhanced` instead for better UTXO selection and error handling.
|
|
65
66
|
* @param {TxParams & { sender: Address; feeRate: FeeRate; spendPendingUTXO?: boolean }} params The transfer options including sender address, fee rate, and optional flag for spending pending UTXOs.
|
|
66
67
|
* @returns {Promise<PreparedTx>} A promise that resolves to the raw unsigned transaction (PSBT).
|
|
67
68
|
*/
|
|
@@ -89,5 +90,59 @@ declare abstract class Client extends UTXOClient {
|
|
|
89
90
|
* @returns {number} The calculated transaction fee.
|
|
90
91
|
*/
|
|
91
92
|
protected getFeeFromUtxos(inputs: UTXO[], feeRate: FeeRate, data?: Buffer | null): number;
|
|
93
|
+
/**
|
|
94
|
+
* Build transaction with enhanced UTXO selection
|
|
95
|
+
* Note: Doge uses legacy P2PKH addresses (nonWitnessUtxo)
|
|
96
|
+
*/
|
|
97
|
+
buildTxEnhanced({ amount, recipient, memo, feeRate, sender, spendPendingUTXO, utxoSelectionPreferences, }: TxParams & {
|
|
98
|
+
feeRate: FeeRate;
|
|
99
|
+
sender: Address;
|
|
100
|
+
spendPendingUTXO?: boolean;
|
|
101
|
+
utxoSelectionPreferences?: UtxoSelectionPreferences;
|
|
102
|
+
}): Promise<{
|
|
103
|
+
psbt: Dogecoin.Psbt;
|
|
104
|
+
utxos: UTXO[];
|
|
105
|
+
inputs: UTXO[];
|
|
106
|
+
}>;
|
|
107
|
+
/**
|
|
108
|
+
* Prepare transaction with enhanced UTXO selection
|
|
109
|
+
*/
|
|
110
|
+
prepareTxEnhanced({ sender, memo, amount, recipient, spendPendingUTXO, feeRate, utxoSelectionPreferences, }: TxParams & {
|
|
111
|
+
sender: Address;
|
|
112
|
+
feeRate: FeeRate;
|
|
113
|
+
spendPendingUTXO?: boolean;
|
|
114
|
+
utxoSelectionPreferences?: UtxoSelectionPreferences;
|
|
115
|
+
}): Promise<PreparedTx>;
|
|
116
|
+
/**
|
|
117
|
+
* Send maximum possible amount (sweep)
|
|
118
|
+
*/
|
|
119
|
+
sendMax({ sender, recipient, memo, feeRate, spendPendingUTXO, utxoSelectionPreferences, }: {
|
|
120
|
+
sender: Address;
|
|
121
|
+
recipient: Address;
|
|
122
|
+
memo?: string;
|
|
123
|
+
feeRate: FeeRate;
|
|
124
|
+
spendPendingUTXO?: boolean;
|
|
125
|
+
utxoSelectionPreferences?: UtxoSelectionPreferences;
|
|
126
|
+
}): Promise<{
|
|
127
|
+
psbt: Dogecoin.Psbt;
|
|
128
|
+
utxos: UTXO[];
|
|
129
|
+
inputs: UTXO[];
|
|
130
|
+
maxAmount: number;
|
|
131
|
+
fee: number;
|
|
132
|
+
}>;
|
|
133
|
+
/**
|
|
134
|
+
* Prepare max send transaction
|
|
135
|
+
*/
|
|
136
|
+
prepareMaxTx({ sender, recipient, memo, feeRate, spendPendingUTXO, utxoSelectionPreferences, }: {
|
|
137
|
+
sender: Address;
|
|
138
|
+
recipient: Address;
|
|
139
|
+
memo?: string;
|
|
140
|
+
feeRate: FeeRate;
|
|
141
|
+
spendPendingUTXO?: boolean;
|
|
142
|
+
utxoSelectionPreferences?: UtxoSelectionPreferences;
|
|
143
|
+
}): Promise<PreparedTx & {
|
|
144
|
+
maxAmount: number;
|
|
145
|
+
fee: number;
|
|
146
|
+
}>;
|
|
92
147
|
}
|
|
93
148
|
export { Client };
|
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 } from '@xchainjs/xchain-utxo';
|
|
3
|
+
import { TxParams, UtxoSelectionPreferences } from '@xchainjs/xchain-utxo';
|
|
4
4
|
import { Client } from './client';
|
|
5
5
|
/**
|
|
6
6
|
* Custom Doge client extended to support keystore functionality
|
|
@@ -44,11 +44,36 @@ declare class ClientKeystore extends Client {
|
|
|
44
44
|
* Asynchronously transfers Dogecoin.
|
|
45
45
|
*
|
|
46
46
|
* Builds, signs, and broadcasts a Dogecoin transaction with the specified parameters.
|
|
47
|
-
*
|
|
48
|
-
* @
|
|
47
|
+
*
|
|
48
|
+
* @param {Object} params The transfer parameters.
|
|
49
|
+
* @returns {Promise<TxHash>} The transaction hash.
|
|
49
50
|
*/
|
|
50
51
|
transfer(params: TxParams & {
|
|
51
52
|
feeRate?: FeeRate;
|
|
53
|
+
utxoSelectionPreferences?: UtxoSelectionPreferences;
|
|
52
54
|
}): Promise<TxHash>;
|
|
55
|
+
/**
|
|
56
|
+
* Transfer the maximum amount of Dogecoin (sweep).
|
|
57
|
+
*
|
|
58
|
+
* Calculates the maximum sendable amount after fees, signs, and broadcasts the transaction.
|
|
59
|
+
* @param {Object} params The transfer parameters.
|
|
60
|
+
* @param {string} params.recipient The recipient address.
|
|
61
|
+
* @param {string} [params.memo] Optional memo for the transaction.
|
|
62
|
+
* @param {FeeRate} [params.feeRate] Optional fee rate. Defaults to 'fast' rate.
|
|
63
|
+
* @param {number} [params.walletIndex] Optional wallet index. Defaults to 0.
|
|
64
|
+
* @param {UtxoSelectionPreferences} [params.utxoSelectionPreferences] Optional UTXO selection preferences.
|
|
65
|
+
* @returns {Promise<{ hash: TxHash; maxAmount: number; fee: number }>} The transaction hash, amount sent, and fee.
|
|
66
|
+
*/
|
|
67
|
+
transferMax(params: {
|
|
68
|
+
recipient: Address;
|
|
69
|
+
memo?: string;
|
|
70
|
+
feeRate?: FeeRate;
|
|
71
|
+
walletIndex?: number;
|
|
72
|
+
utxoSelectionPreferences?: UtxoSelectionPreferences;
|
|
73
|
+
}): Promise<{
|
|
74
|
+
hash: TxHash;
|
|
75
|
+
maxAmount: number;
|
|
76
|
+
fee: number;
|
|
77
|
+
}>;
|
|
53
78
|
}
|
|
54
79
|
export { ClientKeystore };
|
package/lib/index.esm.js
CHANGED
|
@@ -4,7 +4,7 @@ import { getSeed } from '@xchainjs/xchain-crypto';
|
|
|
4
4
|
import * as Dogecoin from 'bitcoinjs-lib';
|
|
5
5
|
import { ECPairFactory } from 'ecpair';
|
|
6
6
|
import { HDKey } from '@scure/bip32';
|
|
7
|
-
import { toBitcoinJS, Client as Client$1 } from '@xchainjs/xchain-utxo';
|
|
7
|
+
import { toBitcoinJS, Client as Client$1, UtxoError } from '@xchainjs/xchain-utxo';
|
|
8
8
|
import accumulative from 'coinselect/accumulative.js';
|
|
9
9
|
import { AssetType } from '@xchainjs/xchain-util';
|
|
10
10
|
import { SochainProvider, SochainNetwork, BlockcypherProvider, BlockcypherNetwork, BitgoProvider } from '@xchainjs/xchain-utxo-providers';
|
|
@@ -340,6 +340,7 @@ class Client extends Client$1 {
|
|
|
340
340
|
* Asynchronously prepares a transaction for transfer.
|
|
341
341
|
*
|
|
342
342
|
* Builds a transaction (PSBT) with the specified transfer options.
|
|
343
|
+
* @deprecated Use `prepareTxEnhanced` instead for better UTXO selection and error handling.
|
|
343
344
|
* @param {TxParams & { sender: Address; feeRate: FeeRate; spendPendingUTXO?: boolean }} params The transfer options including sender address, fee rate, and optional flag for spending pending UTXOs.
|
|
344
345
|
* @returns {Promise<PreparedTx>} A promise that resolves to the raw unsigned transaction (PSBT).
|
|
345
346
|
*/
|
|
@@ -398,6 +399,167 @@ class Client extends Client$1 {
|
|
|
398
399
|
// Ensure the fee is not less than the minimum transaction fee
|
|
399
400
|
return fee > MIN_TX_FEE ? fee : MIN_TX_FEE;
|
|
400
401
|
}
|
|
402
|
+
// ==================== Enhanced Transaction Methods ====================
|
|
403
|
+
/**
|
|
404
|
+
* Build transaction with enhanced UTXO selection
|
|
405
|
+
* Note: Doge uses legacy P2PKH addresses (nonWitnessUtxo)
|
|
406
|
+
*/
|
|
407
|
+
buildTxEnhanced(_a) {
|
|
408
|
+
return __awaiter(this, arguments, void 0, function* ({ amount, recipient, memo, feeRate, sender, spendPendingUTXO = true, utxoSelectionPreferences, }) {
|
|
409
|
+
try {
|
|
410
|
+
this.validateTransactionInputs({
|
|
411
|
+
amount,
|
|
412
|
+
recipient,
|
|
413
|
+
memo,
|
|
414
|
+
sender,
|
|
415
|
+
feeRate,
|
|
416
|
+
});
|
|
417
|
+
const confirmedOnly = !spendPendingUTXO;
|
|
418
|
+
const utxos = yield this.getValidatedUtxos(sender, confirmedOnly);
|
|
419
|
+
const compiledMemo = memo ? this.compileMemo(memo) : null;
|
|
420
|
+
const targetValue = amount.amount().toNumber();
|
|
421
|
+
const extraOutputs = 1 + (compiledMemo ? 1 : 0);
|
|
422
|
+
const selectionResult = this.selectUtxosForTransaction(utxos, targetValue, Math.ceil(feeRate), extraOutputs, utxoSelectionPreferences);
|
|
423
|
+
const psbt = new Dogecoin.Psbt({ network: dogeNetwork(this.network) });
|
|
424
|
+
psbt.setMaximumFeeRate(7500000);
|
|
425
|
+
// Add inputs - Doge uses nonWitnessUtxo (legacy P2PKH)
|
|
426
|
+
for (const utxo of selectionResult.inputs) {
|
|
427
|
+
psbt.addInput({
|
|
428
|
+
hash: utxo.hash,
|
|
429
|
+
index: utxo.index,
|
|
430
|
+
nonWitnessUtxo: Buffer.from(utxo.txHex || '', 'hex'),
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
// Add recipient output
|
|
434
|
+
psbt.addOutput({
|
|
435
|
+
address: recipient,
|
|
436
|
+
value: targetValue,
|
|
437
|
+
});
|
|
438
|
+
// Add change output if needed
|
|
439
|
+
if (selectionResult.changeAmount > 0) {
|
|
440
|
+
psbt.addOutput({
|
|
441
|
+
address: sender,
|
|
442
|
+
value: selectionResult.changeAmount,
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
// Add memo output if present
|
|
446
|
+
if (compiledMemo) {
|
|
447
|
+
psbt.addOutput({ script: compiledMemo, value: 0 });
|
|
448
|
+
}
|
|
449
|
+
return { psbt, utxos, inputs: selectionResult.inputs };
|
|
450
|
+
}
|
|
451
|
+
catch (error) {
|
|
452
|
+
if (UtxoError.isUtxoError(error)) {
|
|
453
|
+
throw error;
|
|
454
|
+
}
|
|
455
|
+
throw UtxoError.fromUnknown(error, 'buildTxEnhanced');
|
|
456
|
+
}
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Prepare transaction with enhanced UTXO selection
|
|
461
|
+
*/
|
|
462
|
+
prepareTxEnhanced(_a) {
|
|
463
|
+
return __awaiter(this, arguments, void 0, function* ({ sender, memo, amount, recipient, spendPendingUTXO = true, feeRate, utxoSelectionPreferences, }) {
|
|
464
|
+
try {
|
|
465
|
+
const { psbt, utxos, inputs } = yield this.buildTxEnhanced({
|
|
466
|
+
sender,
|
|
467
|
+
recipient,
|
|
468
|
+
amount,
|
|
469
|
+
feeRate,
|
|
470
|
+
memo,
|
|
471
|
+
spendPendingUTXO,
|
|
472
|
+
utxoSelectionPreferences,
|
|
473
|
+
});
|
|
474
|
+
return { rawUnsignedTx: psbt.toBase64(), utxos, inputs };
|
|
475
|
+
}
|
|
476
|
+
catch (error) {
|
|
477
|
+
if (UtxoError.isUtxoError(error)) {
|
|
478
|
+
throw error;
|
|
479
|
+
}
|
|
480
|
+
throw UtxoError.fromUnknown(error, 'prepareTxEnhanced');
|
|
481
|
+
}
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Send maximum possible amount (sweep)
|
|
486
|
+
*/
|
|
487
|
+
sendMax(_a) {
|
|
488
|
+
return __awaiter(this, arguments, void 0, function* ({ sender, recipient, memo, feeRate, spendPendingUTXO = true, utxoSelectionPreferences, }) {
|
|
489
|
+
try {
|
|
490
|
+
if (!this.validateAddress(recipient)) {
|
|
491
|
+
throw UtxoError.invalidAddress(recipient, this.network);
|
|
492
|
+
}
|
|
493
|
+
if (!this.validateAddress(sender)) {
|
|
494
|
+
throw UtxoError.invalidAddress(sender, this.network);
|
|
495
|
+
}
|
|
496
|
+
const confirmedOnly = !spendPendingUTXO;
|
|
497
|
+
const utxos = yield this.getValidatedUtxos(sender, confirmedOnly);
|
|
498
|
+
const maxCalc = this.calculateMaxSendableAmount(utxos, Math.ceil(feeRate), !!memo, utxoSelectionPreferences);
|
|
499
|
+
const compiledMemo = memo ? this.compileMemo(memo) : null;
|
|
500
|
+
const psbt = new Dogecoin.Psbt({ network: dogeNetwork(this.network) });
|
|
501
|
+
psbt.setMaximumFeeRate(7500000);
|
|
502
|
+
// Add inputs - Doge uses nonWitnessUtxo
|
|
503
|
+
for (const utxo of maxCalc.inputs) {
|
|
504
|
+
psbt.addInput({
|
|
505
|
+
hash: utxo.hash,
|
|
506
|
+
index: utxo.index,
|
|
507
|
+
nonWitnessUtxo: Buffer.from(utxo.txHex || '', 'hex'),
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
psbt.addOutput({
|
|
511
|
+
address: recipient,
|
|
512
|
+
value: maxCalc.amount,
|
|
513
|
+
});
|
|
514
|
+
if (compiledMemo) {
|
|
515
|
+
psbt.addOutput({ script: compiledMemo, value: 0 });
|
|
516
|
+
}
|
|
517
|
+
return {
|
|
518
|
+
psbt,
|
|
519
|
+
utxos,
|
|
520
|
+
inputs: maxCalc.inputs,
|
|
521
|
+
maxAmount: maxCalc.amount,
|
|
522
|
+
fee: maxCalc.fee,
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
catch (error) {
|
|
526
|
+
if (UtxoError.isUtxoError(error)) {
|
|
527
|
+
throw error;
|
|
528
|
+
}
|
|
529
|
+
throw UtxoError.fromUnknown(error, 'sendMax');
|
|
530
|
+
}
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* Prepare max send transaction
|
|
535
|
+
*/
|
|
536
|
+
prepareMaxTx(_a) {
|
|
537
|
+
return __awaiter(this, arguments, void 0, function* ({ sender, recipient, memo, feeRate, spendPendingUTXO = true, utxoSelectionPreferences, }) {
|
|
538
|
+
try {
|
|
539
|
+
const { psbt, utxos, inputs, maxAmount, fee } = yield this.sendMax({
|
|
540
|
+
sender,
|
|
541
|
+
recipient,
|
|
542
|
+
memo,
|
|
543
|
+
feeRate,
|
|
544
|
+
spendPendingUTXO,
|
|
545
|
+
utxoSelectionPreferences,
|
|
546
|
+
});
|
|
547
|
+
return {
|
|
548
|
+
rawUnsignedTx: psbt.toBase64(),
|
|
549
|
+
utxos,
|
|
550
|
+
inputs,
|
|
551
|
+
maxAmount,
|
|
552
|
+
fee,
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
catch (error) {
|
|
556
|
+
if (UtxoError.isUtxoError(error)) {
|
|
557
|
+
throw error;
|
|
558
|
+
}
|
|
559
|
+
throw UtxoError.fromUnknown(error, 'prepareMaxTx');
|
|
560
|
+
}
|
|
561
|
+
});
|
|
562
|
+
}
|
|
401
563
|
}
|
|
402
564
|
|
|
403
565
|
const ECPair = ECPairFactory(ecc);
|
|
@@ -474,33 +636,60 @@ class ClientKeystore extends Client {
|
|
|
474
636
|
* Asynchronously transfers Dogecoin.
|
|
475
637
|
*
|
|
476
638
|
* Builds, signs, and broadcasts a Dogecoin transaction with the specified parameters.
|
|
477
|
-
*
|
|
478
|
-
* @
|
|
639
|
+
*
|
|
640
|
+
* @param {Object} params The transfer parameters.
|
|
641
|
+
* @returns {Promise<TxHash>} The transaction hash.
|
|
479
642
|
*/
|
|
480
643
|
transfer(params) {
|
|
481
644
|
return __awaiter(this, void 0, void 0, function* () {
|
|
482
|
-
// Determine the fee rate for the transaction, using provided fee rate or fetching it from the network
|
|
483
645
|
const feeRate = params.feeRate || (yield this.getFeeRates())[FeeOption.Fast];
|
|
484
|
-
// Check if the fee rate is within the specified fee bounds
|
|
485
646
|
checkFeeBounds(this.feeBounds, feeRate);
|
|
486
|
-
// Get the index of the sender's address or use the default index (0)
|
|
487
647
|
const fromAddressIndex = (params === null || params === void 0 ? void 0 : params.walletIndex) || 0;
|
|
488
|
-
|
|
489
|
-
const { rawUnsignedTx } = yield this.prepareTx(Object.assign(Object.assign({}, params), { feeRate, sender: yield this.getAddressAsync(fromAddressIndex) }));
|
|
490
|
-
// Get the Dogecoin keys for signing the transaction
|
|
648
|
+
const sender = yield this.getAddressAsync(fromAddressIndex);
|
|
491
649
|
const dogeKeys = this.getDogeKeys(this.phrase, fromAddressIndex);
|
|
492
|
-
|
|
650
|
+
const mergedPreferences = Object.assign({ minimizeFee: true, avoidDust: true, minimizeInputs: false }, params.utxoSelectionPreferences);
|
|
651
|
+
const { rawUnsignedTx } = yield this.prepareTxEnhanced(Object.assign(Object.assign({}, params), { feeRate,
|
|
652
|
+
sender, utxoSelectionPreferences: mergedPreferences }));
|
|
493
653
|
const psbt = Dogecoin.Psbt.fromBase64(rawUnsignedTx, { maximumFeeRate: 7500000 });
|
|
494
|
-
// Sign all inputs of the transaction with the Dogecoin keys
|
|
495
654
|
psbt.signAllInputs(dogeKeys);
|
|
496
|
-
// Finalize all inputs of the transaction
|
|
497
655
|
psbt.finalizeAllInputs();
|
|
498
|
-
// Extract the signed transaction and format it to hexadecimal
|
|
499
656
|
const txHex = psbt.extractTransaction().toHex();
|
|
500
|
-
// Broadcast the signed transaction to the Dogecoin network and return the transaction hash
|
|
501
657
|
return yield this.roundRobinBroadcastTx(txHex);
|
|
502
658
|
});
|
|
503
659
|
}
|
|
660
|
+
/**
|
|
661
|
+
* Transfer the maximum amount of Dogecoin (sweep).
|
|
662
|
+
*
|
|
663
|
+
* Calculates the maximum sendable amount after fees, signs, and broadcasts the transaction.
|
|
664
|
+
* @param {Object} params The transfer parameters.
|
|
665
|
+
* @param {string} params.recipient The recipient address.
|
|
666
|
+
* @param {string} [params.memo] Optional memo for the transaction.
|
|
667
|
+
* @param {FeeRate} [params.feeRate] Optional fee rate. Defaults to 'fast' rate.
|
|
668
|
+
* @param {number} [params.walletIndex] Optional wallet index. Defaults to 0.
|
|
669
|
+
* @param {UtxoSelectionPreferences} [params.utxoSelectionPreferences] Optional UTXO selection preferences.
|
|
670
|
+
* @returns {Promise<{ hash: TxHash; maxAmount: number; fee: number }>} The transaction hash, amount sent, and fee.
|
|
671
|
+
*/
|
|
672
|
+
transferMax(params) {
|
|
673
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
674
|
+
const feeRate = params.feeRate || (yield this.getFeeRates())[FeeOption.Fast];
|
|
675
|
+
checkFeeBounds(this.feeBounds, feeRate);
|
|
676
|
+
const fromAddressIndex = params.walletIndex || 0;
|
|
677
|
+
const sender = yield this.getAddressAsync(fromAddressIndex);
|
|
678
|
+
const { psbt, maxAmount, fee } = yield this.sendMax({
|
|
679
|
+
sender,
|
|
680
|
+
recipient: params.recipient,
|
|
681
|
+
memo: params.memo,
|
|
682
|
+
feeRate,
|
|
683
|
+
utxoSelectionPreferences: params.utxoSelectionPreferences,
|
|
684
|
+
});
|
|
685
|
+
const dogeKeys = this.getDogeKeys(this.phrase, fromAddressIndex);
|
|
686
|
+
psbt.signAllInputs(dogeKeys);
|
|
687
|
+
psbt.finalizeAllInputs();
|
|
688
|
+
const txHex = psbt.extractTransaction().toHex();
|
|
689
|
+
const hash = yield this.roundRobinBroadcastTx(txHex);
|
|
690
|
+
return { hash, maxAmount, fee };
|
|
691
|
+
});
|
|
692
|
+
}
|
|
504
693
|
}
|
|
505
694
|
|
|
506
695
|
/**
|
package/lib/index.js
CHANGED
|
@@ -367,6 +367,7 @@ class Client extends xchainUtxo.Client {
|
|
|
367
367
|
* Asynchronously prepares a transaction for transfer.
|
|
368
368
|
*
|
|
369
369
|
* Builds a transaction (PSBT) with the specified transfer options.
|
|
370
|
+
* @deprecated Use `prepareTxEnhanced` instead for better UTXO selection and error handling.
|
|
370
371
|
* @param {TxParams & { sender: Address; feeRate: FeeRate; spendPendingUTXO?: boolean }} params The transfer options including sender address, fee rate, and optional flag for spending pending UTXOs.
|
|
371
372
|
* @returns {Promise<PreparedTx>} A promise that resolves to the raw unsigned transaction (PSBT).
|
|
372
373
|
*/
|
|
@@ -425,6 +426,167 @@ class Client extends xchainUtxo.Client {
|
|
|
425
426
|
// Ensure the fee is not less than the minimum transaction fee
|
|
426
427
|
return fee > MIN_TX_FEE ? fee : MIN_TX_FEE;
|
|
427
428
|
}
|
|
429
|
+
// ==================== Enhanced Transaction Methods ====================
|
|
430
|
+
/**
|
|
431
|
+
* Build transaction with enhanced UTXO selection
|
|
432
|
+
* Note: Doge uses legacy P2PKH addresses (nonWitnessUtxo)
|
|
433
|
+
*/
|
|
434
|
+
buildTxEnhanced(_a) {
|
|
435
|
+
return __awaiter(this, arguments, void 0, function* ({ amount, recipient, memo, feeRate, sender, spendPendingUTXO = true, utxoSelectionPreferences, }) {
|
|
436
|
+
try {
|
|
437
|
+
this.validateTransactionInputs({
|
|
438
|
+
amount,
|
|
439
|
+
recipient,
|
|
440
|
+
memo,
|
|
441
|
+
sender,
|
|
442
|
+
feeRate,
|
|
443
|
+
});
|
|
444
|
+
const confirmedOnly = !spendPendingUTXO;
|
|
445
|
+
const utxos = yield this.getValidatedUtxos(sender, confirmedOnly);
|
|
446
|
+
const compiledMemo = memo ? this.compileMemo(memo) : null;
|
|
447
|
+
const targetValue = amount.amount().toNumber();
|
|
448
|
+
const extraOutputs = 1 + (compiledMemo ? 1 : 0);
|
|
449
|
+
const selectionResult = this.selectUtxosForTransaction(utxos, targetValue, Math.ceil(feeRate), extraOutputs, utxoSelectionPreferences);
|
|
450
|
+
const psbt = new Dogecoin__namespace.Psbt({ network: dogeNetwork(this.network) });
|
|
451
|
+
psbt.setMaximumFeeRate(7500000);
|
|
452
|
+
// Add inputs - Doge uses nonWitnessUtxo (legacy P2PKH)
|
|
453
|
+
for (const utxo of selectionResult.inputs) {
|
|
454
|
+
psbt.addInput({
|
|
455
|
+
hash: utxo.hash,
|
|
456
|
+
index: utxo.index,
|
|
457
|
+
nonWitnessUtxo: Buffer.from(utxo.txHex || '', 'hex'),
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
// Add recipient output
|
|
461
|
+
psbt.addOutput({
|
|
462
|
+
address: recipient,
|
|
463
|
+
value: targetValue,
|
|
464
|
+
});
|
|
465
|
+
// Add change output if needed
|
|
466
|
+
if (selectionResult.changeAmount > 0) {
|
|
467
|
+
psbt.addOutput({
|
|
468
|
+
address: sender,
|
|
469
|
+
value: selectionResult.changeAmount,
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
// Add memo output if present
|
|
473
|
+
if (compiledMemo) {
|
|
474
|
+
psbt.addOutput({ script: compiledMemo, value: 0 });
|
|
475
|
+
}
|
|
476
|
+
return { psbt, utxos, inputs: selectionResult.inputs };
|
|
477
|
+
}
|
|
478
|
+
catch (error) {
|
|
479
|
+
if (xchainUtxo.UtxoError.isUtxoError(error)) {
|
|
480
|
+
throw error;
|
|
481
|
+
}
|
|
482
|
+
throw xchainUtxo.UtxoError.fromUnknown(error, 'buildTxEnhanced');
|
|
483
|
+
}
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* Prepare transaction with enhanced UTXO selection
|
|
488
|
+
*/
|
|
489
|
+
prepareTxEnhanced(_a) {
|
|
490
|
+
return __awaiter(this, arguments, void 0, function* ({ sender, memo, amount, recipient, spendPendingUTXO = true, feeRate, utxoSelectionPreferences, }) {
|
|
491
|
+
try {
|
|
492
|
+
const { psbt, utxos, inputs } = yield this.buildTxEnhanced({
|
|
493
|
+
sender,
|
|
494
|
+
recipient,
|
|
495
|
+
amount,
|
|
496
|
+
feeRate,
|
|
497
|
+
memo,
|
|
498
|
+
spendPendingUTXO,
|
|
499
|
+
utxoSelectionPreferences,
|
|
500
|
+
});
|
|
501
|
+
return { rawUnsignedTx: psbt.toBase64(), utxos, inputs };
|
|
502
|
+
}
|
|
503
|
+
catch (error) {
|
|
504
|
+
if (xchainUtxo.UtxoError.isUtxoError(error)) {
|
|
505
|
+
throw error;
|
|
506
|
+
}
|
|
507
|
+
throw xchainUtxo.UtxoError.fromUnknown(error, 'prepareTxEnhanced');
|
|
508
|
+
}
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* Send maximum possible amount (sweep)
|
|
513
|
+
*/
|
|
514
|
+
sendMax(_a) {
|
|
515
|
+
return __awaiter(this, arguments, void 0, function* ({ sender, recipient, memo, feeRate, spendPendingUTXO = true, utxoSelectionPreferences, }) {
|
|
516
|
+
try {
|
|
517
|
+
if (!this.validateAddress(recipient)) {
|
|
518
|
+
throw xchainUtxo.UtxoError.invalidAddress(recipient, this.network);
|
|
519
|
+
}
|
|
520
|
+
if (!this.validateAddress(sender)) {
|
|
521
|
+
throw xchainUtxo.UtxoError.invalidAddress(sender, this.network);
|
|
522
|
+
}
|
|
523
|
+
const confirmedOnly = !spendPendingUTXO;
|
|
524
|
+
const utxos = yield this.getValidatedUtxos(sender, confirmedOnly);
|
|
525
|
+
const maxCalc = this.calculateMaxSendableAmount(utxos, Math.ceil(feeRate), !!memo, utxoSelectionPreferences);
|
|
526
|
+
const compiledMemo = memo ? this.compileMemo(memo) : null;
|
|
527
|
+
const psbt = new Dogecoin__namespace.Psbt({ network: dogeNetwork(this.network) });
|
|
528
|
+
psbt.setMaximumFeeRate(7500000);
|
|
529
|
+
// Add inputs - Doge uses nonWitnessUtxo
|
|
530
|
+
for (const utxo of maxCalc.inputs) {
|
|
531
|
+
psbt.addInput({
|
|
532
|
+
hash: utxo.hash,
|
|
533
|
+
index: utxo.index,
|
|
534
|
+
nonWitnessUtxo: Buffer.from(utxo.txHex || '', 'hex'),
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
psbt.addOutput({
|
|
538
|
+
address: recipient,
|
|
539
|
+
value: maxCalc.amount,
|
|
540
|
+
});
|
|
541
|
+
if (compiledMemo) {
|
|
542
|
+
psbt.addOutput({ script: compiledMemo, value: 0 });
|
|
543
|
+
}
|
|
544
|
+
return {
|
|
545
|
+
psbt,
|
|
546
|
+
utxos,
|
|
547
|
+
inputs: maxCalc.inputs,
|
|
548
|
+
maxAmount: maxCalc.amount,
|
|
549
|
+
fee: maxCalc.fee,
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
catch (error) {
|
|
553
|
+
if (xchainUtxo.UtxoError.isUtxoError(error)) {
|
|
554
|
+
throw error;
|
|
555
|
+
}
|
|
556
|
+
throw xchainUtxo.UtxoError.fromUnknown(error, 'sendMax');
|
|
557
|
+
}
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
561
|
+
* Prepare max send transaction
|
|
562
|
+
*/
|
|
563
|
+
prepareMaxTx(_a) {
|
|
564
|
+
return __awaiter(this, arguments, void 0, function* ({ sender, recipient, memo, feeRate, spendPendingUTXO = true, utxoSelectionPreferences, }) {
|
|
565
|
+
try {
|
|
566
|
+
const { psbt, utxos, inputs, maxAmount, fee } = yield this.sendMax({
|
|
567
|
+
sender,
|
|
568
|
+
recipient,
|
|
569
|
+
memo,
|
|
570
|
+
feeRate,
|
|
571
|
+
spendPendingUTXO,
|
|
572
|
+
utxoSelectionPreferences,
|
|
573
|
+
});
|
|
574
|
+
return {
|
|
575
|
+
rawUnsignedTx: psbt.toBase64(),
|
|
576
|
+
utxos,
|
|
577
|
+
inputs,
|
|
578
|
+
maxAmount,
|
|
579
|
+
fee,
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
catch (error) {
|
|
583
|
+
if (xchainUtxo.UtxoError.isUtxoError(error)) {
|
|
584
|
+
throw error;
|
|
585
|
+
}
|
|
586
|
+
throw xchainUtxo.UtxoError.fromUnknown(error, 'prepareMaxTx');
|
|
587
|
+
}
|
|
588
|
+
});
|
|
589
|
+
}
|
|
428
590
|
}
|
|
429
591
|
|
|
430
592
|
const ECPair = ecpair.ECPairFactory(ecc__namespace);
|
|
@@ -501,33 +663,60 @@ class ClientKeystore extends Client {
|
|
|
501
663
|
* Asynchronously transfers Dogecoin.
|
|
502
664
|
*
|
|
503
665
|
* Builds, signs, and broadcasts a Dogecoin transaction with the specified parameters.
|
|
504
|
-
*
|
|
505
|
-
* @
|
|
666
|
+
*
|
|
667
|
+
* @param {Object} params The transfer parameters.
|
|
668
|
+
* @returns {Promise<TxHash>} The transaction hash.
|
|
506
669
|
*/
|
|
507
670
|
transfer(params) {
|
|
508
671
|
return __awaiter(this, void 0, void 0, function* () {
|
|
509
|
-
// Determine the fee rate for the transaction, using provided fee rate or fetching it from the network
|
|
510
672
|
const feeRate = params.feeRate || (yield this.getFeeRates())[xchainClient.FeeOption.Fast];
|
|
511
|
-
// Check if the fee rate is within the specified fee bounds
|
|
512
673
|
xchainClient.checkFeeBounds(this.feeBounds, feeRate);
|
|
513
|
-
// Get the index of the sender's address or use the default index (0)
|
|
514
674
|
const fromAddressIndex = (params === null || params === void 0 ? void 0 : params.walletIndex) || 0;
|
|
515
|
-
|
|
516
|
-
const { rawUnsignedTx } = yield this.prepareTx(Object.assign(Object.assign({}, params), { feeRate, sender: yield this.getAddressAsync(fromAddressIndex) }));
|
|
517
|
-
// Get the Dogecoin keys for signing the transaction
|
|
675
|
+
const sender = yield this.getAddressAsync(fromAddressIndex);
|
|
518
676
|
const dogeKeys = this.getDogeKeys(this.phrase, fromAddressIndex);
|
|
519
|
-
|
|
677
|
+
const mergedPreferences = Object.assign({ minimizeFee: true, avoidDust: true, minimizeInputs: false }, params.utxoSelectionPreferences);
|
|
678
|
+
const { rawUnsignedTx } = yield this.prepareTxEnhanced(Object.assign(Object.assign({}, params), { feeRate,
|
|
679
|
+
sender, utxoSelectionPreferences: mergedPreferences }));
|
|
520
680
|
const psbt = Dogecoin__namespace.Psbt.fromBase64(rawUnsignedTx, { maximumFeeRate: 7500000 });
|
|
521
|
-
// Sign all inputs of the transaction with the Dogecoin keys
|
|
522
681
|
psbt.signAllInputs(dogeKeys);
|
|
523
|
-
// Finalize all inputs of the transaction
|
|
524
682
|
psbt.finalizeAllInputs();
|
|
525
|
-
// Extract the signed transaction and format it to hexadecimal
|
|
526
683
|
const txHex = psbt.extractTransaction().toHex();
|
|
527
|
-
// Broadcast the signed transaction to the Dogecoin network and return the transaction hash
|
|
528
684
|
return yield this.roundRobinBroadcastTx(txHex);
|
|
529
685
|
});
|
|
530
686
|
}
|
|
687
|
+
/**
|
|
688
|
+
* Transfer the maximum amount of Dogecoin (sweep).
|
|
689
|
+
*
|
|
690
|
+
* Calculates the maximum sendable amount after fees, signs, and broadcasts the transaction.
|
|
691
|
+
* @param {Object} params The transfer parameters.
|
|
692
|
+
* @param {string} params.recipient The recipient address.
|
|
693
|
+
* @param {string} [params.memo] Optional memo for the transaction.
|
|
694
|
+
* @param {FeeRate} [params.feeRate] Optional fee rate. Defaults to 'fast' rate.
|
|
695
|
+
* @param {number} [params.walletIndex] Optional wallet index. Defaults to 0.
|
|
696
|
+
* @param {UtxoSelectionPreferences} [params.utxoSelectionPreferences] Optional UTXO selection preferences.
|
|
697
|
+
* @returns {Promise<{ hash: TxHash; maxAmount: number; fee: number }>} The transaction hash, amount sent, and fee.
|
|
698
|
+
*/
|
|
699
|
+
transferMax(params) {
|
|
700
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
701
|
+
const feeRate = params.feeRate || (yield this.getFeeRates())[xchainClient.FeeOption.Fast];
|
|
702
|
+
xchainClient.checkFeeBounds(this.feeBounds, feeRate);
|
|
703
|
+
const fromAddressIndex = params.walletIndex || 0;
|
|
704
|
+
const sender = yield this.getAddressAsync(fromAddressIndex);
|
|
705
|
+
const { psbt, maxAmount, fee } = yield this.sendMax({
|
|
706
|
+
sender,
|
|
707
|
+
recipient: params.recipient,
|
|
708
|
+
memo: params.memo,
|
|
709
|
+
feeRate,
|
|
710
|
+
utxoSelectionPreferences: params.utxoSelectionPreferences,
|
|
711
|
+
});
|
|
712
|
+
const dogeKeys = this.getDogeKeys(this.phrase, fromAddressIndex);
|
|
713
|
+
psbt.signAllInputs(dogeKeys);
|
|
714
|
+
psbt.finalizeAllInputs();
|
|
715
|
+
const txHex = psbt.extractTransaction().toHex();
|
|
716
|
+
const hash = yield this.roundRobinBroadcastTx(txHex);
|
|
717
|
+
return { hash, maxAmount, fee };
|
|
718
|
+
});
|
|
719
|
+
}
|
|
531
720
|
}
|
|
532
721
|
|
|
533
722
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xchainjs/xchain-doge",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Custom Doge client and utilities used by XChain clients",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Xchain",
|
|
@@ -36,18 +36,18 @@
|
|
|
36
36
|
"@bitcoin-js/tiny-secp256k1-asmjs": "^2.2.3",
|
|
37
37
|
"@ledgerhq/hw-app-btc": "^10.9.0",
|
|
38
38
|
"@scure/bip32": "^1.7.0",
|
|
39
|
-
"@xchainjs/xchain-client": "2.0.
|
|
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
|
|
43
|
-
"@xchainjs/xchain-utxo-providers": "2.0.
|
|
42
|
+
"@xchainjs/xchain-utxo": "2.1.0",
|
|
43
|
+
"@xchainjs/xchain-utxo-providers": "2.0.10",
|
|
44
44
|
"bitcoinjs-lib": "^6.1.7",
|
|
45
45
|
"coinselect": "3.1.12",
|
|
46
46
|
"ecpair": "2.1.0"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@ledgerhq/hw-transport-node-hid": "^6.28.6",
|
|
50
|
-
"axios": "1.
|
|
50
|
+
"axios": "1.13.5",
|
|
51
51
|
"axios-mock-adapter": "^2.1.0"
|
|
52
52
|
},
|
|
53
53
|
"publishConfig": {
|