@yaswap/yacoin 3.4.1 → 3.4.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # @yaswap/bitcoin
2
2
 
3
+ ## 3.4.3
4
+
5
+ ### Patch Changes
6
+
7
+ - Improve getUsedUnusedAddresses and buildTransaction
8
+ - Updated dependencies
9
+ - @yaswap/client@3.4.3
10
+ - @yaswap/errors@3.4.3
11
+ - @yaswap/types@3.4.3
12
+ - @yaswap/utils@3.4.3
13
+
14
+ ## 3.4.2
15
+
16
+ ### Patch Changes
17
+
18
+ - Support HD wallet logic
19
+ - Updated dependencies
20
+ - @yaswap/client@3.4.2
21
+ - @yaswap/errors@3.4.2
22
+ - @yaswap/types@3.4.2
23
+ - @yaswap/utils@3.4.2
24
+
3
25
  ## 3.4.1
4
26
 
5
27
  ### Patch Changes
@@ -8,7 +8,7 @@ export { YacoinSwapBaseProvider } from './swap/YacoinSwapBaseProvider';
8
8
  export { YacoinSwapEsploraProvider } from './swap/YacoinSwapEsploraProvider';
9
9
  export * as YacoinTypes from './types';
10
10
  export * as YacoinUtils from './utils';
11
- export { YacoinBaseWalletProvider } from './wallet/YacoinBaseWallet';
11
+ export { AddressSearchType, YacoinBaseWalletProvider, NUMBER_ADDRESS_LIMIT } from './wallet/YacoinBaseWallet';
12
12
  export { YacoinHDWalletProvider } from './wallet/YacoinHDWallet';
13
13
  export { YacoinNftProvider } from './nft/YacoinNftProvider';
14
14
  export { YacoinSingleWallet } from './wallet/YacoinSingleWallet';
package/dist/lib/index.js CHANGED
@@ -23,7 +23,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
23
23
  return result;
24
24
  };
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.YacoinSingleWallet = exports.YacoinNftProvider = exports.YacoinHDWalletProvider = exports.YacoinBaseWalletProvider = exports.YacoinUtils = exports.YacoinTypes = exports.YacoinSwapEsploraProvider = exports.YacoinSwapBaseProvider = exports.YacoinNetworks = exports.YacoinFeeApiProvider = exports.YacoinEsploraBatchBaseProvider = exports.YacoinEsploraBaseProvider = exports.YacoinEsploraApiProvider = exports.YacoinBaseChainProvider = void 0;
26
+ exports.YacoinSingleWallet = exports.YacoinNftProvider = exports.YacoinHDWalletProvider = exports.NUMBER_ADDRESS_LIMIT = exports.YacoinBaseWalletProvider = exports.AddressSearchType = exports.YacoinUtils = exports.YacoinTypes = exports.YacoinSwapEsploraProvider = exports.YacoinSwapBaseProvider = exports.YacoinNetworks = exports.YacoinFeeApiProvider = exports.YacoinEsploraBatchBaseProvider = exports.YacoinEsploraBaseProvider = exports.YacoinEsploraApiProvider = exports.YacoinBaseChainProvider = void 0;
27
27
  var YacoinBaseChainProvider_1 = require("./chain/YacoinBaseChainProvider");
28
28
  Object.defineProperty(exports, "YacoinBaseChainProvider", { enumerable: true, get: function () { return YacoinBaseChainProvider_1.YacoinBaseChainProvider; } });
29
29
  var YacoinEsploraApiProvider_1 = require("./chain/esplora/YacoinEsploraApiProvider");
@@ -43,7 +43,9 @@ Object.defineProperty(exports, "YacoinSwapEsploraProvider", { enumerable: true,
43
43
  exports.YacoinTypes = __importStar(require("./types"));
44
44
  exports.YacoinUtils = __importStar(require("./utils"));
45
45
  var YacoinBaseWallet_1 = require("./wallet/YacoinBaseWallet");
46
+ Object.defineProperty(exports, "AddressSearchType", { enumerable: true, get: function () { return YacoinBaseWallet_1.AddressSearchType; } });
46
47
  Object.defineProperty(exports, "YacoinBaseWalletProvider", { enumerable: true, get: function () { return YacoinBaseWallet_1.YacoinBaseWalletProvider; } });
48
+ Object.defineProperty(exports, "NUMBER_ADDRESS_LIMIT", { enumerable: true, get: function () { return YacoinBaseWallet_1.NUMBER_ADDRESS_LIMIT; } });
47
49
  var YacoinHDWallet_1 = require("./wallet/YacoinHDWallet");
48
50
  Object.defineProperty(exports, "YacoinHDWalletProvider", { enumerable: true, get: function () { return YacoinHDWallet_1.YacoinHDWalletProvider; } });
49
51
  var YacoinNftProvider_1 = require("./nft/YacoinNftProvider");
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../lib/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,2EAA0E;AAAjE,kIAAA,uBAAuB,OAAA;AAChC,qFAAoF;AAA3E,oIAAA,wBAAwB,OAAA;AACjC,uFAAsF;AAA7E,sIAAA,yBAAyB,OAAA;AAClC,iGAAgG;AAAvF,gJAAA,8BAA8B,OAAA;AACvC,mEAAkE;AAAzD,4HAAA,oBAAoB,OAAA;AAC7B,uCAA4C;AAAnC,0GAAA,cAAc,OAAA;AACvB,wEAAuE;AAA9D,gIAAA,sBAAsB,OAAA;AAC/B,8EAA6E;AAApE,sIAAA,yBAAyB,OAAA;AAClC,uDAAuC;AACvC,uDAAuC;AACvC,8DAAqE;AAA5D,4HAAA,wBAAwB,OAAA;AACjC,0DAAiE;AAAxD,wHAAA,sBAAsB,OAAA;AAC/B,6DAA4D;AAAnD,sHAAA,iBAAiB,OAAA;AAC1B,kEAAiE;AAAxD,wHAAA,kBAAkB,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../lib/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,2EAA0E;AAAjE,kIAAA,uBAAuB,OAAA;AAChC,qFAAoF;AAA3E,oIAAA,wBAAwB,OAAA;AACjC,uFAAsF;AAA7E,sIAAA,yBAAyB,OAAA;AAClC,iGAAgG;AAAvF,gJAAA,8BAA8B,OAAA;AACvC,mEAAkE;AAAzD,4HAAA,oBAAoB,OAAA;AAC7B,uCAA4C;AAAnC,0GAAA,cAAc,OAAA;AACvB,wEAAuE;AAA9D,gIAAA,sBAAsB,OAAA;AAC/B,8EAA6E;AAApE,sIAAA,yBAAyB,OAAA;AAClC,uDAAuC;AACvC,uDAAuC;AACvC,8DAA8G;AAArG,qHAAA,iBAAiB,OAAA;AAAE,4HAAA,wBAAwB,OAAA;AAAE,wHAAA,oBAAoB,OAAA;AAC1E,0DAAiE;AAAxD,wHAAA,sBAAsB,OAAA;AAC/B,6DAA4D;AAAnD,sHAAA,iBAAiB,OAAA;AAC1B,kEAAiE;AAAxD,wHAAA,kBAAkB,OAAA"}
@@ -1,5 +1,5 @@
1
1
  /// <reference types="node" />
2
- import { Network, TokenScriptType } from '@yaswap/types';
2
+ import { Address, Network, TokenScriptType, TransactionRequest } from '@yaswap/types';
3
3
  import { ECPairInterface, Network as YacoinJsLibNetwork } from '@yaswap/yacoinjs-lib';
4
4
  export * as YacoinEsploraTypes from './chain/esplora/types';
5
5
  export * from './swap/types';
@@ -18,7 +18,17 @@ export interface YacoinWalletProviderOptions extends YacoinNodeWalletOptions {
18
18
  baseDerivationPath: string;
19
19
  }
20
20
  export interface YacoinHDWalletProviderOptions extends YacoinWalletProviderOptions {
21
- mnemonic: string;
21
+ mnemonic?: string;
22
+ extendedPublicKey?: RawExtendedPublicKey;
23
+ extendedPrivateKey?: RawExtendedPrivateKey;
24
+ }
25
+ export interface RawExtendedPublicKey {
26
+ publicKey: Buffer;
27
+ chainCode: Buffer;
28
+ }
29
+ export interface RawExtendedPrivateKey {
30
+ privateKey: Buffer;
31
+ chainCode: Buffer;
22
32
  }
23
33
  export interface OutputTarget {
24
34
  address?: string;
@@ -96,3 +106,36 @@ export interface P2SHInput {
96
106
  vout: any;
97
107
  outputScript: Buffer;
98
108
  }
109
+ export interface UsedUnusedAddressesResult {
110
+ usedAddresses: {
111
+ change: Address[];
112
+ external: Address[];
113
+ };
114
+ unusedAddress: {
115
+ change: Address | null;
116
+ external: Address | null;
117
+ };
118
+ }
119
+ /**
120
+ * Options for Yacoin sendTransaction and sendTransactionFixedInputs.
121
+ * Extends TransactionRequest with Yacoin-specific options.
122
+ */
123
+ export interface YacoinSendTransactionOptions extends TransactionRequest {
124
+ /**
125
+ * Pre-selected UTXOs to use as inputs. When set, only these UTXOs are used
126
+ * (e.g. for sendTransactionFixedInputs).
127
+ */
128
+ inputs?: UTXO[];
129
+ /**
130
+ * When true, use a change address (internal branch) for the change output;
131
+ * when false, use a receiving address (external branch). Default: false.
132
+ *
133
+ * NOTE: This only affects which branch is scanned for UTXOs and where
134
+ * the change output is sent; it does not alter token behavior.
135
+ */
136
+ change?: boolean;
137
+ /**
138
+ * Maximum number of addresses to scan for UTXOs. Default: 40.
139
+ */
140
+ addressLimit?: number;
141
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../lib/types.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAGA,4EAA4D;AAC5D,+CAA6B;AAsF7B,IAAY,WAEX;AAFD,WAAY,WAAW;IACnB,gCAAiB,CAAA;AACrB,CAAC,EAFW,WAAW,GAAX,mBAAW,KAAX,mBAAW,QAEtB;AAED,IAAY,QAEX;AAFD,WAAY,QAAQ;IAChB,yBAAa,CAAA;AACjB,CAAC,EAFW,QAAQ,GAAR,gBAAQ,KAAR,gBAAQ,QAEnB"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../lib/types.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAGA,4EAA4D;AAC5D,+CAA6B;AAkG7B,IAAY,WAEX;AAFD,WAAY,WAAW;IACnB,gCAAiB,CAAA;AACrB,CAAC,EAFW,WAAW,GAAX,mBAAW,KAAX,mBAAW,QAEtB;AAED,IAAY,QAEX;AAFD,WAAY,QAAQ;IAChB,yBAAa,CAAA;AACjB,CAAC,EAFW,QAAQ,GAAR,gBAAQ,KAAR,gBAAQ,QAEnB"}
@@ -1,11 +1,32 @@
1
1
  import { Chain, Wallet } from '@yaswap/client';
2
- import { Address, FeeType, Transaction, TransactionRequest } from '@yaswap/types';
3
- import { PsbtInputTarget } from '../types';
2
+ import { Address, FeeType, Transaction } from '@yaswap/types';
3
+ import { PsbtInputTarget, UsedUnusedAddressesResult, YacoinSendTransactionOptions } from '../types';
4
+ import { AddressSearchType } from './YacoinBaseWallet';
4
5
  export interface IYacoinWallet<T, S = any> extends Wallet<T, S> {
5
6
  getChainProvider(): Chain<T>;
6
- sendTransaction(txRequest: TransactionRequest): Promise<Transaction>;
7
+ sendTransaction(txRequest: YacoinSendTransactionOptions): Promise<Transaction>;
8
+ sendTransactionFixedInputs(options: YacoinSendTransactionOptions): Promise<Transaction>;
9
+ getTotalFeeFixedInputs(opts: YacoinSendTransactionOptions, max: boolean): Promise<number>;
7
10
  updateTransactionFee(tx: string | Transaction, newFee: FeeType): Promise<Transaction>;
8
11
  getWalletAddress(address: string): Promise<Address>;
9
12
  signPSBT(data: string, inputs: PsbtInputTarget[]): Promise<string>;
10
13
  signTx(transaction: string, hash: string, derivationPath: string, txfee: number): Promise<string>;
14
+ /**
15
+ * Get one unused receiving or change address.
16
+ *
17
+ * @param change When true, search change addresses; otherwise external/receiving.
18
+ * @param addressLimit Maximum number of address indexes to scan (per branch).
19
+ */
20
+ getUnusedAddress(change?: boolean, addressLimit?: number): Promise<Address | null>;
21
+ /**
22
+ * Get used addresses.
23
+ *
24
+ * @param addressType Which branch to scan: external, change or both. Defaults to both.
25
+ * @param addressLimit Maximum number of address indexes to scan (per branch).
26
+ */
27
+ getUsedAddresses(addressType?: AddressSearchType, addressLimit?: number): Promise<Address[]>;
28
+ /**
29
+ * Scan addresses and return both used and first unused addresses per branch.
30
+ */
31
+ getUsedUnusedAddresses(addressType: AddressSearchType, addressLimit?: number, gapLimit?: number, useCache?: boolean): Promise<UsedUnusedAddressesResult>;
11
32
  }
@@ -4,8 +4,9 @@ import { Address, AddressType, Asset, Transaction, TransactionRequest, CreateTok
4
4
  import { BIP32Interface } from 'bip32';
5
5
  import { payments } from '@yaswap/yacoinjs-lib';
6
6
  import { YacoinBaseChainProvider } from '../chain/YacoinBaseChainProvider';
7
- import { AddressType as YaAddressType, YacoinNetwork, YacoinWalletProviderOptions, Input, OutputTarget, P2SHInput, PsbtInputTarget, Transaction as YaTransaction, UTXO } from '../types';
7
+ import { AddressType as YaAddressType, YacoinNetwork, YacoinWalletProviderOptions, OutputTarget, P2SHInput, PsbtInputTarget, Transaction as YaTransaction, UsedUnusedAddressesResult, UTXO, YacoinSendTransactionOptions } from '../types';
8
8
  import { CoinSelectTarget } from '../utils';
9
+ export declare const NUMBER_ADDRESS_LIMIT = 40;
9
10
  export declare enum AddressSearchType {
10
11
  EXTERNAL = 0,
11
12
  CHANGE = 1,
@@ -19,10 +20,11 @@ export declare abstract class YacoinBaseWalletProvider<T extends YacoinBaseChain
19
20
  protected _network: YacoinNetwork;
20
21
  protected _addressType: YaAddressType;
21
22
  protected _derivationCache: DerivationCache;
23
+ private _usedUnusedAddressesCache;
22
24
  constructor(options: YacoinWalletProviderOptions, chainProvider?: Chain<T>);
23
25
  protected onChainProviderUpdate(chainProvider: Chain<T>): void;
24
26
  protected abstract baseDerivationNode(): Promise<BIP32Interface>;
25
- protected abstract buildTransaction(targets: OutputTarget[], feePerByte?: number, fixedInputs?: Input[]): Promise<{
27
+ protected abstract buildTransaction(targets: OutputTarget[], feePerByte?: number, fixedUtxos?: UTXO[], change?: boolean, addressLimit?: number): Promise<{
26
28
  hex: string;
27
29
  fee: number;
28
30
  }>;
@@ -34,29 +36,50 @@ export declare abstract class YacoinBaseWalletProvider<T extends YacoinBaseChain
34
36
  abstract signTx(transaction: string, hash: string, derivationPath: string, txfee: number): Promise<string>;
35
37
  abstract signBatchP2SHTransaction(inputs: P2SHInput[], addresses: string, tx: any, lockTime?: number, segwit?: boolean): Promise<Buffer[]>;
36
38
  getDerivationCache(): DerivationCache;
37
- getUnusedAddress(change?: boolean): Promise<Address>;
38
- getUsedAddresses(): Promise<Address[]>;
39
+ /**
40
+ * Get one unused receiving or change address.
41
+ *
42
+ * @param change When true, search change addresses; otherwise external/receiving.
43
+ * @param addressLimit Maximum number of address indexes to scan (per branch). Defaults to NUMBER_ADDRESS_LIMIT.
44
+ * @returns The first unused address found or null if none within the limit.
45
+ */
46
+ getUnusedAddress(change?: boolean, addressLimit?: number): Promise<Address>;
47
+ /**
48
+ * Get used addresses.
49
+ *
50
+ * @param addressType Which branch to scan: external, change or both. Defaults to both.
51
+ * @param addressLimit Maximum number of address indexes to scan (per branch). Defaults to NUMBER_ADDRESS_LIMIT.
52
+ * @returns List of used addresses for the requested branch(es).
53
+ */
54
+ getUsedAddresses(addressType?: AddressSearchType, addressLimit?: number): Promise<Address[]>;
39
55
  getAddresses(startingIndex?: number, numAddresses?: number, change?: boolean): Promise<Address[]>;
40
56
  createToken(options: CreateTokenTransaction): Promise<Transaction>;
41
- sendTransaction(options: TransactionRequest): Promise<Transaction<YaTransaction>>;
42
- sendBatchTransaction(transactions: TransactionRequest[]): Promise<Transaction<YaTransaction>[]>;
57
+ sendTransaction(options: TransactionRequest & {
58
+ change?: boolean;
59
+ addressLimit?: number;
60
+ inputs?: UTXO[];
61
+ }): Promise<Transaction<YaTransaction>>;
62
+ sendTransactionFixedInputs(options: TransactionRequest & {
63
+ change?: boolean;
64
+ addressLimit?: number;
65
+ inputs?: UTXO[];
66
+ }): Promise<Transaction<YaTransaction>>;
67
+ sendBatchTransaction(transactions: (TransactionRequest & {
68
+ change?: boolean;
69
+ addressLimit?: number;
70
+ })[]): Promise<Transaction<YaTransaction>[]>;
43
71
  sendSweepTransaction(externalChangeAddress: AddressType, _asset: Asset, feePerByte: number): Promise<Transaction<YaTransaction>>;
44
72
  updateTransactionFee(tx: Transaction<YaTransaction> | string, newFeePerByte: number): Promise<Transaction<YaTransaction>>;
45
- getTotalFees(transactions: TransactionRequest[], max: boolean): Promise<any>;
46
- protected _sendTransaction(transactions: OutputTarget[], feePerByte?: number): Promise<Transaction<YaTransaction>>;
47
- protected findAddress(addresses: string[], change?: boolean): Promise<Address>;
73
+ getTotalFees(transactions: YacoinSendTransactionOptions[], max: boolean): Promise<any>;
74
+ protected _sendTransaction(transactions: OutputTarget[], feePerByte?: number, change?: boolean, addressLimit?: number, fixedUtxos?: UTXO[]): Promise<Transaction<YaTransaction>>;
75
+ protected findAddress(addresses: string[], change?: boolean, addressLimit?: number): Promise<Address>;
48
76
  getWalletAddress(address: string): Promise<Address>;
49
77
  protected getDerivationPathAddress(path: string): Promise<Address>;
50
- protected _getUsedUnusedAddresses(addressType: AddressSearchType): Promise<{
51
- usedAddresses: Address[];
52
- unusedAddress: {
53
- change: Address;
54
- external: Address;
55
- };
56
- }>;
78
+ getUsedUnusedAddresses(addressType: AddressSearchType, addressLimit?: number, gapLimit?: number, useCache?: boolean): Promise<UsedUnusedAddressesResult>;
57
79
  protected withCachedUtxos(func: () => any): Promise<any>;
58
- protected getTotalFee(opts: TransactionRequest, max: boolean): Promise<number>;
59
- protected getInputsForAmount(_targets: OutputTarget[], feePerByte?: number, fixedInputs?: Input[], sweep?: boolean): Promise<{
80
+ getTotalFeeFixedInputs(opts: YacoinSendTransactionOptions, max: boolean): Promise<number>;
81
+ protected getTotalFee(opts: YacoinSendTransactionOptions, max: boolean): Promise<number>;
82
+ protected getInputsForAmount(_targets: OutputTarget[], feePerByte?: number, fixedUtxos?: UTXO[], sweep?: boolean, addressLimit?: number, change?: boolean): Promise<{
60
83
  inputs: UTXO[];
61
84
  coinChange: CoinSelectTarget;
62
85
  tokenChange: CoinSelectTarget;
@@ -12,7 +12,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
12
12
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.YacoinBaseWalletProvider = exports.AddressSearchType = void 0;
15
+ exports.YacoinBaseWalletProvider = exports.AddressSearchType = exports.NUMBER_ADDRESS_LIMIT = void 0;
16
16
  const client_1 = require("@yaswap/client");
17
17
  const errors_1 = require("@yaswap/errors");
18
18
  const types_1 = require("@yaswap/types");
@@ -21,7 +21,9 @@ const yacoinjs_lib_1 = require("@yaswap/yacoinjs-lib");
21
21
  const memoizee_1 = __importDefault(require("memoizee"));
22
22
  const types_2 = require("../types");
23
23
  const utils_2 = require("../utils");
24
- const NUMBER_ADDRESS_LIMIT = 1;
24
+ exports.NUMBER_ADDRESS_LIMIT = 40;
25
+ /** Cache TTL in milliseconds for getUsedUnusedAddresses results. */
26
+ const USED_UNUSED_ADDRESSES_CACHE_TTL_MS = 60 * 1000;
25
27
  var AddressSearchType;
26
28
  (function (AddressSearchType) {
27
29
  AddressSearchType[AddressSearchType["EXTERNAL"] = 0] = "EXTERNAL";
@@ -45,6 +47,7 @@ class YacoinBaseWalletProvider extends client_1.Wallet {
45
47
  throw new Error(`addressType must be one of ${addressTypes.join(',')}`);
46
48
  }
47
49
  super(chainProvider);
50
+ this._usedUnusedAddressesCache = {};
48
51
  this._baseDerivationPath = baseDerivationPath;
49
52
  this._network = chainProvider ? chainProvider.getNetwork() : options.network;
50
53
  this._addressType = addressType;
@@ -56,16 +59,38 @@ class YacoinBaseWalletProvider extends client_1.Wallet {
56
59
  getDerivationCache() {
57
60
  return this._derivationCache;
58
61
  }
59
- getUnusedAddress(change = false) {
62
+ /**
63
+ * Get one unused receiving or change address.
64
+ *
65
+ * @param change When true, search change addresses; otherwise external/receiving.
66
+ * @param addressLimit Maximum number of address indexes to scan (per branch). Defaults to NUMBER_ADDRESS_LIMIT.
67
+ * @returns The first unused address found or null if none within the limit.
68
+ */
69
+ getUnusedAddress(change = false, addressLimit = exports.NUMBER_ADDRESS_LIMIT) {
60
70
  return __awaiter(this, void 0, void 0, function* () {
61
71
  const addressType = change ? AddressSearchType.CHANGE : AddressSearchType.EXTERNAL;
62
72
  const key = change ? 'change' : 'external';
63
- return this._getUsedUnusedAddresses(addressType).then(({ unusedAddress }) => unusedAddress[key]);
73
+ const { unusedAddress } = yield this.getUsedUnusedAddresses(addressType, addressLimit);
74
+ return unusedAddress[key];
64
75
  });
65
76
  }
66
- getUsedAddresses() {
77
+ /**
78
+ * Get used addresses.
79
+ *
80
+ * @param addressType Which branch to scan: external, change or both. Defaults to both.
81
+ * @param addressLimit Maximum number of address indexes to scan (per branch). Defaults to NUMBER_ADDRESS_LIMIT.
82
+ * @returns List of used addresses for the requested branch(es).
83
+ */
84
+ getUsedAddresses(addressType = AddressSearchType.EXTERNAL_OR_CHANGE, addressLimit = exports.NUMBER_ADDRESS_LIMIT) {
67
85
  return __awaiter(this, void 0, void 0, function* () {
68
- return this._getUsedUnusedAddresses(AddressSearchType.EXTERNAL_OR_CHANGE).then(({ usedAddresses }) => usedAddresses);
86
+ const result = yield this.getUsedUnusedAddresses(addressType, addressLimit);
87
+ if (addressType === AddressSearchType.EXTERNAL) {
88
+ return result.usedAddresses.external;
89
+ }
90
+ if (addressType === AddressSearchType.CHANGE) {
91
+ return result.usedAddresses.change;
92
+ }
93
+ return [...result.usedAddresses.change, ...result.usedAddresses.external];
69
94
  });
70
95
  }
71
96
  getAddresses(startingIndex = 0, numAddresses = 1, change = false) {
@@ -92,13 +117,35 @@ class YacoinBaseWalletProvider extends client_1.Wallet {
92
117
  });
93
118
  }
94
119
  sendTransaction(options) {
120
+ var _a, _b;
95
121
  return __awaiter(this, void 0, void 0, function* () {
96
- return this._sendTransaction(this.sendOptionsToOutputs([options]), options.fee);
122
+ const change = (_a = options.change) !== null && _a !== void 0 ? _a : false;
123
+ const addressLimit = (_b = options.addressLimit) !== null && _b !== void 0 ? _b : exports.NUMBER_ADDRESS_LIMIT;
124
+ return this._sendTransaction(this.sendOptionsToOutputs([options]), options.fee, change, addressLimit, options.inputs);
125
+ });
126
+ }
127
+ sendTransactionFixedInputs(options) {
128
+ var _a, _b, _c;
129
+ return __awaiter(this, void 0, void 0, function* () {
130
+ const targets = this.sendOptionsToOutputs([options]);
131
+ const fixedUtxos = (_a = options.inputs) !== null && _a !== void 0 ? _a : [];
132
+ const change = (_b = options.change) !== null && _b !== void 0 ? _b : false;
133
+ const addressLimit = (_c = options.addressLimit) !== null && _c !== void 0 ? _c : exports.NUMBER_ADDRESS_LIMIT;
134
+ const { hex, fee } = yield this.buildTransaction(targets, options.fee, fixedUtxos, change, addressLimit);
135
+ const result = yield this.chainProvider.sendRawTransaction(`data=${hex}`);
136
+ if (result == 'There was an error. Check your console.') {
137
+ throw new Error("Cannot send transaction, there might some reasons:\n 1) It might be the fee is not enough, please try increasing the fee.\n 2) The wallet haven't updated latest balance info, please wait 10 seconds and try again.");
138
+ }
139
+ return (0, utils_2.normalizeTransactionObject)((0, utils_2.decodeRawTransaction)(hex, this._network), fee);
97
140
  });
98
141
  }
99
142
  sendBatchTransaction(transactions) {
143
+ var _a, _b;
100
144
  return __awaiter(this, void 0, void 0, function* () {
101
- return [yield this._sendTransaction(this.sendOptionsToOutputs(transactions))];
145
+ const first = transactions[0];
146
+ const change = (_a = first === null || first === void 0 ? void 0 : first.change) !== null && _a !== void 0 ? _a : false;
147
+ const addressLimit = (_b = first === null || first === void 0 ? void 0 : first.addressLimit) !== null && _b !== void 0 ? _b : exports.NUMBER_ADDRESS_LIMIT;
148
+ return [yield this._sendTransaction(this.sendOptionsToOutputs(transactions), undefined, change, addressLimit)];
102
149
  });
103
150
  }
104
151
  sendSweepTransaction(externalChangeAddress, _asset, feePerByte) {
@@ -112,7 +159,21 @@ class YacoinBaseWalletProvider extends client_1.Wallet {
112
159
  return __awaiter(this, void 0, void 0, function* () {
113
160
  const txHash = typeof tx === 'string' ? tx : tx.hash;
114
161
  const transaction = (yield this.chainProvider.getTransactionByHash(txHash))._raw;
115
- const fixedInputs = [transaction.vin[0]]; // TODO: should this pick more than 1 input? RBF doesn't mandate it
162
+ const fixedUtxos = [];
163
+ for (const vin of transaction.vin) {
164
+ const txHex = yield this.chainProvider.getProvider().getRawTransactionByHash(vin.txid);
165
+ const txDecoded = (0, utils_2.decodeRawTransaction)(txHex, this._network);
166
+ const value = new types_1.BigNumber(txDecoded.vout[vin.vout].value).times(1e6).toNumber();
167
+ const address = txDecoded.vout[vin.vout].scriptPubKey.addresses[0];
168
+ const walletAddress = yield this.getWalletAddress(address);
169
+ fixedUtxos.push({
170
+ txid: vin.txid,
171
+ vout: vin.vout,
172
+ value,
173
+ address,
174
+ derivationPath: walletAddress.derivationPath,
175
+ });
176
+ }
116
177
  const lookupAddresses = transaction.vout.map((vout) => vout.scriptPubKey.addresses[0]);
117
178
  const changeAddress = yield this.findAddress(lookupAddresses, true);
118
179
  const changeOutput = transaction.vout.find((vout) => vout.scriptPubKey.addresses[0] === changeAddress.address);
@@ -125,8 +186,11 @@ class YacoinBaseWalletProvider extends client_1.Wallet {
125
186
  address: output.scriptPubKey.addresses[0],
126
187
  value: new types_1.BigNumber(output.value).times(1e6).toNumber(),
127
188
  }));
128
- const { hex, fee } = yield this.buildTransaction(transactions, newFeePerByte, fixedInputs);
129
- yield this.chainProvider.sendRawTransaction(`data=${hex}`);
189
+ const { hex, fee } = yield this.buildTransaction(transactions, newFeePerByte, fixedUtxos, true, exports.NUMBER_ADDRESS_LIMIT);
190
+ const result = yield this.chainProvider.sendRawTransaction(`data=${hex}`);
191
+ if (result == 'There was an error. Check your console.') {
192
+ throw new Error("Cannot send transaction, there might some reasons:\n 1) It might be the fee is not enough, please try increasing the fee.\n 2) The wallet haven't updated latest balance info, please wait 10 seconds and try again.");
193
+ }
130
194
  return (0, utils_2.normalizeTransactionObject)((0, utils_2.decodeRawTransaction)(hex, this._network), fee);
131
195
  });
132
196
  }
@@ -143,9 +207,9 @@ class YacoinBaseWalletProvider extends client_1.Wallet {
143
207
  return fees;
144
208
  });
145
209
  }
146
- _sendTransaction(transactions, feePerByte) {
210
+ _sendTransaction(transactions, feePerByte, change = false, addressLimit = exports.NUMBER_ADDRESS_LIMIT, fixedUtxos = []) {
147
211
  return __awaiter(this, void 0, void 0, function* () {
148
- const { hex, fee } = yield this.buildTransaction(transactions, feePerByte);
212
+ const { hex, fee } = yield this.buildTransaction(transactions, feePerByte, fixedUtxos, change, addressLimit);
149
213
  const result = yield this.chainProvider.sendRawTransaction(`data=${hex}`);
150
214
  if (result == 'There was an error. Check your console.') {
151
215
  throw new Error("Cannot send transaction, there might some reasons:\n 1) It might be the fee is not enough, please try increasing the fee.\n 2) The wallet haven't updated latest balance info, please wait 10 seconds and try again.");
@@ -153,11 +217,11 @@ class YacoinBaseWalletProvider extends client_1.Wallet {
153
217
  return (0, utils_2.normalizeTransactionObject)((0, utils_2.decodeRawTransaction)(hex, this._network), fee);
154
218
  });
155
219
  }
156
- findAddress(addresses, change = false) {
220
+ findAddress(addresses, change = false, addressLimit = exports.NUMBER_ADDRESS_LIMIT) {
157
221
  return __awaiter(this, void 0, void 0, function* () {
158
222
  // A maximum number of addresses to lookup after which it is deemed that the wallet does not contain this address
159
- const maxAddresses = NUMBER_ADDRESS_LIMIT;
160
- const addressesPerCall = 50;
223
+ const maxAddresses = addressLimit;
224
+ const addressesPerCall = exports.NUMBER_ADDRESS_LIMIT;
161
225
  let index = 0;
162
226
  while (index < maxAddresses) {
163
227
  const walletAddresses = yield this.getAddresses(index, addressesPerCall, change);
@@ -200,26 +264,87 @@ class YacoinBaseWalletProvider extends client_1.Wallet {
200
264
  return addressObject;
201
265
  });
202
266
  }
203
- _getUsedUnusedAddresses(addressType) {
267
+ getUsedUnusedAddresses(addressType, addressLimit = exports.NUMBER_ADDRESS_LIMIT, gapLimit = 1, useCache = true) {
204
268
  return __awaiter(this, void 0, void 0, function* () {
205
- const usedAddresses = [];
206
- const unusedAddressMap = { change: null, external: null };
207
- if (addressType === AddressSearchType.EXTERNAL_OR_CHANGE || addressType === AddressSearchType.EXTERNAL) {
208
- const externalAddresses = yield this.getAddresses(0, 1, false);
209
- const externalAddress = externalAddresses[0];
210
- usedAddresses.push(externalAddress);
211
- unusedAddressMap.external = externalAddress;
269
+ const cacheKey = `${addressType}_${addressLimit}_${gapLimit}`;
270
+ if (useCache) {
271
+ const cached = this._usedUnusedAddressesCache[cacheKey];
272
+ if (cached && Date.now() - cached.timestamp < USED_UNUSED_ADDRESSES_CACHE_TTL_MS) {
273
+ return cached.result;
274
+ }
212
275
  }
213
- if (addressType === AddressSearchType.EXTERNAL_OR_CHANGE || addressType === AddressSearchType.CHANGE) {
214
- const changeAddresses = yield this.getAddresses(0, 1, true);
215
- const changeAddress = changeAddresses[0];
216
- usedAddresses.push(changeAddress);
217
- unusedAddressMap.change = changeAddress;
276
+ const usedAddresses = {
277
+ change: [],
278
+ external: [],
279
+ };
280
+ const unusedAddressMap = {
281
+ change: null,
282
+ external: null,
283
+ };
284
+ const consecutiveUnusedCount = { change: 0, external: 0 };
285
+ let numAddressAlreadyGet = 0;
286
+ const numAddressPerCall = 100;
287
+ let addressIndex = 0;
288
+ /* eslint-disable no-unmodified-loop-condition */
289
+ while (((addressType === AddressSearchType.EXTERNAL_OR_CHANGE &&
290
+ (consecutiveUnusedCount.change < gapLimit || consecutiveUnusedCount.external < gapLimit)) ||
291
+ (addressType === AddressSearchType.EXTERNAL && consecutiveUnusedCount.external < gapLimit) ||
292
+ (addressType === AddressSearchType.CHANGE && consecutiveUnusedCount.change < gapLimit)) &&
293
+ numAddressAlreadyGet < addressLimit) {
294
+ /* eslint-enable no-unmodified-loop-condition */
295
+ let addrList = [];
296
+ let changeAddresses = [];
297
+ let externalAddresses = [];
298
+ let maxBatchSize = 0;
299
+ if ((addressType === AddressSearchType.EXTERNAL_OR_CHANGE || addressType === AddressSearchType.CHANGE) &&
300
+ consecutiveUnusedCount.change < gapLimit &&
301
+ numAddressAlreadyGet < addressLimit) {
302
+ const remainingLimit = addressLimit - numAddressAlreadyGet;
303
+ const batchSize = Math.min(numAddressPerCall, remainingLimit);
304
+ changeAddresses = yield this.getAddresses(addressIndex, batchSize, true);
305
+ addrList = addrList.concat(changeAddresses);
306
+ maxBatchSize = Math.max(maxBatchSize, batchSize);
307
+ }
308
+ if ((addressType === AddressSearchType.EXTERNAL_OR_CHANGE || addressType === AddressSearchType.EXTERNAL) &&
309
+ consecutiveUnusedCount.external < gapLimit &&
310
+ numAddressAlreadyGet < addressLimit) {
311
+ const remainingLimit = addressLimit - numAddressAlreadyGet;
312
+ const batchSize = Math.min(numAddressPerCall, remainingLimit);
313
+ externalAddresses = yield this.getAddresses(addressIndex, batchSize, false);
314
+ addrList = addrList.concat(externalAddresses);
315
+ maxBatchSize = Math.max(maxBatchSize, batchSize);
316
+ }
317
+ if (addrList.length === 0) {
318
+ break;
319
+ }
320
+ const transactionCounts = yield this.chainProvider.getProvider().getAddressTransactionCounts(addrList);
321
+ for (const address of addrList) {
322
+ const isUsed = transactionCounts[address.toString()] > 0;
323
+ const isChangeAddress = changeAddresses.some((a) => address.toString() === a.toString());
324
+ const key = isChangeAddress ? 'change' : 'external';
325
+ if (isUsed) {
326
+ usedAddresses[key].push(address);
327
+ consecutiveUnusedCount[key] = 0;
328
+ unusedAddressMap[key] = null;
329
+ }
330
+ else {
331
+ consecutiveUnusedCount[key]++;
332
+ if (!unusedAddressMap[key]) {
333
+ unusedAddressMap[key] = address;
334
+ }
335
+ }
336
+ }
337
+ numAddressAlreadyGet += maxBatchSize;
338
+ addressIndex += maxBatchSize;
218
339
  }
219
- return {
340
+ const result = {
220
341
  usedAddresses,
221
342
  unusedAddress: unusedAddressMap,
222
343
  };
344
+ if (useCache) {
345
+ this._usedUnusedAddressesCache[cacheKey] = { result, timestamp: Date.now() };
346
+ }
347
+ return result;
223
348
  });
224
349
  }
225
350
  withCachedUtxos(func) {
@@ -238,40 +363,47 @@ class YacoinBaseWalletProvider extends client_1.Wallet {
238
363
  return result;
239
364
  });
240
365
  }
366
+ getTotalFeeFixedInputs(opts, max) {
367
+ var _a, _b, _c;
368
+ return __awaiter(this, void 0, void 0, function* () {
369
+ const targets = this.sendOptionsToOutputs([opts]);
370
+ const addressLimit = (_a = opts.addressLimit) !== null && _a !== void 0 ? _a : exports.NUMBER_ADDRESS_LIMIT;
371
+ const useChangeUtxos = (_b = opts.change) !== null && _b !== void 0 ? _b : false;
372
+ const { fee } = yield this.getInputsForAmount(targets.filter((t) => !t.value), opts.fee, (_c = opts.inputs) !== null && _c !== void 0 ? _c : [], max, addressLimit, useChangeUtxos);
373
+ return fee;
374
+ });
375
+ }
241
376
  getTotalFee(opts, max) {
377
+ var _a, _b, _c, _d;
242
378
  return __awaiter(this, void 0, void 0, function* () {
243
379
  const targets = this.sendOptionsToOutputs([opts]);
244
380
  if (!max) {
245
- // const { fee } = await this.getInputsForAmount(targets, opts.fee as number);
246
- const { fee } = yield this.getInputsForAmount(targets, opts.fee);
381
+ const { fee } = yield this.getInputsForAmount(targets, opts.fee, [], false, (_a = opts.addressLimit) !== null && _a !== void 0 ? _a : exports.NUMBER_ADDRESS_LIMIT, (_b = opts.change) !== null && _b !== void 0 ? _b : false);
247
382
  return fee;
248
383
  }
249
384
  else {
250
- const { fee } = yield this.getInputsForAmount(targets.filter((t) => !t.value), opts.fee, [], true);
385
+ const { fee } = yield this.getInputsForAmount(targets.filter((t) => !t.value), opts.fee, [], true, (_c = opts.addressLimit) !== null && _c !== void 0 ? _c : exports.NUMBER_ADDRESS_LIMIT, (_d = opts.change) !== null && _d !== void 0 ? _d : false);
251
386
  return fee;
252
387
  }
253
388
  });
254
389
  }
255
- getInputsForAmount(_targets, feePerByte, fixedInputs = [], sweep = false) {
390
+ getInputsForAmount(_targets, feePerByte, fixedUtxos = [], sweep = false, addressLimit = exports.NUMBER_ADDRESS_LIMIT, change = false) {
256
391
  return __awaiter(this, void 0, void 0, function* () {
257
392
  const tokenTransferOutput = _targets.find((target) => target.tokenName !== undefined && target.tokenScriptType === types_1.TokenScriptType.transfer);
258
393
  const feePerBytePromise = this.chainProvider.getProvider().getFeePerByte();
259
394
  let utxos = [];
260
395
  let tokenUtxos = [];
261
- const addresses = yield this.getUsedAddresses();
262
- const fixedUtxos = [];
263
- if (fixedInputs.length > 0) {
264
- for (const input of fixedInputs) {
265
- const txHex = yield this.chainProvider.getProvider().getRawTransactionByHash(input.txid);
266
- const tx = (0, utils_2.decodeRawTransaction)(txHex, this._network);
267
- const value = new types_1.BigNumber(tx.vout[input.vout].value).times(1e6).toNumber();
268
- const address = tx.vout[input.vout].scriptPubKey.addresses[0];
269
- const walletAddress = yield this.getWalletAddress(address);
270
- const utxo = Object.assign(Object.assign({}, input), { value, address, derivationPath: walletAddress.derivationPath });
271
- fixedUtxos.push(utxo);
396
+ const addressType = change ? AddressSearchType.EXTERNAL_OR_CHANGE : AddressSearchType.EXTERNAL;
397
+ const addresses = yield this.getUsedAddresses(addressType, addressLimit);
398
+ const enrichedFixedUtxos = yield Promise.all(fixedUtxos.map((utxo) => __awaiter(this, void 0, void 0, function* () {
399
+ let derivationPath = utxo.derivationPath;
400
+ if (derivationPath === undefined) {
401
+ const walletAddress = yield this.getWalletAddress(utxo.address);
402
+ derivationPath = walletAddress.derivationPath;
272
403
  }
273
- }
274
- if (!sweep || fixedUtxos.length === 0) {
404
+ return Object.assign(Object.assign({}, utxo), { derivationPath });
405
+ })));
406
+ if (!sweep || enrichedFixedUtxos.length === 0) {
275
407
  const _utxos = yield this.chainProvider.getProvider().getUnspentTransactions(addresses);
276
408
  utxos.push(..._utxos.map((utxo) => {
277
409
  const addr = addresses.find((a) => a.address === utxo.address);
@@ -279,7 +411,7 @@ class YacoinBaseWalletProvider extends client_1.Wallet {
279
411
  }));
280
412
  }
281
413
  else {
282
- utxos = fixedUtxos;
414
+ utxos = enrichedFixedUtxos;
283
415
  }
284
416
  if (tokenTransferOutput) {
285
417
  const isNFTOutput = tokenTransferOutput.tokenName.indexOf('#') !== -1;