@ledgerhq/live-common 21.16.1 → 21.17.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.
Files changed (111) hide show
  1. package/lib/__tests__/families/bitcoin/wallet-btc/utils.test.js +95 -7
  2. package/lib/__tests__/families/bitcoin/wallet-btc/utils.test.js.map +1 -1
  3. package/lib/__tests__/families/bitcoin/wallet-btc/wallet.estimateMaxSpendable.test.js +1 -1
  4. package/lib/__tests__/families/bitcoin/wallet-btc/wallet.estimateMaxSpendable.test.js.map +1 -1
  5. package/lib/__tests__/families/bitcoin/wallet-btc/wallet.integration.test.js +2 -2
  6. package/lib/__tests__/families/bitcoin/wallet-btc/wallet.integration.test.js.map +1 -1
  7. package/lib/__tests__/families/bitcoin/wallet-btc/xpub.pickingStrategies.integration.test.js +20 -10
  8. package/lib/__tests__/families/bitcoin/wallet-btc/xpub.pickingStrategies.integration.test.js.map +1 -1
  9. package/lib/__tests__/families/bitcoin/wallet-btc/xpub.txs.NP2WPKH.integration.test.js +1 -1
  10. package/lib/__tests__/families/bitcoin/wallet-btc/xpub.txs.NP2WPKH.integration.test.js.map +1 -1
  11. package/lib/__tests__/families/bitcoin/wallet-btc/xpub.txs.P2WPKH.integration.test.js +1 -1
  12. package/lib/__tests__/families/bitcoin/wallet-btc/xpub.txs.P2WPKH.integration.test.js.map +1 -1
  13. package/lib/__tests__/families/bitcoin/wallet-btc/xpub.txs.integration.test.js +2 -2
  14. package/lib/__tests__/families/bitcoin/wallet-btc/xpub.txs.integration.test.js.map +1 -1
  15. package/lib/apps/polyfill.d.ts.map +1 -1
  16. package/lib/apps/polyfill.js +3 -0
  17. package/lib/apps/polyfill.js.map +1 -1
  18. package/lib/currencies/support.d.ts.map +1 -1
  19. package/lib/currencies/support.js.map +1 -1
  20. package/lib/families/bitcoin/bech32m.d.ts +22 -0
  21. package/lib/families/bitcoin/bech32m.d.ts.map +1 -0
  22. package/lib/families/bitcoin/bech32m.js +166 -0
  23. package/lib/families/bitcoin/bech32m.js.map +1 -0
  24. package/lib/families/bitcoin/js-buildTransaction.d.ts.map +1 -1
  25. package/lib/families/bitcoin/js-buildTransaction.js +1 -1
  26. package/lib/families/bitcoin/js-buildTransaction.js.map +1 -1
  27. package/lib/families/bitcoin/js-estimateMaxSpendable.d.ts.map +1 -1
  28. package/lib/families/bitcoin/js-estimateMaxSpendable.js +1 -1
  29. package/lib/families/bitcoin/js-estimateMaxSpendable.js.map +1 -1
  30. package/lib/families/bitcoin/js-synchronisation.d.ts.map +1 -1
  31. package/lib/families/bitcoin/js-synchronisation.js +3 -2
  32. package/lib/families/bitcoin/js-synchronisation.js.map +1 -1
  33. package/lib/families/bitcoin/wallet-btc/crypto/bitcoin.d.ts.map +1 -1
  34. package/lib/families/bitcoin/wallet-btc/crypto/bitcoin.js +11 -11
  35. package/lib/families/bitcoin/wallet-btc/crypto/bitcoin.js.map +1 -1
  36. package/lib/families/bitcoin/wallet-btc/crypto/bitcoincash.d.ts.map +1 -1
  37. package/lib/families/bitcoin/wallet-btc/crypto/bitcoincash.js +2 -1
  38. package/lib/families/bitcoin/wallet-btc/crypto/bitcoincash.js.map +1 -1
  39. package/lib/families/bitcoin/wallet-btc/crypto/digibyte.js +21 -2
  40. package/lib/families/bitcoin/wallet-btc/crypto/digibyte.js.map +1 -1
  41. package/lib/families/bitcoin/wallet-btc/crypto/litecoin.js +2 -2
  42. package/lib/families/bitcoin/wallet-btc/crypto/litecoin.js.map +1 -1
  43. package/lib/families/bitcoin/wallet-btc/crypto/zec.d.ts.map +1 -1
  44. package/lib/families/bitcoin/wallet-btc/crypto/zec.js +3 -2
  45. package/lib/families/bitcoin/wallet-btc/crypto/zec.js.map +1 -1
  46. package/lib/families/bitcoin/wallet-btc/crypto/zen.d.ts +2 -1
  47. package/lib/families/bitcoin/wallet-btc/crypto/zen.d.ts.map +1 -1
  48. package/lib/families/bitcoin/wallet-btc/crypto/zen.js +7 -4
  49. package/lib/families/bitcoin/wallet-btc/crypto/zen.js.map +1 -1
  50. package/lib/families/bitcoin/wallet-btc/pickingstrategies/CoinSelect.d.ts +2 -1
  51. package/lib/families/bitcoin/wallet-btc/pickingstrategies/CoinSelect.d.ts.map +1 -1
  52. package/lib/families/bitcoin/wallet-btc/pickingstrategies/CoinSelect.js +16 -12
  53. package/lib/families/bitcoin/wallet-btc/pickingstrategies/CoinSelect.js.map +1 -1
  54. package/lib/families/bitcoin/wallet-btc/pickingstrategies/DeepFirst.d.ts +2 -1
  55. package/lib/families/bitcoin/wallet-btc/pickingstrategies/DeepFirst.d.ts.map +1 -1
  56. package/lib/families/bitcoin/wallet-btc/pickingstrategies/DeepFirst.js +12 -7
  57. package/lib/families/bitcoin/wallet-btc/pickingstrategies/DeepFirst.js.map +1 -1
  58. package/lib/families/bitcoin/wallet-btc/pickingstrategies/Merge.d.ts +2 -1
  59. package/lib/families/bitcoin/wallet-btc/pickingstrategies/Merge.d.ts.map +1 -1
  60. package/lib/families/bitcoin/wallet-btc/pickingstrategies/Merge.js +12 -7
  61. package/lib/families/bitcoin/wallet-btc/pickingstrategies/Merge.js.map +1 -1
  62. package/lib/families/bitcoin/wallet-btc/pickingstrategies/types.d.ts +2 -1
  63. package/lib/families/bitcoin/wallet-btc/pickingstrategies/types.d.ts.map +1 -1
  64. package/lib/families/bitcoin/wallet-btc/pickingstrategies/types.js.map +1 -1
  65. package/lib/families/bitcoin/wallet-btc/utils.d.ts +38 -2
  66. package/lib/families/bitcoin/wallet-btc/utils.d.ts.map +1 -1
  67. package/lib/families/bitcoin/wallet-btc/utils.js +103 -35
  68. package/lib/families/bitcoin/wallet-btc/utils.js.map +1 -1
  69. package/lib/families/bitcoin/wallet-btc/wallet.d.ts +1 -1
  70. package/lib/families/bitcoin/wallet-btc/wallet.d.ts.map +1 -1
  71. package/lib/families/bitcoin/wallet-btc/wallet.js +9 -8
  72. package/lib/families/bitcoin/wallet-btc/wallet.js.map +1 -1
  73. package/lib/families/bitcoin/wallet-btc/xpub.js +2 -2
  74. package/lib/families/bitcoin/wallet-btc/xpub.js.map +1 -1
  75. package/lib/families/elrond/logic.js +22 -3
  76. package/lib/families/elrond/logic.js.map +1 -1
  77. package/lib/operation.js +1 -1
  78. package/lib/operation.js.map +1 -1
  79. package/package.json +16 -16
  80. package/src/.DS_Store +0 -0
  81. package/src/__tests__/__snapshots__/all.libcore.ts.snap +1188 -630
  82. package/src/__tests__/families/bitcoin/wallet-btc/utils.test.ts +109 -2
  83. package/src/__tests__/families/bitcoin/wallet-btc/wallet.estimateMaxSpendable.test.ts +3 -2
  84. package/src/__tests__/families/bitcoin/wallet-btc/wallet.integration.test.ts +2 -2
  85. package/src/__tests__/families/bitcoin/wallet-btc/xpub.pickingStrategies.integration.test.ts +35 -31
  86. package/src/__tests__/families/bitcoin/wallet-btc/xpub.txs.NP2WPKH.integration.test.ts +3 -2
  87. package/src/__tests__/families/bitcoin/wallet-btc/xpub.txs.P2WPKH.integration.test.ts +3 -2
  88. package/src/__tests__/families/bitcoin/wallet-btc/xpub.txs.integration.test.ts +6 -4
  89. package/src/apps/polyfill.ts +3 -0
  90. package/src/currencies/.DS_Store +0 -0
  91. package/src/currencies/__snapshots__/sortByMarketcap.test.ts.snap +68 -1325
  92. package/src/currencies/support.ts +1 -0
  93. package/src/families/bitcoin/bech32m.ts +197 -0
  94. package/src/families/bitcoin/js-buildTransaction.ts +2 -1
  95. package/src/families/bitcoin/js-estimateMaxSpendable.ts +2 -1
  96. package/src/families/bitcoin/js-synchronisation.ts +4 -2
  97. package/src/families/bitcoin/wallet-btc/crypto/bitcoin.ts +5 -4
  98. package/src/families/bitcoin/wallet-btc/crypto/bitcoincash.ts +2 -1
  99. package/src/families/bitcoin/wallet-btc/crypto/digibyte.ts +1 -1
  100. package/src/families/bitcoin/wallet-btc/crypto/litecoin.ts +1 -1
  101. package/src/families/bitcoin/wallet-btc/crypto/zec.ts +3 -2
  102. package/src/families/bitcoin/wallet-btc/crypto/zen.ts +10 -4
  103. package/src/families/bitcoin/wallet-btc/pickingstrategies/CoinSelect.ts +34 -22
  104. package/src/families/bitcoin/wallet-btc/pickingstrategies/DeepFirst.ts +30 -9
  105. package/src/families/bitcoin/wallet-btc/pickingstrategies/Merge.ts +30 -9
  106. package/src/families/bitcoin/wallet-btc/pickingstrategies/types.ts +3 -3
  107. package/src/families/bitcoin/wallet-btc/utils.ts +113 -41
  108. package/src/families/bitcoin/wallet-btc/wallet.ts +10 -8
  109. package/src/families/bitcoin/wallet-btc/xpub.ts +5 -5
  110. package/src/families/elrond/logic.ts +1 -1
  111. package/src/operation.ts +1 -1
@@ -9,6 +9,7 @@ import {
9
9
  hasCryptoCurrencyId,
10
10
  } from "@ledgerhq/cryptoassets";
11
11
  import { getEnv } from "../env";
12
+
12
13
  // set by user side effect to precise which currencies are considered supported (typically by live)
13
14
  let userSupportedCurrencies: CryptoCurrency[] = [];
14
15
  let userSupportedFiats: FiatCurrency[] = [];
@@ -0,0 +1,197 @@
1
+ // Ported from https://github.com/bitcoinjs/bech32/tree/605655d6b37a782345549cd1388db688a96ad56f
2
+ // until we can use bech32 2.0.0
3
+ // We can't directly use bech32 2.0.0 because many of our dependencies are still using bech32 ^1.1.3
4
+ // which conflict on mobile (root cause of https://ledgerhq.atlassian.net/browse/LL-7930)
5
+ // FIXME Remove that file and use bech32 2.0.0 when all dependencies also uses it.
6
+
7
+ const ENCODING_CONST = 0x2bc830a3;
8
+ const ALPHABET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
9
+ const ALPHABET_MAP: { [key: string]: number } = {};
10
+ for (let z = 0; z < ALPHABET.length; z++) {
11
+ const x = ALPHABET.charAt(z);
12
+ ALPHABET_MAP[x] = z;
13
+ }
14
+
15
+ function prefixChk(prefix) {
16
+ let chk = 1;
17
+ for (let i = 0; i < prefix.length; ++i) {
18
+ const c = prefix.charCodeAt(i);
19
+ if (c < 33 || c > 126) return "Invalid prefix (" + prefix + ")";
20
+ chk = polymodStep(chk) ^ (c >> 5);
21
+ }
22
+ chk = polymodStep(chk);
23
+ for (let i = 0; i < prefix.length; ++i) {
24
+ const v = prefix.charCodeAt(i);
25
+ chk = polymodStep(chk) ^ (v & 0x1f);
26
+ }
27
+ return chk;
28
+ }
29
+
30
+ function polymodStep(pre: number): number {
31
+ const b = pre >> 25;
32
+ return (
33
+ ((pre & 0x1ffffff) << 5) ^
34
+ (-((b >> 0) & 1) & 0x3b6a57b2) ^
35
+ (-((b >> 1) & 1) & 0x26508e6d) ^
36
+ (-((b >> 2) & 1) & 0x1ea119fa) ^
37
+ (-((b >> 3) & 1) & 0x3d4233dd) ^
38
+ (-((b >> 4) & 1) & 0x2a1462b3)
39
+ );
40
+ }
41
+
42
+ function encode(
43
+ prefix: string,
44
+ words: ArrayLike<number>,
45
+ LIMIT?: number
46
+ ): string {
47
+ LIMIT = LIMIT || 90;
48
+ if (prefix.length + 7 + words.length > LIMIT)
49
+ throw new TypeError("Exceeds length limit");
50
+
51
+ prefix = prefix.toLowerCase();
52
+
53
+ // determine chk mod
54
+ let chk = prefixChk(prefix);
55
+ if (typeof chk === "string") throw new Error(chk);
56
+
57
+ let result = prefix + "1";
58
+ for (let i = 0; i < words.length; ++i) {
59
+ const x = words[i];
60
+ if (x >> 5 !== 0) throw new Error("Non 5-bit word");
61
+
62
+ chk = polymodStep(chk) ^ x;
63
+ result += ALPHABET.charAt(x);
64
+ }
65
+
66
+ for (let i = 0; i < 6; ++i) {
67
+ chk = polymodStep(chk);
68
+ }
69
+ chk ^= ENCODING_CONST;
70
+
71
+ for (let i = 0; i < 6; ++i) {
72
+ const v = (chk >> ((5 - i) * 5)) & 0x1f;
73
+ result += ALPHABET.charAt(v);
74
+ }
75
+
76
+ return result;
77
+ }
78
+
79
+ function __decode(str: string, LIMIT?: number) {
80
+ LIMIT = LIMIT || 90;
81
+ if (str.length < 8) return str + " too short";
82
+ if (str.length > LIMIT) return "Exceeds length limit";
83
+
84
+ // don't allow mixed case
85
+ const lowered = str.toLowerCase();
86
+ const uppered = str.toUpperCase();
87
+ if (str !== lowered && str !== uppered) return "Mixed-case string " + str;
88
+ str = lowered;
89
+
90
+ const split = str.lastIndexOf("1");
91
+ if (split === -1) return "No separator character for " + str;
92
+ if (split === 0) return "Missing prefix for " + str;
93
+
94
+ const prefix = str.slice(0, split);
95
+ const wordChars = str.slice(split + 1);
96
+ if (wordChars.length < 6) return "Data too short";
97
+
98
+ let chk = prefixChk(prefix);
99
+ if (typeof chk === "string") return chk;
100
+
101
+ const words: number[] = [];
102
+ for (let i = 0; i < wordChars.length; ++i) {
103
+ const c = wordChars.charAt(i);
104
+ const v = ALPHABET_MAP[c];
105
+ if (v === undefined) return "Unknown character " + c;
106
+ chk = polymodStep(chk) ^ v;
107
+
108
+ // not in the checksum?
109
+ if (i + 6 >= wordChars.length) continue;
110
+ words.push(v);
111
+ }
112
+
113
+ if (chk !== ENCODING_CONST) return "Invalid checksum for " + str;
114
+ return { prefix, words };
115
+ }
116
+
117
+ function decodeUnsafe(str: string, LIMIT?: number) {
118
+ const res = __decode(str, LIMIT);
119
+ if (typeof res === "object") return res;
120
+ }
121
+
122
+ function decode(str: string, LIMIT?: number) {
123
+ const res = __decode(str, LIMIT);
124
+ if (typeof res === "object") return res;
125
+
126
+ throw new Error(res);
127
+ }
128
+
129
+ function convert(
130
+ data: ArrayLike<number>,
131
+ inBits: number,
132
+ outBits: number,
133
+ pad: true
134
+ ): number[];
135
+ function convert(
136
+ data: ArrayLike<number>,
137
+ inBits: number,
138
+ outBits: number,
139
+ pad: false
140
+ ): number[] | string;
141
+ function convert(
142
+ data: ArrayLike<number>,
143
+ inBits: number,
144
+ outBits: number,
145
+ pad: boolean
146
+ ): number[] | string {
147
+ let value = 0;
148
+ let bits = 0;
149
+ const maxV = (1 << outBits) - 1;
150
+
151
+ const result: number[] = [];
152
+ for (let i = 0; i < data.length; ++i) {
153
+ value = (value << inBits) | data[i];
154
+ bits += inBits;
155
+
156
+ while (bits >= outBits) {
157
+ bits -= outBits;
158
+ result.push((value >> bits) & maxV);
159
+ }
160
+ }
161
+
162
+ if (pad) {
163
+ if (bits > 0) {
164
+ result.push((value << (outBits - bits)) & maxV);
165
+ }
166
+ } else {
167
+ if (bits >= inBits) return "Excess padding";
168
+ if ((value << (outBits - bits)) & maxV) return "Non-zero padding";
169
+ }
170
+
171
+ return result;
172
+ }
173
+
174
+ function toWords(bytes: ArrayLike<number>): number[] {
175
+ return convert(bytes, 8, 5, true);
176
+ }
177
+
178
+ function fromWordsUnsafe(words: ArrayLike<number>): number[] | undefined {
179
+ const res = convert(words, 5, 8, false);
180
+ if (Array.isArray(res)) return res;
181
+ }
182
+
183
+ function fromWords(words: ArrayLike<number>): number[] {
184
+ const res = convert(words, 5, 8, false);
185
+ if (Array.isArray(res)) return res;
186
+
187
+ throw new Error(res);
188
+ }
189
+
190
+ export const bech32m = {
191
+ decodeUnsafe,
192
+ decode,
193
+ encode,
194
+ toWords,
195
+ fromWordsUnsafe,
196
+ fromWords,
197
+ };
@@ -44,7 +44,8 @@ export const buildTransaction = async (
44
44
  walletAccount,
45
45
  transaction.feePerByte.toNumber(), //!\ wallet-btc handles fees as JS number
46
46
  transaction.utxoStrategy.excludeUTXOs,
47
- transaction.utxoStrategy.pickUnconfirmedRBF
47
+ transaction.utxoStrategy.pickUnconfirmedRBF,
48
+ [transaction.recipient]
48
49
  );
49
50
  log("btcwallet", "building transaction", transaction);
50
51
  const txInfo = await wallet.buildAccountTx({
@@ -30,7 +30,8 @@ const estimateMaxSpendable = async ({
30
30
  walletAccount,
31
31
  feePerByte.toNumber(), //!\ wallet-btc handles fees as JS number
32
32
  transaction?.utxoStrategy?.excludeUTXOs || [],
33
- transaction?.utxoStrategy?.pickUnconfirmedRBF || false
33
+ transaction?.utxoStrategy?.pickUnconfirmedRBF || false,
34
+ transaction ? [transaction.recipient] : []
34
35
  );
35
36
  return maxSpendable.lt(0) ? new BigNumber(0) : maxSpendable;
36
37
  };
@@ -139,8 +139,10 @@ const mapTxToOperations = (
139
139
  if (output.address) {
140
140
  if (!accountAddresses.includes(output.address)) {
141
141
  // The output doesn't belong to this account
142
- if (output.output_index < changeOutputIndex) {
143
- // The output isn't the change output
142
+ if (
143
+ tx.outputs.length === 1 || // The transaction has only 1 output
144
+ output.output_index < changeOutputIndex // The output isn't the change output
145
+ ) {
144
146
  recipients.push(
145
147
  syncReplaceAddress
146
148
  ? syncReplaceAddress(output.address)
@@ -1,9 +1,10 @@
1
1
  // from https://github.com/LedgerHQ/xpub-scan/blob/master/src/actions/deriveAddresses.ts
2
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
3
- // @ts-ignore
4
- import { bech32, bech32m } from "bech32";
2
+
3
+ import * as bech32 from "bech32";
4
+ import { bech32m } from "../../bech32m";
5
5
  import * as bjs from "bitcoinjs-lib";
6
6
  import { publicKeyTweakAdd } from "secp256k1";
7
+ import { InvalidAddress } from "@ledgerhq/errors";
7
8
  import { DerivationModes } from "../types";
8
9
  import Base from "./base";
9
10
 
@@ -87,7 +88,7 @@ class Bitcoin extends Base {
87
88
  // Make sure the address is valid on this network
88
89
  // otherwise we can't call toOutputScriptTemporary.
89
90
  if (!this.validateAddress(address)) {
90
- throw new Error("Invalid address");
91
+ throw new InvalidAddress();
91
92
  }
92
93
  // bitcoinjs-lib/src/address doesn't yet have released support for bech32m,
93
94
  // so we'll implement our own version of toOutputScript while waiting.
@@ -5,6 +5,7 @@ import bchaddr from "bchaddrjs";
5
5
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
6
6
  // @ts-ignore
7
7
  import { toOutputScript } from "bitcoinjs-lib/src/address";
8
+ import { InvalidAddress } from "@ledgerhq/errors";
8
9
  import { DerivationModes } from "../types";
9
10
  import { ICrypto } from "./types";
10
11
 
@@ -60,7 +61,7 @@ class BitcoinCash implements ICrypto {
60
61
 
61
62
  toOutputScript(address: string) {
62
63
  if (!this.validateAddress(address)) {
63
- throw new Error("Invalid address");
64
+ throw new InvalidAddress();
64
65
  }
65
66
  // TODO find a better way to calculate the script from bch address instead of converting to bitcoin address
66
67
  return toOutputScript(bchaddr.toLegacyAddress(address), this.network);
@@ -1,4 +1,4 @@
1
- import { bech32 } from "bech32";
1
+ import * as bech32 from "bech32";
2
2
  import bs58check from "bs58check";
3
3
  import Base from "./base";
4
4
 
@@ -3,7 +3,7 @@ import * as bip32 from "bip32";
3
3
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
4
4
  // @ts-ignore
5
5
  import { toOutputScript } from "bitcoinjs-lib/src/address";
6
- import { bech32 } from "bech32";
6
+ import * as bech32 from "bech32";
7
7
  import { DerivationModes } from "../types";
8
8
  import { ICrypto } from "./types";
9
9
  import Base from "./base";
@@ -5,9 +5,10 @@ import { toOutputScript } from "bitcoinjs-lib/src/address";
5
5
  // @ts-ignore
6
6
  import zec from "zcash-bitcore-lib";
7
7
  import bs58check from "bs58check";
8
+ import coininfo from "coininfo";
9
+ import { InvalidAddress } from "@ledgerhq/errors";
8
10
  import { DerivationModes } from "../types";
9
11
  import { ICrypto } from "./types";
10
- import coininfo from "coininfo";
11
12
 
12
13
  class ZCash implements ICrypto {
13
14
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -61,7 +62,7 @@ class ZCash implements ICrypto {
61
62
 
62
63
  toOutputScript(address: string) {
63
64
  if (!this.validateAddress(address)) {
64
- throw new Error("Invalid address");
65
+ throw new InvalidAddress();
65
66
  }
66
67
  // TODO find a better way to calculate the script from zec address instead of converting to bitcoin address
67
68
  return toOutputScript(
@@ -5,9 +5,10 @@ import { toOutputScript } from "bitcoinjs-lib/src/address";
5
5
  // @ts-ignore
6
6
  import zec from "zcash-bitcore-lib";
7
7
  import bs58check from "bs58check";
8
+ import coininfo from "coininfo";
9
+ import { InvalidAddress } from "@ledgerhq/errors";
8
10
  import { DerivationModes } from "../types";
9
11
  import { ICrypto } from "./types";
10
- import coininfo from "coininfo";
11
12
 
12
13
  class Zen implements ICrypto {
13
14
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -81,13 +82,18 @@ class Zen implements ICrypto {
81
82
 
82
83
  toOutputScript(address: string) {
83
84
  if (!this.validateAddress(address)) {
84
- throw new Error("Invalid address");
85
+ throw new InvalidAddress();
85
86
  }
86
- // TODO find a better way to calculate the script from zen address instead of converting to bitcoin address
87
- return toOutputScript(
87
+ const outputScript = toOutputScript(
88
88
  Zen.toBitcoinAddr(address),
89
89
  coininfo.bitcoin.main.toBitcoinJS()
90
90
  );
91
+ // refer to https://github.com/LedgerHQ/lib-ledger-core/blob/fc9d762b83fc2b269d072b662065747a64ab2816/core/src/wallet/bitcoin/scripts/BitcoinLikeScript.cpp#L139 and https://github.com/LedgerHQ/lib-ledger-core/blob/fc9d762b83fc2b269d072b662065747a64ab2816/core/src/wallet/bitcoin/networks.cpp#L39 for bip115 Script and its network parameters
92
+ const bip115Script = Buffer.from(
93
+ "209ec9845acb02fab24e1c0368b3b517c1a4488fba97f0e3459ac053ea0100000003c01f02b4",
94
+ "hex"
95
+ );
96
+ return Buffer.concat([outputScript, bip115Script]);
91
97
  }
92
98
 
93
99
  // eslint-disable-next-line class-methods-use-this
@@ -7,14 +7,14 @@ import { PickingStrategy } from "./types";
7
7
  import * as utils from "../utils";
8
8
  import { DeepFirst } from "./DeepFirst";
9
9
  import { log } from "@ledgerhq/logs";
10
+ import { OutputInfo } from "..";
10
11
 
11
12
  export class CoinSelect extends PickingStrategy {
12
13
  // eslint-disable-next-line class-methods-use-this
13
14
  async selectUnspentUtxosToUse(
14
15
  xpub: Xpub,
15
- amount: BigNumber,
16
- feePerByte: number,
17
- nbOutputsWithoutChange: number
16
+ outputs: OutputInfo[],
17
+ feePerByte: number
18
18
  ) {
19
19
  // get the utxos to use as input
20
20
  // from all addresses of the account
@@ -34,18 +34,30 @@ export class CoinSelect extends PickingStrategy {
34
34
  const TOTAL_TRIES = 100000;
35
35
  log("picking strategy", "utxos", unspentUtxos);
36
36
  // Compute cost of change
37
- const fixedSize = utils.accurateTxSize(
38
- 0,
37
+ const fixedSize = utils.maxTxSize(
39
38
  0,
39
+ [],
40
+ false,
40
41
  this.crypto,
41
42
  this.derivationMode
42
43
  );
44
+ const outputAddresses = outputs.map((o) => o.address);
43
45
  // Size of only 1 output (without fixed size)
44
46
  const oneOutputSize =
45
- utils.accurateTxSize(0, 1, this.crypto, this.derivationMode) - fixedSize;
47
+ outputAddresses.length > 0
48
+ ? utils.maxTxSize(
49
+ 0,
50
+ [outputAddresses[0]],
51
+ false,
52
+ this.crypto,
53
+ this.derivationMode
54
+ ) - fixedSize
55
+ : utils.maxTxSize(0, [], true, this.crypto, this.derivationMode) -
56
+ fixedSize;
46
57
  // Size 1 signed UTXO (signed input)
47
58
  const oneInputSize =
48
- utils.accurateTxSize(1, 0, this.crypto, this.derivationMode) - fixedSize;
59
+ utils.maxTxSize(1, [], false, this.crypto, this.derivationMode) -
60
+ fixedSize;
49
61
 
50
62
  // Calculate effective value of outputs
51
63
  let currentAvailableValue = 0;
@@ -70,12 +82,16 @@ export class CoinSelect extends PickingStrategy {
70
82
  // Get no inputs fees
71
83
  // At beginning, there are no outputs in tx, so noInputFees are fixed fees
72
84
  const notInputFees =
73
- feePerByte * (fixedSize + oneOutputSize * nbOutputsWithoutChange);
85
+ feePerByte * (fixedSize + oneOutputSize * outputs.length);
74
86
 
75
87
  // Start coin selection algorithm (according to SelectCoinBnb from Bitcoin Core)
76
88
  let currentValue = 0;
77
89
  const currentSelection: boolean[] = [];
78
90
 
91
+ const amount = outputs.reduce(
92
+ (sum, output) => sum.plus(output.value),
93
+ new BigNumber(0)
94
+ );
79
95
  // Actual amount we are targetting
80
96
  const actualTarget = notInputFees + amount.toNumber();
81
97
 
@@ -102,9 +118,10 @@ export class CoinSelect extends PickingStrategy {
102
118
  currentSelectionNeedChangeoutput = true;
103
119
  currentWaste =
104
120
  feePerByte *
105
- utils.estimateTxSize(
121
+ utils.maxTxSizeCeil(
106
122
  nbInput,
107
- nbOutputsWithoutChange + 1,
123
+ outputAddresses,
124
+ true,
108
125
  this.crypto,
109
126
  this.derivationMode
110
127
  );
@@ -113,9 +130,10 @@ export class CoinSelect extends PickingStrategy {
113
130
  currentSelectionNeedChangeoutput = false;
114
131
  currentWaste =
115
132
  feePerByte *
116
- utils.estimateTxSize(
133
+ utils.maxTxSizeCeil(
117
134
  nbInput,
118
- nbOutputsWithoutChange,
135
+ outputAddresses,
136
+ false,
119
137
  this.crypto,
120
138
  this.derivationMode
121
139
  ) +
@@ -170,11 +188,10 @@ export class CoinSelect extends PickingStrategy {
170
188
  }
171
189
  const fee =
172
190
  feePerByte *
173
- utils.estimateTxSize(
191
+ utils.maxTxSizeCeil(
174
192
  unspentUtxoSelected.length,
175
- bestSelectionNeedChangeoutput
176
- ? nbOutputsWithoutChange + 1
177
- : nbOutputsWithoutChange,
193
+ outputAddresses,
194
+ bestSelectionNeedChangeoutput,
178
195
  this.crypto,
179
196
  this.derivationMode
180
197
  );
@@ -191,11 +208,6 @@ export class CoinSelect extends PickingStrategy {
191
208
  this.derivationMode,
192
209
  this.excludedUTXOs
193
210
  );
194
- return pickingStrategy.selectUnspentUtxosToUse(
195
- xpub,
196
- amount,
197
- feePerByte,
198
- nbOutputsWithoutChange
199
- );
211
+ return pickingStrategy.selectUnspentUtxosToUse(xpub, outputs, feePerByte);
200
212
  }
201
213
  }
@@ -6,14 +6,14 @@ import Xpub from "../xpub";
6
6
  import { PickingStrategy } from "./types";
7
7
  import * as utils from "../utils";
8
8
  import { log } from "@ledgerhq/logs";
9
+ import { OutputInfo } from "..";
9
10
 
10
11
  export class DeepFirst extends PickingStrategy {
11
12
  // eslint-disable-next-line class-methods-use-this
12
13
  async selectUnspentUtxosToUse(
13
14
  xpub: Xpub,
14
- amount: BigNumber,
15
- feePerByte: number,
16
- nbOutputsWithoutChange: number
15
+ outputs: OutputInfo[],
16
+ feePerByte: number
17
17
  ) {
18
18
  // get the utxos to use as input
19
19
  // from all addresses of the account
@@ -31,26 +31,47 @@ export class DeepFirst extends PickingStrategy {
31
31
  ).length
32
32
  );
33
33
 
34
+ const outputAddresses = outputs.map((o) => o.address);
34
35
  unspentUtxos = sortBy(unspentUtxos, "block_height");
35
36
  // https://metamug.com/article/security/bitcoin-transaction-fee-satoshi-per-byte.html
36
- const txSizeNoInput = utils.accurateTxSize(
37
+ const txSizeNoInput = utils.maxTxSize(
37
38
  0,
38
- nbOutputsWithoutChange,
39
+ outputAddresses,
40
+ false,
39
41
  this.crypto,
40
42
  this.derivationMode
41
43
  );
42
44
  let fee = txSizeNoInput * feePerByte;
45
+ const emptyTxSize = utils.maxTxSizeCeil(
46
+ 0,
47
+ [],
48
+ false,
49
+ this.crypto,
50
+ this.derivationMode
51
+ );
43
52
  const sizePerInput =
44
- utils.accurateTxSize(1, 0, this.crypto, this.derivationMode) -
45
- utils.accurateTxSize(0, 0, this.crypto, this.derivationMode);
53
+ utils.maxTxSize(1, [], false, this.crypto, this.derivationMode) -
54
+ emptyTxSize;
46
55
 
47
56
  const sizePerOutput =
48
- utils.accurateTxSize(0, 1, this.crypto, this.derivationMode) -
49
- utils.accurateTxSize(0, 0, this.crypto, this.derivationMode);
57
+ (outputAddresses[0]
58
+ ? utils.maxTxSize(
59
+ 0,
60
+ [outputAddresses[0]],
61
+ false,
62
+ this.crypto,
63
+ this.derivationMode
64
+ )
65
+ : utils.maxTxSize(0, [], true, this.crypto, this.derivationMode)) -
66
+ emptyTxSize;
50
67
 
51
68
  let total = new BigNumber(0);
52
69
  const unspentUtxoSelected: Output[] = [];
53
70
 
71
+ const amount = outputs.reduce(
72
+ (sum, output) => sum.plus(output.value),
73
+ new BigNumber(0)
74
+ );
54
75
  let i = 0;
55
76
  while (total.lt(amount.plus(fee))) {
56
77
  if (!unspentUtxos[i]) {
@@ -6,14 +6,14 @@ import Xpub from "../xpub";
6
6
  import { PickingStrategy } from "./types";
7
7
  import * as utils from "../utils";
8
8
  import { log } from "@ledgerhq/logs";
9
+ import { OutputInfo } from "..";
9
10
 
10
11
  export class Merge extends PickingStrategy {
11
12
  // eslint-disable-next-line class-methods-use-this
12
13
  async selectUnspentUtxosToUse(
13
14
  xpub: Xpub,
14
- amount: BigNumber,
15
- feePerByte: number,
16
- nbOutputsWithoutChange: number
15
+ outputs: OutputInfo[],
16
+ feePerByte: number
17
17
  ) {
18
18
  // get the utxos to use as input
19
19
  // from all addresses of the account
@@ -31,19 +31,36 @@ export class Merge extends PickingStrategy {
31
31
  ).length
32
32
  );
33
33
 
34
+ const outputAddresses = outputs.map((o) => o.address);
35
+ const emptyTxSize = utils.maxTxSizeCeil(
36
+ 0,
37
+ [],
38
+ false,
39
+ this.crypto,
40
+ this.derivationMode
41
+ );
34
42
  const sizePerInput =
35
- utils.accurateTxSize(1, 0, this.crypto, this.derivationMode) -
36
- utils.accurateTxSize(0, 0, this.crypto, this.derivationMode);
43
+ utils.maxTxSize(1, [], false, this.crypto, this.derivationMode) -
44
+ emptyTxSize;
37
45
 
38
46
  const sizePerOutput =
39
- utils.accurateTxSize(0, 1, this.crypto, this.derivationMode) -
40
- utils.accurateTxSize(0, 0, this.crypto, this.derivationMode);
47
+ (outputAddresses[0]
48
+ ? utils.maxTxSize(
49
+ 0,
50
+ [outputAddresses[0]],
51
+ false,
52
+ this.crypto,
53
+ this.derivationMode
54
+ )
55
+ : utils.maxTxSize(0, [], true, this.crypto, this.derivationMode)) -
56
+ emptyTxSize;
41
57
 
42
58
  unspentUtxos = sortBy(unspentUtxos, (utxo) => parseInt(utxo.value, 10));
43
59
  // https://metamug.com/article/security/bitcoin-transaction-fee-satoshi-per-byte.html
44
- const txSizeNoInput = utils.accurateTxSize(
60
+ const txSizeNoInput = utils.maxTxSize(
45
61
  0,
46
- nbOutputsWithoutChange,
62
+ outputAddresses,
63
+ false,
47
64
  this.crypto,
48
65
  this.derivationMode
49
66
  );
@@ -53,6 +70,10 @@ export class Merge extends PickingStrategy {
53
70
  const unspentUtxoSelected: Output[] = [];
54
71
 
55
72
  let i = 0;
73
+ const amount = outputs.reduce(
74
+ (sum, output) => sum.plus(output.value),
75
+ new BigNumber(0)
76
+ );
56
77
  while (total.lt(amount.plus(fee))) {
57
78
  if (!unspentUtxos[i]) {
58
79
  throw new NotEnoughBalance();
@@ -1,4 +1,5 @@
1
1
  import BigNumber from "bignumber.js";
2
+ import { OutputInfo } from "..";
2
3
  import { ICrypto } from "../crypto/types";
3
4
  import { Output } from "../storage/types";
4
5
  import Xpub from "../xpub";
@@ -29,9 +30,8 @@ export abstract class PickingStrategy {
29
30
 
30
31
  abstract selectUnspentUtxosToUse(
31
32
  xpub: Xpub,
32
- amount: BigNumber,
33
- feePerByte: number,
34
- nbOutputsWithoutChange: number
33
+ outputs: OutputInfo[],
34
+ feePerByte: number
35
35
  ): Promise<{
36
36
  unspentUtxos: Output[];
37
37
  totalValue: BigNumber;