@xchainjs/xchain-dash 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  import { AssetInfo, FeeRate, TxHistoryParams } from '@xchainjs/xchain-client';
2
2
  import { Address } from '@xchainjs/xchain-util';
3
- import { Balance, Client as UTXOClient, Tx, TxParams, TxsPage, UTXO, UtxoClientParams } from '@xchainjs/xchain-utxo';
3
+ import { Balance, Client as UTXOClient, PreparedTx, Tx, TxParams, TxsPage, UTXO, UtxoClientParams, UtxoSelectionPreferences } from '@xchainjs/xchain-utxo';
4
4
  import { DashPreparedTx, NodeAuth, NodeUrls } from './types';
5
5
  /**
6
6
  * Default parameters for the DASH client.
@@ -59,6 +59,7 @@ declare abstract class Client extends UTXOClient {
59
59
  private insightTxToXChainTx;
60
60
  /**
61
61
  * Asynchronously prepares a transaction for sending assets.
62
+ * @deprecated Use `prepareTxEnhanced` instead for better UTXO selection and error handling.
62
63
  * @param {TxParams&Address&FeeRate} params - Parameters for the transaction preparation.
63
64
  * @returns {string} A promise resolving to the prepared transaction data.
64
65
  */
@@ -80,5 +81,31 @@ declare abstract class Client extends UTXOClient {
80
81
  * @returns {number} The calculated transaction fee amount.
81
82
  */
82
83
  protected getFeeFromUtxos(inputs: UTXO[], feeRate: FeeRate, data?: Buffer | null): number;
84
+ /**
85
+ * Prepare transaction with enhanced UTXO selection.
86
+ * Uses base class UTXO selection logic with dashcore-lib transaction building.
87
+ */
88
+ prepareTxEnhanced({ sender, memo, amount, recipient, feeRate, spendPendingUTXO, utxoSelectionPreferences, selectedUtxos, }: TxParams & {
89
+ sender: Address;
90
+ feeRate: FeeRate;
91
+ spendPendingUTXO?: boolean;
92
+ utxoSelectionPreferences?: UtxoSelectionPreferences;
93
+ selectedUtxos?: UTXO[];
94
+ }): Promise<PreparedTx>;
95
+ /**
96
+ * Prepare max send transaction
97
+ */
98
+ prepareMaxTx({ sender, recipient, memo, feeRate, spendPendingUTXO, utxoSelectionPreferences, selectedUtxos, }: {
99
+ sender: Address;
100
+ recipient: Address;
101
+ memo?: string;
102
+ feeRate: FeeRate;
103
+ spendPendingUTXO?: boolean;
104
+ utxoSelectionPreferences?: UtxoSelectionPreferences;
105
+ selectedUtxos?: UTXO[];
106
+ }): Promise<PreparedTx & {
107
+ maxAmount: number;
108
+ fee: number;
109
+ }>;
83
110
  }
84
111
  export { Client };
@@ -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, UTXO, UtxoSelectionPreferences } from '@xchainjs/xchain-utxo';
4
4
  import { ECPairInterface } from 'ecpair';
5
5
  import { Client } from './client';
6
6
  export declare class ClientKeystore extends Client {
@@ -30,10 +30,36 @@ export declare class ClientKeystore extends Client {
30
30
  getDashKeys(phrase: string, index?: number): ECPairInterface;
31
31
  /**
32
32
  * Asynchronously transfers assets between addresses.
33
- * @param {TxParams & { feeRate?: FeeRate }} params - Parameters for the transfer.
33
+ * @param {TxParams & { feeRate?: FeeRate; utxoSelectionPreferences?: UtxoSelectionPreferences }} params - Parameters for the transfer.
34
34
  * @returns {Promise<TxHash>} A promise resolving to the transaction hash.
35
35
  */
36
36
  transfer(params: TxParams & {
37
37
  feeRate?: FeeRate;
38
+ utxoSelectionPreferences?: UtxoSelectionPreferences;
39
+ selectedUtxos?: UTXO[];
38
40
  }): Promise<TxHash>;
41
+ /**
42
+ * Transfer the maximum amount of DASH (sweep).
43
+ *
44
+ * Calculates the maximum sendable amount after fees, signs, and broadcasts the transaction.
45
+ * @param {Object} params The transfer parameters.
46
+ * @param {string} params.recipient The recipient address.
47
+ * @param {string} [params.memo] Optional memo for the transaction.
48
+ * @param {FeeRate} [params.feeRate] Optional fee rate. Defaults to 'average' rate.
49
+ * @param {number} [params.walletIndex] Optional wallet index. Defaults to 0.
50
+ * @param {UtxoSelectionPreferences} [params.utxoSelectionPreferences] Optional UTXO selection preferences.
51
+ * @returns {Promise<{ hash: TxHash; maxAmount: number; fee: number }>} The transaction hash, amount sent, and fee.
52
+ */
53
+ transferMax(params: {
54
+ recipient: Address;
55
+ memo?: string;
56
+ feeRate?: FeeRate;
57
+ walletIndex?: number;
58
+ utxoSelectionPreferences?: UtxoSelectionPreferences;
59
+ selectedUtxos?: UTXO[];
60
+ }): Promise<{
61
+ hash: TxHash;
62
+ maxAmount: number;
63
+ fee: number;
64
+ }>;
39
65
  }
@@ -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 { NodeAuth, NodeUrls } from './types';
7
7
  /**
@@ -21,5 +21,17 @@ declare class ClientLedger extends Client {
21
21
  transfer(params: TxParams & {
22
22
  feeRate?: FeeRate;
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
@@ -2,7 +2,7 @@ import { ExplorerProvider, Network, TxType, FeeOption, checkFeeBounds } from '@x
2
2
  import { AssetType, baseAmount, assetToBase, assetAmount } from '@xchainjs/xchain-util';
3
3
  import { BlockcypherProvider, BlockcypherNetwork, BitgoProvider } from '@xchainjs/xchain-utxo-providers';
4
4
  import dashcore from '@dashevo/dashcore-lib';
5
- import { toBitcoinJS, Client as Client$1 } from '@xchainjs/xchain-utxo';
5
+ import { toBitcoinJS, Client as Client$1, UtxoTransactionValidator, UtxoError } from '@xchainjs/xchain-utxo';
6
6
  import * as Dash from 'bitcoinjs-lib';
7
7
  import accumulative from 'coinselect/accumulative.js';
8
8
  import * as ecc from '@bitcoin-js/tiny-secp256k1-asmjs';
@@ -4449,6 +4449,7 @@ class Client extends Client$1 {
4449
4449
  }
4450
4450
  /**
4451
4451
  * Asynchronously prepares a transaction for sending assets.
4452
+ * @deprecated Use `prepareTxEnhanced` instead for better UTXO selection and error handling.
4452
4453
  * @param {TxParams&Address&FeeRate} params - Parameters for the transaction preparation.
4453
4454
  * @returns {string} A promise resolving to the prepared transaction data.
4454
4455
  */
@@ -4515,6 +4516,137 @@ class Client extends Client$1 {
4515
4516
  // Ensure fee meets minimum requirement
4516
4517
  return fee > TX_MIN_FEE ? fee : TX_MIN_FEE;
4517
4518
  }
4519
+ // ==================== Enhanced Transaction Methods ====================
4520
+ /**
4521
+ * Prepare transaction with enhanced UTXO selection.
4522
+ * Uses base class UTXO selection logic with dashcore-lib transaction building.
4523
+ */
4524
+ prepareTxEnhanced(_a) {
4525
+ return __awaiter(this, arguments, void 0, function* ({ sender, memo, amount, recipient, feeRate, spendPendingUTXO = true, utxoSelectionPreferences, selectedUtxos, }) {
4526
+ try {
4527
+ // Validate inputs using base class method
4528
+ this.validateTransactionInputs({
4529
+ amount,
4530
+ recipient,
4531
+ memo,
4532
+ sender,
4533
+ feeRate,
4534
+ });
4535
+ // Use provided UTXOs (coin control) or fetch from chain
4536
+ let utxos;
4537
+ if (selectedUtxos && selectedUtxos.length > 0) {
4538
+ UtxoTransactionValidator.validateUtxoSet(selectedUtxos);
4539
+ utxos = selectedUtxos;
4540
+ }
4541
+ else {
4542
+ const confirmedOnly = !spendPendingUTXO;
4543
+ utxos = yield this.getValidatedUtxos(sender, confirmedOnly);
4544
+ }
4545
+ const compiledMemo = memo ? this.compileMemo(memo) : null;
4546
+ const targetValue = amount.amount().toNumber();
4547
+ const extraOutputs = 1 + (compiledMemo ? 1 : 0);
4548
+ // Use base class UTXO selection
4549
+ const selectionResult = this.selectUtxosForTransaction(utxos, targetValue, Math.ceil(feeRate), extraOutputs, utxoSelectionPreferences);
4550
+ // Build transaction using dashcore-lib
4551
+ const tx = new dashcore.Transaction().to(recipient, targetValue);
4552
+ // Add selected inputs
4553
+ for (const utxo of selectionResult.inputs) {
4554
+ const scriptBuffer = Buffer.from(utxo.scriptPubKey || '', 'hex');
4555
+ const script = new dashcore.Script(scriptBuffer);
4556
+ const input = new dashcore.Transaction.Input.PublicKeyHash({
4557
+ prevTxId: Buffer.from(utxo.hash, 'hex'),
4558
+ outputIndex: utxo.index,
4559
+ script: '',
4560
+ output: new dashcore.Transaction.Output({
4561
+ satoshis: utxo.value,
4562
+ script,
4563
+ }),
4564
+ });
4565
+ tx.uncheckedAddInput(input);
4566
+ }
4567
+ // Set change address
4568
+ const senderAddress = dashcore.Address.fromString(sender, this.network);
4569
+ tx.change(senderAddress);
4570
+ // Add memo if provided
4571
+ if (memo) {
4572
+ tx.addData(memo);
4573
+ }
4574
+ // ESLint disabled: Dash transaction has proper toString() method that returns hex string
4575
+ // eslint-disable-next-line @typescript-eslint/no-base-to-string
4576
+ return { rawUnsignedTx: tx.toString(), utxos, inputs: selectionResult.inputs };
4577
+ }
4578
+ catch (error) {
4579
+ if (UtxoError.isUtxoError(error)) {
4580
+ throw error;
4581
+ }
4582
+ throw UtxoError.fromUnknown(error, 'prepareTxEnhanced');
4583
+ }
4584
+ });
4585
+ }
4586
+ /**
4587
+ * Prepare max send transaction
4588
+ */
4589
+ prepareMaxTx(_a) {
4590
+ return __awaiter(this, arguments, void 0, function* ({ sender, recipient, memo, feeRate, spendPendingUTXO = true, utxoSelectionPreferences, selectedUtxos, }) {
4591
+ try {
4592
+ // Validate addresses
4593
+ if (!this.validateAddress(recipient)) {
4594
+ throw UtxoError.invalidAddress(recipient, this.network);
4595
+ }
4596
+ if (!this.validateAddress(sender)) {
4597
+ throw UtxoError.invalidAddress(sender, this.network);
4598
+ }
4599
+ // Use provided UTXOs (coin control) or fetch from chain
4600
+ let utxos;
4601
+ if (selectedUtxos && selectedUtxos.length > 0) {
4602
+ UtxoTransactionValidator.validateUtxoSet(selectedUtxos);
4603
+ utxos = selectedUtxos;
4604
+ }
4605
+ else {
4606
+ const confirmedOnly = !spendPendingUTXO;
4607
+ utxos = yield this.getValidatedUtxos(sender, confirmedOnly);
4608
+ }
4609
+ // Calculate max using base class method
4610
+ const maxCalc = this.calculateMaxSendableAmount(utxos, Math.ceil(feeRate), !!memo, utxoSelectionPreferences);
4611
+ // Build transaction using dashcore-lib
4612
+ const tx = new dashcore.Transaction().to(recipient, maxCalc.amount);
4613
+ // Add inputs
4614
+ for (const utxo of maxCalc.inputs) {
4615
+ const scriptBuffer = Buffer.from(utxo.scriptPubKey || '', 'hex');
4616
+ const script = new dashcore.Script(scriptBuffer);
4617
+ const input = new dashcore.Transaction.Input.PublicKeyHash({
4618
+ prevTxId: Buffer.from(utxo.hash, 'hex'),
4619
+ outputIndex: utxo.index,
4620
+ script: '',
4621
+ output: new dashcore.Transaction.Output({
4622
+ satoshis: utxo.value,
4623
+ script,
4624
+ }),
4625
+ });
4626
+ tx.uncheckedAddInput(input);
4627
+ }
4628
+ // Add memo if provided
4629
+ if (memo) {
4630
+ tx.addData(memo);
4631
+ }
4632
+ // ESLint disabled: Dash transaction has proper toString() method that returns hex string
4633
+ // eslint-disable-next-line @typescript-eslint/no-base-to-string
4634
+ return {
4635
+ rawUnsignedTx: tx.toString(),
4636
+ utxos,
4637
+ inputs: maxCalc.inputs,
4638
+ maxAmount: maxCalc.amount,
4639
+ fee: maxCalc.fee,
4640
+ };
4641
+ }
4642
+ catch (error) {
4643
+ if (UtxoError.isUtxoError(error)) {
4644
+ throw error;
4645
+ }
4646
+ throw UtxoError.fromUnknown(error, 'prepareMaxTx');
4647
+ }
4648
+ });
4649
+ }
4518
4650
  }
4519
4651
 
4520
4652
  /**
@@ -4605,7 +4737,7 @@ class ClientKeystore extends Client {
4605
4737
  }
4606
4738
  /**
4607
4739
  * Asynchronously transfers assets between addresses.
4608
- * @param {TxParams & { feeRate?: FeeRate }} params - Parameters for the transfer.
4740
+ * @param {TxParams & { feeRate?: FeeRate; utxoSelectionPreferences?: UtxoSelectionPreferences }} params - Parameters for the transfer.
4609
4741
  * @returns {Promise<TxHash>} A promise resolving to the transaction hash.
4610
4742
  */
4611
4743
  transfer(params) {
@@ -4614,7 +4746,9 @@ class ClientKeystore extends Client {
4614
4746
  const feeRate = params.feeRate || (yield this.getFeeRates())[FeeOption.Average];
4615
4747
  checkFeeBounds(this.feeBounds, feeRate);
4616
4748
  const fromAddressIndex = params.walletIndex || 0;
4617
- const { rawUnsignedTx, utxos } = yield this.prepareTx(Object.assign(Object.assign({}, params), { feeRate, sender: yield this.getAddressAsync(fromAddressIndex) }));
4749
+ // Merge default preferences
4750
+ const mergedPreferences = Object.assign({ minimizeFee: true, avoidDust: true, minimizeInputs: false }, params.utxoSelectionPreferences);
4751
+ const { rawUnsignedTx, utxos } = yield this.prepareTxEnhanced(Object.assign(Object.assign({}, params), { feeRate, sender: yield this.getAddressAsync(fromAddressIndex), utxoSelectionPreferences: mergedPreferences, selectedUtxos: params.selectedUtxos }));
4618
4752
  const tx = new dashcore.Transaction(rawUnsignedTx);
4619
4753
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
4620
4754
  tx.inputs.forEach((input, index) => {
@@ -4646,6 +4780,65 @@ class ClientKeystore extends Client {
4646
4780
  });
4647
4781
  });
4648
4782
  }
4783
+ /**
4784
+ * Transfer the maximum amount of DASH (sweep).
4785
+ *
4786
+ * Calculates the maximum sendable amount after fees, signs, and broadcasts the transaction.
4787
+ * @param {Object} params The transfer parameters.
4788
+ * @param {string} params.recipient The recipient address.
4789
+ * @param {string} [params.memo] Optional memo for the transaction.
4790
+ * @param {FeeRate} [params.feeRate] Optional fee rate. Defaults to 'average' rate.
4791
+ * @param {number} [params.walletIndex] Optional wallet index. Defaults to 0.
4792
+ * @param {UtxoSelectionPreferences} [params.utxoSelectionPreferences] Optional UTXO selection preferences.
4793
+ * @returns {Promise<{ hash: TxHash; maxAmount: number; fee: number }>} The transaction hash, amount sent, and fee.
4794
+ */
4795
+ transferMax(params) {
4796
+ return __awaiter(this, void 0, void 0, function* () {
4797
+ var _a;
4798
+ const feeRate = params.feeRate || (yield this.getFeeRates())[FeeOption.Average];
4799
+ checkFeeBounds(this.feeBounds, feeRate);
4800
+ const fromAddressIndex = params.walletIndex || 0;
4801
+ const sender = yield this.getAddressAsync(fromAddressIndex);
4802
+ const { rawUnsignedTx, utxos, maxAmount, fee } = yield this.prepareMaxTx({
4803
+ sender,
4804
+ recipient: params.recipient,
4805
+ memo: params.memo,
4806
+ feeRate,
4807
+ utxoSelectionPreferences: params.utxoSelectionPreferences,
4808
+ selectedUtxos: params.selectedUtxos,
4809
+ });
4810
+ const tx = new dashcore.Transaction(rawUnsignedTx);
4811
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
4812
+ tx.inputs.forEach((input, index) => {
4813
+ const insightUtxo = utxos.find((utxo) => {
4814
+ return utxo.hash === input.prevTxId.toString('hex') && utxo.index == input.outputIndex;
4815
+ });
4816
+ if (!insightUtxo) {
4817
+ throw new Error('Unable to match accumulative inputs with insight utxos');
4818
+ }
4819
+ const scriptBuffer = Buffer.from(insightUtxo.scriptPubKey || '', 'hex');
4820
+ const script = new dashcore.Script(scriptBuffer);
4821
+ tx.inputs[index] = new dashcore.Transaction.Input.PublicKeyHash({
4822
+ prevTxId: Buffer.from(insightUtxo.hash, 'hex'),
4823
+ outputIndex: insightUtxo.index,
4824
+ script: '',
4825
+ output: new dashcore.Transaction.Output({
4826
+ satoshis: insightUtxo.value,
4827
+ script,
4828
+ }),
4829
+ });
4830
+ });
4831
+ const dashKeys = this.getDashKeys(this.phrase, fromAddressIndex);
4832
+ tx.sign(`${(_a = dashKeys.privateKey) === null || _a === void 0 ? void 0 : _a.toString('hex')}`);
4833
+ const txHex = tx.checkedSerialize({});
4834
+ const hash = yield broadcastTx({
4835
+ txHex,
4836
+ nodeUrl: this.nodeUrls[this.network],
4837
+ auth: this.nodeAuth,
4838
+ });
4839
+ return { hash, maxAmount, fee };
4840
+ });
4841
+ }
4649
4842
  }
4650
4843
 
4651
4844
  /**
@@ -4738,6 +4931,58 @@ class ClientLedger extends Client {
4738
4931
  return txHash;
4739
4932
  });
4740
4933
  }
4934
+ // Transfer max DASH from Ledger (sweep transaction)
4935
+ transferMax(params) {
4936
+ return __awaiter(this, void 0, void 0, function* () {
4937
+ const app = yield this.getApp();
4938
+ const fromAddressIndex = (params === null || params === void 0 ? void 0 : params.walletIndex) || 0;
4939
+ const feeRate = params.feeRate || (yield this.getFeeRates())[FeeOption.Fast];
4940
+ checkFeeBounds(this.feeBounds, feeRate);
4941
+ const sender = yield this.getAddressAsync(fromAddressIndex);
4942
+ const { rawUnsignedTx, inputs, maxAmount, fee } = yield this.prepareMaxTx({
4943
+ sender,
4944
+ recipient: params.recipient,
4945
+ memo: params.memo,
4946
+ feeRate,
4947
+ utxoSelectionPreferences: params.utxoSelectionPreferences,
4948
+ selectedUtxos: params.selectedUtxos,
4949
+ });
4950
+ const tx = new dashcore.Transaction(rawUnsignedTx);
4951
+ const ledgerInputs = [];
4952
+ for (const input of tx.inputs) {
4953
+ const insightUtxo = inputs.find((utxo) => {
4954
+ return utxo.hash === input.prevTxId.toString('hex') && utxo.index == input.outputIndex;
4955
+ });
4956
+ if (!insightUtxo) {
4957
+ throw new Error('Unable to match accumulative inputs with insight utxos');
4958
+ }
4959
+ const txHex = yield getRawTx({ txid: insightUtxo.hash, network: this.network });
4960
+ const utxoTx = new dashcore.Transaction(txHex);
4961
+ const splittedTx = app.splitTransaction(utxoTx.toString());
4962
+ ledgerInputs.push([splittedTx, input.outputIndex, null, null]);
4963
+ }
4964
+ const associatedKeysets = tx.inputs.map(() => this.getFullDerivationPath(fromAddressIndex));
4965
+ const newTx = app.splitTransaction(tx.toString(), true);
4966
+ const outputScriptHex = app.serializeTransactionOutputs(newTx).toString('hex');
4967
+ const txHex = yield app.createPaymentTransaction({
4968
+ inputs: ledgerInputs,
4969
+ associatedKeysets,
4970
+ outputScriptHex,
4971
+ segwit: false,
4972
+ useTrustedInputForSegwit: false,
4973
+ additionals: [],
4974
+ });
4975
+ const hash = yield broadcastTx({
4976
+ txHex,
4977
+ nodeUrl: this.nodeUrls[this.network],
4978
+ auth: this.nodeAuth,
4979
+ });
4980
+ if (!hash) {
4981
+ throw Error('No Tx hash');
4982
+ }
4983
+ return { hash, maxAmount, fee };
4984
+ });
4985
+ }
4741
4986
  }
4742
4987
 
4743
4988
  export { AssetDASH, BitgoProviders, BlockcypherDataProviders, ClientKeystore as Client, ClientKeystore, ClientLedger, DASHChain, DASH_DECIMAL, DASH_SYMBOL, DEFAULT_FEE_RATE, LOWER_FEE_BOUND, MIN_TX_FEE, UPPER_FEE_BOUND, buildTx, defaultDashParams, explorerProviders, getPrefix, validateAddress };
package/lib/index.js CHANGED
@@ -4477,6 +4477,7 @@ class Client extends xchainUtxo.Client {
4477
4477
  }
4478
4478
  /**
4479
4479
  * Asynchronously prepares a transaction for sending assets.
4480
+ * @deprecated Use `prepareTxEnhanced` instead for better UTXO selection and error handling.
4480
4481
  * @param {TxParams&Address&FeeRate} params - Parameters for the transaction preparation.
4481
4482
  * @returns {string} A promise resolving to the prepared transaction data.
4482
4483
  */
@@ -4543,6 +4544,137 @@ class Client extends xchainUtxo.Client {
4543
4544
  // Ensure fee meets minimum requirement
4544
4545
  return fee > TX_MIN_FEE ? fee : TX_MIN_FEE;
4545
4546
  }
4547
+ // ==================== Enhanced Transaction Methods ====================
4548
+ /**
4549
+ * Prepare transaction with enhanced UTXO selection.
4550
+ * Uses base class UTXO selection logic with dashcore-lib transaction building.
4551
+ */
4552
+ prepareTxEnhanced(_a) {
4553
+ return __awaiter(this, arguments, void 0, function* ({ sender, memo, amount, recipient, feeRate, spendPendingUTXO = true, utxoSelectionPreferences, selectedUtxos, }) {
4554
+ try {
4555
+ // Validate inputs using base class method
4556
+ this.validateTransactionInputs({
4557
+ amount,
4558
+ recipient,
4559
+ memo,
4560
+ sender,
4561
+ feeRate,
4562
+ });
4563
+ // Use provided UTXOs (coin control) or fetch from chain
4564
+ let utxos;
4565
+ if (selectedUtxos && selectedUtxos.length > 0) {
4566
+ xchainUtxo.UtxoTransactionValidator.validateUtxoSet(selectedUtxos);
4567
+ utxos = selectedUtxos;
4568
+ }
4569
+ else {
4570
+ const confirmedOnly = !spendPendingUTXO;
4571
+ utxos = yield this.getValidatedUtxos(sender, confirmedOnly);
4572
+ }
4573
+ const compiledMemo = memo ? this.compileMemo(memo) : null;
4574
+ const targetValue = amount.amount().toNumber();
4575
+ const extraOutputs = 1 + (compiledMemo ? 1 : 0);
4576
+ // Use base class UTXO selection
4577
+ const selectionResult = this.selectUtxosForTransaction(utxos, targetValue, Math.ceil(feeRate), extraOutputs, utxoSelectionPreferences);
4578
+ // Build transaction using dashcore-lib
4579
+ const tx = new dashcore__default.default.Transaction().to(recipient, targetValue);
4580
+ // Add selected inputs
4581
+ for (const utxo of selectionResult.inputs) {
4582
+ const scriptBuffer = Buffer.from(utxo.scriptPubKey || '', 'hex');
4583
+ const script = new dashcore__default.default.Script(scriptBuffer);
4584
+ const input = new dashcore__default.default.Transaction.Input.PublicKeyHash({
4585
+ prevTxId: Buffer.from(utxo.hash, 'hex'),
4586
+ outputIndex: utxo.index,
4587
+ script: '',
4588
+ output: new dashcore__default.default.Transaction.Output({
4589
+ satoshis: utxo.value,
4590
+ script,
4591
+ }),
4592
+ });
4593
+ tx.uncheckedAddInput(input);
4594
+ }
4595
+ // Set change address
4596
+ const senderAddress = dashcore__default.default.Address.fromString(sender, this.network);
4597
+ tx.change(senderAddress);
4598
+ // Add memo if provided
4599
+ if (memo) {
4600
+ tx.addData(memo);
4601
+ }
4602
+ // ESLint disabled: Dash transaction has proper toString() method that returns hex string
4603
+ // eslint-disable-next-line @typescript-eslint/no-base-to-string
4604
+ return { rawUnsignedTx: tx.toString(), utxos, inputs: selectionResult.inputs };
4605
+ }
4606
+ catch (error) {
4607
+ if (xchainUtxo.UtxoError.isUtxoError(error)) {
4608
+ throw error;
4609
+ }
4610
+ throw xchainUtxo.UtxoError.fromUnknown(error, 'prepareTxEnhanced');
4611
+ }
4612
+ });
4613
+ }
4614
+ /**
4615
+ * Prepare max send transaction
4616
+ */
4617
+ prepareMaxTx(_a) {
4618
+ return __awaiter(this, arguments, void 0, function* ({ sender, recipient, memo, feeRate, spendPendingUTXO = true, utxoSelectionPreferences, selectedUtxos, }) {
4619
+ try {
4620
+ // Validate addresses
4621
+ if (!this.validateAddress(recipient)) {
4622
+ throw xchainUtxo.UtxoError.invalidAddress(recipient, this.network);
4623
+ }
4624
+ if (!this.validateAddress(sender)) {
4625
+ throw xchainUtxo.UtxoError.invalidAddress(sender, this.network);
4626
+ }
4627
+ // Use provided UTXOs (coin control) or fetch from chain
4628
+ let utxos;
4629
+ if (selectedUtxos && selectedUtxos.length > 0) {
4630
+ xchainUtxo.UtxoTransactionValidator.validateUtxoSet(selectedUtxos);
4631
+ utxos = selectedUtxos;
4632
+ }
4633
+ else {
4634
+ const confirmedOnly = !spendPendingUTXO;
4635
+ utxos = yield this.getValidatedUtxos(sender, confirmedOnly);
4636
+ }
4637
+ // Calculate max using base class method
4638
+ const maxCalc = this.calculateMaxSendableAmount(utxos, Math.ceil(feeRate), !!memo, utxoSelectionPreferences);
4639
+ // Build transaction using dashcore-lib
4640
+ const tx = new dashcore__default.default.Transaction().to(recipient, maxCalc.amount);
4641
+ // Add inputs
4642
+ for (const utxo of maxCalc.inputs) {
4643
+ const scriptBuffer = Buffer.from(utxo.scriptPubKey || '', 'hex');
4644
+ const script = new dashcore__default.default.Script(scriptBuffer);
4645
+ const input = new dashcore__default.default.Transaction.Input.PublicKeyHash({
4646
+ prevTxId: Buffer.from(utxo.hash, 'hex'),
4647
+ outputIndex: utxo.index,
4648
+ script: '',
4649
+ output: new dashcore__default.default.Transaction.Output({
4650
+ satoshis: utxo.value,
4651
+ script,
4652
+ }),
4653
+ });
4654
+ tx.uncheckedAddInput(input);
4655
+ }
4656
+ // Add memo if provided
4657
+ if (memo) {
4658
+ tx.addData(memo);
4659
+ }
4660
+ // ESLint disabled: Dash transaction has proper toString() method that returns hex string
4661
+ // eslint-disable-next-line @typescript-eslint/no-base-to-string
4662
+ return {
4663
+ rawUnsignedTx: tx.toString(),
4664
+ utxos,
4665
+ inputs: maxCalc.inputs,
4666
+ maxAmount: maxCalc.amount,
4667
+ fee: maxCalc.fee,
4668
+ };
4669
+ }
4670
+ catch (error) {
4671
+ if (xchainUtxo.UtxoError.isUtxoError(error)) {
4672
+ throw error;
4673
+ }
4674
+ throw xchainUtxo.UtxoError.fromUnknown(error, 'prepareMaxTx');
4675
+ }
4676
+ });
4677
+ }
4546
4678
  }
4547
4679
 
4548
4680
  /**
@@ -4633,7 +4765,7 @@ class ClientKeystore extends Client {
4633
4765
  }
4634
4766
  /**
4635
4767
  * Asynchronously transfers assets between addresses.
4636
- * @param {TxParams & { feeRate?: FeeRate }} params - Parameters for the transfer.
4768
+ * @param {TxParams & { feeRate?: FeeRate; utxoSelectionPreferences?: UtxoSelectionPreferences }} params - Parameters for the transfer.
4637
4769
  * @returns {Promise<TxHash>} A promise resolving to the transaction hash.
4638
4770
  */
4639
4771
  transfer(params) {
@@ -4642,7 +4774,9 @@ class ClientKeystore extends Client {
4642
4774
  const feeRate = params.feeRate || (yield this.getFeeRates())[xchainClient.FeeOption.Average];
4643
4775
  xchainClient.checkFeeBounds(this.feeBounds, feeRate);
4644
4776
  const fromAddressIndex = params.walletIndex || 0;
4645
- const { rawUnsignedTx, utxos } = yield this.prepareTx(Object.assign(Object.assign({}, params), { feeRate, sender: yield this.getAddressAsync(fromAddressIndex) }));
4777
+ // Merge default preferences
4778
+ const mergedPreferences = Object.assign({ minimizeFee: true, avoidDust: true, minimizeInputs: false }, params.utxoSelectionPreferences);
4779
+ const { rawUnsignedTx, utxos } = yield this.prepareTxEnhanced(Object.assign(Object.assign({}, params), { feeRate, sender: yield this.getAddressAsync(fromAddressIndex), utxoSelectionPreferences: mergedPreferences, selectedUtxos: params.selectedUtxos }));
4646
4780
  const tx = new dashcore__default.default.Transaction(rawUnsignedTx);
4647
4781
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
4648
4782
  tx.inputs.forEach((input, index) => {
@@ -4674,6 +4808,65 @@ class ClientKeystore extends Client {
4674
4808
  });
4675
4809
  });
4676
4810
  }
4811
+ /**
4812
+ * Transfer the maximum amount of DASH (sweep).
4813
+ *
4814
+ * Calculates the maximum sendable amount after fees, signs, and broadcasts the transaction.
4815
+ * @param {Object} params The transfer parameters.
4816
+ * @param {string} params.recipient The recipient address.
4817
+ * @param {string} [params.memo] Optional memo for the transaction.
4818
+ * @param {FeeRate} [params.feeRate] Optional fee rate. Defaults to 'average' rate.
4819
+ * @param {number} [params.walletIndex] Optional wallet index. Defaults to 0.
4820
+ * @param {UtxoSelectionPreferences} [params.utxoSelectionPreferences] Optional UTXO selection preferences.
4821
+ * @returns {Promise<{ hash: TxHash; maxAmount: number; fee: number }>} The transaction hash, amount sent, and fee.
4822
+ */
4823
+ transferMax(params) {
4824
+ return __awaiter(this, void 0, void 0, function* () {
4825
+ var _a;
4826
+ const feeRate = params.feeRate || (yield this.getFeeRates())[xchainClient.FeeOption.Average];
4827
+ xchainClient.checkFeeBounds(this.feeBounds, feeRate);
4828
+ const fromAddressIndex = params.walletIndex || 0;
4829
+ const sender = yield this.getAddressAsync(fromAddressIndex);
4830
+ const { rawUnsignedTx, utxos, maxAmount, fee } = yield this.prepareMaxTx({
4831
+ sender,
4832
+ recipient: params.recipient,
4833
+ memo: params.memo,
4834
+ feeRate,
4835
+ utxoSelectionPreferences: params.utxoSelectionPreferences,
4836
+ selectedUtxos: params.selectedUtxos,
4837
+ });
4838
+ const tx = new dashcore__default.default.Transaction(rawUnsignedTx);
4839
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
4840
+ tx.inputs.forEach((input, index) => {
4841
+ const insightUtxo = utxos.find((utxo) => {
4842
+ return utxo.hash === input.prevTxId.toString('hex') && utxo.index == input.outputIndex;
4843
+ });
4844
+ if (!insightUtxo) {
4845
+ throw new Error('Unable to match accumulative inputs with insight utxos');
4846
+ }
4847
+ const scriptBuffer = Buffer.from(insightUtxo.scriptPubKey || '', 'hex');
4848
+ const script = new dashcore__default.default.Script(scriptBuffer);
4849
+ tx.inputs[index] = new dashcore__default.default.Transaction.Input.PublicKeyHash({
4850
+ prevTxId: Buffer.from(insightUtxo.hash, 'hex'),
4851
+ outputIndex: insightUtxo.index,
4852
+ script: '',
4853
+ output: new dashcore__default.default.Transaction.Output({
4854
+ satoshis: insightUtxo.value,
4855
+ script,
4856
+ }),
4857
+ });
4858
+ });
4859
+ const dashKeys = this.getDashKeys(this.phrase, fromAddressIndex);
4860
+ tx.sign(`${(_a = dashKeys.privateKey) === null || _a === void 0 ? void 0 : _a.toString('hex')}`);
4861
+ const txHex = tx.checkedSerialize({});
4862
+ const hash = yield broadcastTx({
4863
+ txHex,
4864
+ nodeUrl: this.nodeUrls[this.network],
4865
+ auth: this.nodeAuth,
4866
+ });
4867
+ return { hash, maxAmount, fee };
4868
+ });
4869
+ }
4677
4870
  }
4678
4871
 
4679
4872
  /**
@@ -4766,6 +4959,58 @@ class ClientLedger extends Client {
4766
4959
  return txHash;
4767
4960
  });
4768
4961
  }
4962
+ // Transfer max DASH from Ledger (sweep transaction)
4963
+ transferMax(params) {
4964
+ return __awaiter(this, void 0, void 0, function* () {
4965
+ const app = yield this.getApp();
4966
+ const fromAddressIndex = (params === null || params === void 0 ? void 0 : params.walletIndex) || 0;
4967
+ const feeRate = params.feeRate || (yield this.getFeeRates())[xchainClient.FeeOption.Fast];
4968
+ xchainClient.checkFeeBounds(this.feeBounds, feeRate);
4969
+ const sender = yield this.getAddressAsync(fromAddressIndex);
4970
+ const { rawUnsignedTx, inputs, maxAmount, fee } = yield this.prepareMaxTx({
4971
+ sender,
4972
+ recipient: params.recipient,
4973
+ memo: params.memo,
4974
+ feeRate,
4975
+ utxoSelectionPreferences: params.utxoSelectionPreferences,
4976
+ selectedUtxos: params.selectedUtxos,
4977
+ });
4978
+ const tx = new dashcore__default.default.Transaction(rawUnsignedTx);
4979
+ const ledgerInputs = [];
4980
+ for (const input of tx.inputs) {
4981
+ const insightUtxo = inputs.find((utxo) => {
4982
+ return utxo.hash === input.prevTxId.toString('hex') && utxo.index == input.outputIndex;
4983
+ });
4984
+ if (!insightUtxo) {
4985
+ throw new Error('Unable to match accumulative inputs with insight utxos');
4986
+ }
4987
+ const txHex = yield getRawTx({ txid: insightUtxo.hash, network: this.network });
4988
+ const utxoTx = new dashcore__default.default.Transaction(txHex);
4989
+ const splittedTx = app.splitTransaction(utxoTx.toString());
4990
+ ledgerInputs.push([splittedTx, input.outputIndex, null, null]);
4991
+ }
4992
+ const associatedKeysets = tx.inputs.map(() => this.getFullDerivationPath(fromAddressIndex));
4993
+ const newTx = app.splitTransaction(tx.toString(), true);
4994
+ const outputScriptHex = app.serializeTransactionOutputs(newTx).toString('hex');
4995
+ const txHex = yield app.createPaymentTransaction({
4996
+ inputs: ledgerInputs,
4997
+ associatedKeysets,
4998
+ outputScriptHex,
4999
+ segwit: false,
5000
+ useTrustedInputForSegwit: false,
5001
+ additionals: [],
5002
+ });
5003
+ const hash = yield broadcastTx({
5004
+ txHex,
5005
+ nodeUrl: this.nodeUrls[this.network],
5006
+ auth: this.nodeAuth,
5007
+ });
5008
+ if (!hash) {
5009
+ throw Error('No Tx hash');
5010
+ }
5011
+ return { hash, maxAmount, fee };
5012
+ });
5013
+ }
4769
5014
  }
4770
5015
 
4771
5016
  exports.AssetDASH = AssetDASH;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xchainjs/xchain-dash",
3
- "version": "2.0.10",
3
+ "version": "2.2.0",
4
4
  "description": "Custom Dash client and utilities used by XChainJS clients",
5
5
  "keywords": [
6
6
  "XChain",
@@ -40,7 +40,7 @@
40
40
  "@xchainjs/xchain-client": "2.0.10",
41
41
  "@xchainjs/xchain-crypto": "1.0.6",
42
42
  "@xchainjs/xchain-util": "2.0.5",
43
- "@xchainjs/xchain-utxo": "2.0.10",
43
+ "@xchainjs/xchain-utxo": "2.2.0",
44
44
  "@xchainjs/xchain-utxo-providers": "2.0.10",
45
45
  "bitcoinjs-lib": "^6.1.7",
46
46
  "coinselect": "3.1.12",