@ledgerhq/live-common 21.13.1 → 21.14.1

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 (101) hide show
  1. package/lib/__tests__/derivation.js +1 -1
  2. package/lib/__tests__/derivation.js.map +1 -1
  3. package/lib/__tests__/families/bitcoin/wallet-btc/wallet.integration.test.js +2 -2
  4. package/lib/apps/polyfill.d.ts.map +1 -1
  5. package/lib/apps/polyfill.js +1 -0
  6. package/lib/apps/polyfill.js.map +1 -1
  7. package/lib/derivation.js +1 -1
  8. package/lib/derivation.js.map +1 -1
  9. package/lib/env.js +1 -1
  10. package/lib/env.js.map +1 -1
  11. package/lib/families/bitcoin/cache.d.ts +1 -0
  12. package/lib/families/bitcoin/cache.d.ts.map +1 -1
  13. package/lib/families/bitcoin/cache.js +7 -1
  14. package/lib/families/bitcoin/cache.js.map +1 -1
  15. package/lib/families/bitcoin/errors.d.ts +4 -0
  16. package/lib/families/bitcoin/errors.d.ts.map +1 -0
  17. package/lib/families/bitcoin/errors.js +6 -0
  18. package/lib/families/bitcoin/errors.js.map +1 -0
  19. package/lib/families/bitcoin/js-getTransactionStatus.d.ts.map +1 -1
  20. package/lib/families/bitcoin/js-getTransactionStatus.js +24 -11
  21. package/lib/families/bitcoin/js-getTransactionStatus.js.map +1 -1
  22. package/lib/families/bitcoin/js-synchronisation.js +2 -2
  23. package/lib/families/bitcoin/js-synchronisation.js.map +1 -1
  24. package/lib/families/bitcoin/wallet-btc/crypto/base.d.ts +4 -0
  25. package/lib/families/bitcoin/wallet-btc/crypto/base.d.ts.map +1 -1
  26. package/lib/families/bitcoin/wallet-btc/crypto/base.js +36 -5
  27. package/lib/families/bitcoin/wallet-btc/crypto/base.js.map +1 -1
  28. package/lib/families/bitcoin/wallet-btc/crypto/bitcoin.d.ts +1 -0
  29. package/lib/families/bitcoin/wallet-btc/crypto/bitcoin.d.ts.map +1 -1
  30. package/lib/families/bitcoin/wallet-btc/crypto/bitcoin.js +19 -1
  31. package/lib/families/bitcoin/wallet-btc/crypto/bitcoin.js.map +1 -1
  32. package/lib/families/bitcoin/wallet-btc/crypto/bitcoincash.d.ts +1 -0
  33. package/lib/families/bitcoin/wallet-btc/crypto/bitcoincash.d.ts.map +1 -1
  34. package/lib/families/bitcoin/wallet-btc/crypto/bitcoincash.js +4 -0
  35. package/lib/families/bitcoin/wallet-btc/crypto/bitcoincash.js.map +1 -1
  36. package/lib/families/bitcoin/wallet-btc/crypto/types.d.ts +1 -0
  37. package/lib/families/bitcoin/wallet-btc/crypto/types.d.ts.map +1 -1
  38. package/lib/families/bitcoin/wallet-btc/crypto/zec.d.ts +1 -0
  39. package/lib/families/bitcoin/wallet-btc/crypto/zec.d.ts.map +1 -1
  40. package/lib/families/bitcoin/wallet-btc/crypto/zec.js +4 -0
  41. package/lib/families/bitcoin/wallet-btc/crypto/zec.js.map +1 -1
  42. package/lib/families/bitcoin/wallet-btc/crypto/zen.d.ts +1 -0
  43. package/lib/families/bitcoin/wallet-btc/crypto/zen.d.ts.map +1 -1
  44. package/lib/families/bitcoin/wallet-btc/crypto/zen.js +4 -0
  45. package/lib/families/bitcoin/wallet-btc/crypto/zen.js.map +1 -1
  46. package/lib/families/bitcoin/wallet-btc/explorer/index.d.ts.map +1 -1
  47. package/lib/families/bitcoin/wallet-btc/explorer/index.js +0 -3
  48. package/lib/families/bitcoin/wallet-btc/explorer/index.js.map +1 -1
  49. package/lib/families/bitcoin/wallet-btc/index.d.ts +2 -2
  50. package/lib/families/bitcoin/wallet-btc/index.d.ts.map +1 -1
  51. package/lib/families/bitcoin/wallet-btc/index.js +4 -5
  52. package/lib/families/bitcoin/wallet-btc/index.js.map +1 -1
  53. package/lib/families/bitcoin/wallet-btc/utils.d.ts +2 -1
  54. package/lib/families/bitcoin/wallet-btc/utils.d.ts.map +1 -1
  55. package/lib/families/bitcoin/wallet-btc/utils.js +23 -7
  56. package/lib/families/bitcoin/wallet-btc/utils.js.map +1 -1
  57. package/lib/families/bitcoin/wallet-btc/wallet.js +1 -1
  58. package/lib/families/bitcoin/wallet-btc/wallet.js.map +1 -1
  59. package/lib/families/bitcoin/wallet-btc/xpub.d.ts +2 -6
  60. package/lib/families/bitcoin/wallet-btc/xpub.d.ts.map +1 -1
  61. package/lib/families/bitcoin/wallet-btc/xpub.js +62 -52
  62. package/lib/families/bitcoin/wallet-btc/xpub.js.map +1 -1
  63. package/lib/families/elrond/js-getTransactionStatus.d.ts.map +1 -1
  64. package/lib/families/elrond/js-getTransactionStatus.js +3 -0
  65. package/lib/families/elrond/js-getTransactionStatus.js.map +1 -1
  66. package/lib/families/elrond/js-signOperation.d.ts.map +1 -1
  67. package/lib/families/elrond/js-signOperation.js +3 -1
  68. package/lib/families/elrond/js-signOperation.js.map +1 -1
  69. package/lib/families/ethereum/specs.d.ts +1 -0
  70. package/lib/families/ethereum/specs.d.ts.map +1 -1
  71. package/lib/families/ethereum/specs.js +41 -0
  72. package/lib/families/ethereum/specs.js.map +1 -1
  73. package/lib/families/polkadot/js-buildTransaction.d.ts +7 -7
  74. package/lib/generated/specs.d.ts +1 -0
  75. package/lib/generated/specs.d.ts.map +1 -1
  76. package/package.json +8 -8
  77. package/src/__tests__/__snapshots__/all.libcore.ts.snap +49100 -49100
  78. package/src/__tests__/derivation.ts +1 -1
  79. package/src/__tests__/families/bitcoin/wallet-btc/wallet.integration.test.ts +2 -2
  80. package/src/apps/polyfill.ts +1 -0
  81. package/src/currencies/__snapshots__/sortByMarketcap.test.ts.snap +10 -1
  82. package/src/derivation.ts +1 -1
  83. package/src/env.ts +1 -1
  84. package/src/families/bitcoin/cache.ts +12 -0
  85. package/src/families/bitcoin/errors.ts +5 -0
  86. package/src/families/bitcoin/js-getTransactionStatus.ts +15 -1
  87. package/src/families/bitcoin/js-synchronisation.ts +2 -2
  88. package/src/families/bitcoin/wallet-btc/crypto/base.ts +40 -5
  89. package/src/families/bitcoin/wallet-btc/crypto/bitcoin.ts +21 -1
  90. package/src/families/bitcoin/wallet-btc/crypto/bitcoincash.ts +5 -0
  91. package/src/families/bitcoin/wallet-btc/crypto/types.ts +1 -0
  92. package/src/families/bitcoin/wallet-btc/crypto/zec.ts +5 -0
  93. package/src/families/bitcoin/wallet-btc/crypto/zen.ts +5 -0
  94. package/src/families/bitcoin/wallet-btc/explorer/index.ts +0 -5
  95. package/src/families/bitcoin/wallet-btc/index.ts +4 -4
  96. package/src/families/bitcoin/wallet-btc/utils.ts +20 -6
  97. package/src/families/bitcoin/wallet-btc/wallet.ts +1 -1
  98. package/src/families/bitcoin/wallet-btc/xpub.ts +37 -53
  99. package/src/families/elrond/js-getTransactionStatus.ts +4 -0
  100. package/src/families/elrond/js-signOperation.ts +3 -1
  101. package/src/families/ethereum/specs.ts +47 -0
@@ -6,7 +6,7 @@ import {
6
6
  describe("derivation", () => {
7
7
  test("getPreferredNewAccountScheme should return a list of schemes for a given currency", () => {
8
8
  const testData = [
9
- ["bitcoin", ["native_segwit", "segwit", ""]],
9
+ ["bitcoin", ["native_segwit", "taproot", "segwit", ""]],
10
10
  ["ethereum", null],
11
11
  ["cosmos", null],
12
12
  ["litecoin", ["native_segwit", "segwit", ""]],
@@ -63,7 +63,7 @@ describe("testing wallet", () => {
63
63
  fromAccount: account,
64
64
  txInfo,
65
65
  });
66
- expect(tx).toEqual("66ad13fd62ca677ae4d169ec5fe84be2f846562a");
66
+ expect(tx).toEqual("d6154bba4915d07453bb09d4fa02d62e975b4475");
67
67
  });
68
68
 
69
69
  it("should allow to build a transaction splitting outputs", async () => {
@@ -87,7 +87,7 @@ describe("testing wallet", () => {
87
87
  fromAccount: account,
88
88
  txInfo,
89
89
  });
90
- expect(tx).toEqual("23ab414d38fe836e22619d6ee84fdebafa6b5168");
90
+ expect(tx).toEqual("d922a70d6a66b96cdaba4565270a21f97eb23a6d");
91
91
  });
92
92
 
93
93
  it("should throw during sync if there is an error in explorer", async () => {
@@ -48,6 +48,7 @@ export const whitelistDependencies = ["Decred", "Decred Testnet"];
48
48
  ["Compound", "Ethereum"],
49
49
  ["Opensea", "Ethereum"],
50
50
  ["StakeDAO", "Ethereum"],
51
+ ["Yearn", "Ethereum"],
51
52
  ].forEach(([name, dep]) => declareDep(name, dep));
52
53
  export const getDependencies = (appName: string): string[] =>
53
54
  directDep[appName] || [];
@@ -722,7 +722,6 @@ Array [
722
722
  "ethereum/erc20/wholeearthcoin",
723
723
  "ethereum/erc20/datx",
724
724
  "ethereum/erc20/mileverse",
725
- "ethereum/erc20/cr_coin",
726
725
  "ethereum/erc20/biocrypt",
727
726
  "ethereum/erc20/bittwatt",
728
727
  "ethereum/erc20/ono_token",
@@ -2186,6 +2185,7 @@ Array [
2186
2185
  "ethereum/erc20/cpollo",
2187
2186
  "ethereum/erc20/cprop",
2188
2187
  "ethereum/erc20/cr7coin",
2188
+ "ethereum/erc20/cr_coin",
2189
2189
  "ethereum/erc20/crafty_token",
2190
2190
  "ethereum/erc20/crb",
2191
2191
  "ethereum/erc20/cream",
@@ -3626,6 +3626,7 @@ Array [
3626
3626
  "ethereum/erc20/monftertoken",
3627
3627
  "ethereum/erc20/monkeycoin",
3628
3628
  "ethereum/erc20/monnos_token",
3629
+ "ethereum/erc20/mononoke_inu",
3629
3630
  "ethereum/erc20/moon_juice",
3630
3631
  "ethereum/erc20/moon_yfi",
3631
3632
  "ethereum/erc20/moona239_coins",
@@ -4271,6 +4272,7 @@ Array [
4271
4272
  "ethereum/erc20/safeswap",
4272
4273
  "ethereum/erc20/saitama_inu",
4273
4274
  "ethereum/erc20/saito",
4275
+ "ethereum/erc20/saja",
4274
4276
  "ethereum/erc20/sake",
4275
4277
  "ethereum/erc20/saketoken",
4276
4278
  "ethereum/erc20/sakhalin_husky",
@@ -4496,6 +4498,7 @@ Array [
4496
4498
  "ethereum/erc20/spurt_plus",
4497
4499
  "ethereum/erc20/squirrel_finance",
4498
4500
  "ethereum/erc20/srh",
4501
+ "ethereum/erc20/ssv_token",
4499
4502
  "ethereum/erc20/sta",
4500
4503
  "ethereum/erc20/stabilize_token",
4501
4504
  "ethereum/erc20/stabinol_token",
@@ -4855,6 +4858,7 @@ Array [
4855
4858
  "ethereum/erc20/tryfinance",
4856
4859
  "ethereum/erc20/tsla.finance",
4857
4860
  "ethereum/erc20/tsuki_inu",
4861
+ "ethereum/erc20/tsuzuki_inu",
4858
4862
  "ethereum/erc20/ttx_nft",
4859
4863
  "ethereum/erc20/tugz",
4860
4864
  "ethereum/erc20/tune",
@@ -5090,6 +5094,7 @@ Array [
5090
5094
  "ethereum/erc20/wild_credit",
5091
5095
  "ethereum/erc20/wilder",
5092
5096
  "ethereum/erc20/windbellows",
5097
+ "ethereum/erc20/winry_inu",
5093
5098
  "ethereum/erc20/wise_token",
5094
5099
  "ethereum/erc20/witch_token",
5095
5100
  "ethereum/erc20/wiva",
@@ -5777,7 +5782,9 @@ Array [
5777
5782
  "bsc/bep20/easy_v2",
5778
5783
  "bsc/bep20/elite_swap_binance_token",
5779
5784
  "bsc/bep20/ellipsis",
5785
+ "bsc/bep20/empire_token",
5780
5786
  "bsc/bep20/empiricus_token",
5787
+ "bsc/bep20/ev_direct_currency",
5781
5788
  "bsc/bep20/exeedme",
5782
5789
  "bsc/bep20/extend_finance",
5783
5790
  "bsc/bep20/faracrystal",
@@ -5922,6 +5929,7 @@ Array [
5922
5929
  "bsc/bep20/seedifyfund",
5923
5930
  "bsc/bep20/seven_up_token",
5924
5931
  "bsc/bep20/shard",
5932
+ "bsc/bep20/shiba_inu",
5925
5933
  "bsc/bep20/shield_protocol",
5926
5934
  "bsc/bep20/shirtum",
5927
5935
  "bsc/bep20/smart_advertising_transaction_token",
@@ -5951,6 +5959,7 @@ Array [
5951
5959
  "bsc/bep20/tenet",
5952
5960
  "bsc/bep20/the_force_token",
5953
5961
  "bsc/bep20/thugs",
5962
+ "bsc/bep20/tiki",
5954
5963
  "bsc/bep20/titanswap",
5955
5964
  "bsc/bep20/tixl_token",
5956
5965
  "bsc/bep20/tokenpocket_token",
package/src/derivation.ts CHANGED
@@ -470,7 +470,7 @@ export const getDerivationModesForCurrency = (
470
470
 
471
471
  // taproot logic. FIXME should move per family
472
472
  if (currency.family === "bitcoin") {
473
- if (currency.id === "bitcoin_testnet") {
473
+ if (currency.id === "bitcoin" || currency.id === "bitcoin_testnet") {
474
474
  all.push("taproot");
475
475
  }
476
476
  }
package/src/env.ts CHANGED
@@ -67,7 +67,7 @@ const envDefinitions = {
67
67
  },
68
68
  API_RIPPLE_WS: {
69
69
  parser: stringParser,
70
- def: "wss://xrplcluster.com",
70
+ def: "wss://xrplcluster.com/ledgerlive",
71
71
  desc: "XRP Ledger full history open WebSocket endpoint",
72
72
  },
73
73
  API_FILECOIN_ENDPOINT: {
@@ -4,6 +4,7 @@ import type { Account, CryptoCurrency } from "./../../types";
4
4
  import type { Transaction } from "./types";
5
5
  import getFeesForTransaction from "./js-getFeesForTransaction";
6
6
  import { isValidRecipient } from "./logic";
7
+ import { Currency, isTaprootAddress } from "./wallet-btc";
7
8
 
8
9
  const getCacheKeyForCalculateFees = ({
9
10
  a,
@@ -41,6 +42,7 @@ export const calculateFees = makeLRUCache(
41
42
  maxAge: 5 * 60 * 1000, // 5 minutes
42
43
  }
43
44
  );
45
+
44
46
  export const validateRecipient: (
45
47
  arg0: CryptoCurrency,
46
48
  arg1: string | null | undefined
@@ -74,3 +76,13 @@ export const validateRecipient: (
74
76
  },
75
77
  (currency, recipient) => `${currency.id}_${recipient || ""}`
76
78
  );
79
+
80
+ export const isTaprootRecipient: (
81
+ arg0: CryptoCurrency,
82
+ arg1: string
83
+ ) => Promise<boolean> = makeLRUCache(
84
+ async (currency, recipient) => {
85
+ return isTaprootAddress(recipient, <Currency>currency.id);
86
+ },
87
+ (currency, recipient) => `${currency.id}_${recipient || ""}`
88
+ );
@@ -0,0 +1,5 @@
1
+ import { createCustomErrorClass } from "@ledgerhq/errors";
2
+
3
+ export const TaprootNotActivated = createCustomErrorClass(
4
+ "TaprootNotActivated"
5
+ );
@@ -9,7 +9,8 @@ import {
9
9
  } from "@ledgerhq/errors";
10
10
  import type { Account, TransactionStatus } from "./../../types";
11
11
  import type { Transaction } from "./types";
12
- import { calculateFees, validateRecipient } from "./cache";
12
+ import { calculateFees, validateRecipient, isTaprootRecipient } from "./cache";
13
+ import { TaprootNotActivated } from "./errors";
13
14
 
14
15
  const getTransactionStatus = async (
15
16
  a: Account,
@@ -31,6 +32,19 @@ const getTransactionStatus = async (
31
32
  warnings.recipient = recipientWarning;
32
33
  }
33
34
 
35
+ // Safeguard before Taproot activation
36
+ if (
37
+ t.recipient &&
38
+ !errors.recipient &&
39
+ a.currency.id === "bitcoin" &&
40
+ a.blockHeight <= 709632
41
+ ) {
42
+ const isTaproot = await isTaprootRecipient(a.currency, t.recipient);
43
+ if (isTaproot) {
44
+ errors.recipient = new TaprootNotActivated();
45
+ }
46
+ }
47
+
34
48
  let txInputs;
35
49
  let txOutputs;
36
50
  let estimatedFees = new BigNumber(0);
@@ -342,8 +342,8 @@ const getAccountShape: GetAccountShape = async (info) => {
342
342
  spendableBalance: balance,
343
343
  operations,
344
344
  operationsCount: operations.length,
345
- freshAddress: walletAccount.xpub.freshAddress[0],
346
- freshAddressPath: `${accountPath}/0/${walletAccount.xpub.freshAddressIndex[0]}`,
345
+ freshAddress: walletAccount.xpub.freshAddress,
346
+ freshAddressPath: `${accountPath}/0/${walletAccount.xpub.freshAddressIndex}`,
347
347
  blockHeight,
348
348
  bitcoinResources: {
349
349
  utxos,
@@ -27,6 +27,9 @@ export function fallbackValidateAddress(address: string): boolean {
27
27
  class Base implements ICrypto {
28
28
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
29
29
  network: any;
30
+ protected static publickeyCache = {}; // xpub + account + index to publicKey
31
+ protected static addressCache = {}; // derivationMode + xpub + account + index to address
32
+ protected static bech32Cache = {}; // xpub to bech32 interface
30
33
 
31
34
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
32
35
  constructor({ network }: { network: any }) {
@@ -37,9 +40,26 @@ class Base implements ICrypto {
37
40
  }
38
41
 
39
42
  protected getPubkeyAt(xpub: string, account: number, index: number): Buffer {
40
- return bip32.fromBase58(xpub, this.network).derive(account).derive(index)
41
- .publicKey;
43
+ if (!Base.bech32Cache[xpub]) {
44
+ Base.bech32Cache[xpub] = bip32.fromBase58(xpub, this.network);
45
+ }
46
+ if (Base.publickeyCache[`${xpub}-${account}-${index}`]) {
47
+ return Base.publickeyCache[`${xpub}-${account}-${index}`];
48
+ }
49
+ if (Base.publickeyCache[`${xpub}-${account}`]) {
50
+ const publicKey =
51
+ Base.publickeyCache[`${xpub}-${account}`].derive(index).publicKey;
52
+ Base.publickeyCache[`${xpub}-${account}-${index}`] = publicKey;
53
+ return publicKey;
54
+ }
55
+ Base.publickeyCache[`${xpub}-${account}`] =
56
+ Base.bech32Cache[xpub].derive(account);
57
+ const publicKey =
58
+ Base.publickeyCache[`${xpub}-${account}`].derive(index).publicKey;
59
+ Base.publickeyCache[`${xpub}-${account}-${index}`] = publicKey;
60
+ return publicKey;
42
61
  }
62
+
43
63
  // derive legacy address at account and index positions
44
64
  getLegacyAddress(xpub: string, account: number, index: number): string {
45
65
  const { address } = bjs.payments.p2pkh({
@@ -78,16 +98,26 @@ class Base implements ICrypto {
78
98
  account: number,
79
99
  index: number
80
100
  ): string {
101
+ if (Base.addressCache[`${derivationMode}-${xpub}-${account}-${index}`]) {
102
+ return Base.addressCache[`${derivationMode}-${xpub}-${account}-${index}`];
103
+ }
81
104
  switch (derivationMode) {
82
105
  case DerivationModes.LEGACY:
83
- return this.getLegacyAddress(xpub, account, index);
106
+ Base.addressCache[`${derivationMode}-${xpub}-${account}-${index}`] =
107
+ this.getLegacyAddress(xpub, account, index);
108
+ break;
84
109
  case DerivationModes.SEGWIT:
85
- return this.getSegWitAddress(xpub, account, index);
110
+ Base.addressCache[`${derivationMode}-${xpub}-${account}-${index}`] =
111
+ this.getSegWitAddress(xpub, account, index);
112
+ break;
86
113
  case DerivationModes.NATIVE_SEGWIT:
87
- return this.getNativeSegWitAddress(xpub, account, index);
114
+ Base.addressCache[`${derivationMode}-${xpub}-${account}-${index}`] =
115
+ this.getNativeSegWitAddress(xpub, account, index);
116
+ break;
88
117
  default:
89
118
  throw new Error(`Invalid derivation Mode: ${derivationMode}`);
90
119
  }
120
+ return Base.addressCache[`${derivationMode}-${xpub}-${account}-${index}`];
91
121
  }
92
122
 
93
123
  // infer address type from its syntax
@@ -119,6 +149,11 @@ class Base implements ICrypto {
119
149
  (res[0] === this.network.pubKeyHash || res[0] === this.network.scriptHash)
120
150
  );
121
151
  }
152
+
153
+ // eslint-disable-next-line
154
+ isTaprootAddress(address: string): boolean {
155
+ return false;
156
+ }
122
157
  }
123
158
 
124
159
  export default Base;
@@ -124,9 +124,16 @@ class Bitcoin extends Base {
124
124
  account: number,
125
125
  index: number
126
126
  ): string {
127
+ if (Base.addressCache[`${derivationMode}-${xpub}-${account}-${index}`]) {
128
+ return Base.addressCache[`${derivationMode}-${xpub}-${account}-${index}`];
129
+ }
127
130
  switch (derivationMode) {
128
131
  case DerivationModes.TAPROOT:
129
- return this.getTaprootAddress(xpub, account, index);
132
+ Base.addressCache[`${derivationMode}-${xpub}-${account}-${index}`] =
133
+ this.getTaprootAddress(xpub, account, index);
134
+ return Base.addressCache[
135
+ `${derivationMode}-${xpub}-${account}-${index}`
136
+ ];
130
137
  default:
131
138
  return super.getAddress(derivationMode, xpub, account, index);
132
139
  }
@@ -202,6 +209,19 @@ class Bitcoin extends Base {
202
209
  // Create address
203
210
  return toBech32(outputSchnorrKey, 1, this.network.bech32);
204
211
  }
212
+
213
+ isTaprootAddress(address: string): boolean {
214
+ // This prefix check is to avoid returning false in cases where a valid base58 address also happens
215
+ // to be a valid bech32(m) string (but invalid segwit address).
216
+ if (address.toLowerCase().startsWith(`${this.network.bech32}1`)) {
217
+ try {
218
+ bjs.address.fromBech32(address);
219
+ } catch {
220
+ return true;
221
+ }
222
+ }
223
+ return false;
224
+ }
205
225
  }
206
226
 
207
227
  export default Bitcoin;
@@ -70,6 +70,11 @@ class BitcoinCash implements ICrypto {
70
70
  validateAddress(address: string): boolean {
71
71
  return bchaddr.isValidAddress(address);
72
72
  }
73
+
74
+ // eslint-disable-next-line
75
+ isTaprootAddress(address: string): boolean {
76
+ return false;
77
+ }
73
78
  }
74
79
 
75
80
  export default BitcoinCash;
@@ -11,6 +11,7 @@ export interface ICrypto {
11
11
 
12
12
  toOutputScript(address: string): Buffer;
13
13
  validateAddress(address: string): boolean;
14
+ isTaprootAddress(address: string): boolean;
14
15
  }
15
16
 
16
17
  export type Currency =
@@ -74,6 +74,11 @@ class ZCash implements ICrypto {
74
74
  validateAddress(address: string): boolean {
75
75
  return zec.Address.isValid(address, "livenet");
76
76
  }
77
+
78
+ // eslint-disable-next-line
79
+ isTaprootAddress(address: string): boolean {
80
+ return false;
81
+ }
77
82
  }
78
83
 
79
84
  export default ZCash;
@@ -102,6 +102,11 @@ class Zen implements ICrypto {
102
102
  (res[1] === 0x89 || res[1] === 0x96)
103
103
  );
104
104
  }
105
+
106
+ // eslint-disable-next-line
107
+ isTaprootAddress(address: string): boolean {
108
+ return false;
109
+ }
105
110
  }
106
111
 
107
112
  export default Zen;
@@ -285,12 +285,7 @@ class BitcoinLikeExplorer extends EventEmitter implements IExplorer {
285
285
 
286
286
  // faster than mapping
287
287
  res.txs.forEach((tx) => {
288
- if (!tx.block) {
289
- return;
290
- }
291
-
292
288
  this.hydrateTx(address, tx);
293
-
294
289
  hydratedTxs.push(tx);
295
290
  });
296
291
 
@@ -11,7 +11,7 @@ import { TX, Input, Output } from "./storage/types";
11
11
  import { CoinSelect } from "./pickingstrategies/CoinSelect";
12
12
  import { DeepFirst } from "./pickingstrategies/DeepFirst";
13
13
  import { Merge } from "./pickingstrategies/Merge";
14
- import { isValidAddress } from "./utils";
14
+ import { isValidAddress, isTaprootAddress } from "./utils";
15
15
 
16
16
  import type { Account as WalletAccount } from "./account";
17
17
  import type { Account as LiveAccount } from "./../../../types";
@@ -32,16 +32,16 @@ export {
32
32
  DeepFirst,
33
33
  Merge,
34
34
  isValidAddress,
35
+ isTaprootAddress,
35
36
  Currency,
36
37
  };
37
38
 
38
39
  let wallet: BitcoinLikeWallet | null = null;
39
40
 
40
41
  export const getWalletAccount = (account: LiveAccount): WalletAccount => {
41
- if (account.id.startsWith("libcore")) throw new AccountNeedResync();
42
42
  const walletAccount = account.bitcoinResources?.walletAccount;
43
- if (!walletAccount) {
44
- throw new Error("bitcoin wallet account expected");
43
+ if (account.id.startsWith("libcore") || !walletAccount) {
44
+ throw new AccountNeedResync();
45
45
  }
46
46
  return walletAccount;
47
47
  };
@@ -83,12 +83,12 @@ export function byteSize(count: number) {
83
83
  return 1;
84
84
  }
85
85
  if (count <= 0xffff) {
86
- return 2;
86
+ return 3;
87
87
  }
88
88
  if (count <= 0xffffffff) {
89
- return 4;
89
+ return 5;
90
90
  }
91
- return 8;
91
+ return 9;
92
92
  }
93
93
 
94
94
  // refer to https://github.com/LedgerHQ/lib-ledger-core/blob/fc9d762b83fc2b269d072b662065747a64ab2816/core/src/wallet/bitcoin/api_impl/BitcoinLikeTransactionApi.cpp#L217
@@ -106,10 +106,14 @@ export function estimateTxSize(
106
106
  fixedSize += byteSize(inputCount); // Number of inputs
107
107
  fixedSize += byteSize(outputCount); // Number of outputs
108
108
  fixedSize += 4; // Timelock
109
+ if (derivationMode !== DerivationModes.LEGACY) {
110
+ fixedSize += 0.5; // Segwit marker & segwit flag
111
+ }
109
112
  // refer to https://medium.com/coinmonks/on-bitcoin-transaction-sizes-part-2-9445373d17f4
110
113
  // and https://bitcoin.stackexchange.com/questions/96017/what-are-the-sizes-of-single-sig-and-2-of-3-multisig-taproot-inputs
114
+ // and https://bitcoinops.org/en/tools/calc-size/
111
115
  if (derivationMode === DerivationModes.TAPROOT) {
112
- txSize = fixedSize + 57.5 * inputCount + 43 * outputCount;
116
+ txSize = fixedSize + 57.75 * inputCount + 43 * outputCount;
113
117
  return Math.ceil(txSize);
114
118
  }
115
119
  const isSegwit =
@@ -120,12 +124,12 @@ export function estimateTxSize(
120
124
  // P2SH: 32 PrevTxHash + 4 Index + 23 scriptPubKey + 4 sequence
121
125
  const isNativeSegwit = derivationMode === DerivationModes.NATIVE_SEGWIT;
122
126
  const inputSize = isNativeSegwit ? 41 : 63;
123
- const noWitness = fixedSize + inputSize * inputCount + 34 * outputCount;
127
+ const noWitness = fixedSize + inputSize * inputCount + 43 * outputCount; //43 is the biggest output(taproot output) size, we use the biggest one for safety
124
128
  // Include flag and marker size (one byte each)
125
129
  const witnessSize = noWitness + 108 * inputCount + 2;
126
130
  txSize = (noWitness * 3 + witnessSize) / 4;
127
131
  } else {
128
- txSize = fixedSize + 148 * inputCount + 34 * outputCount;
132
+ txSize = fixedSize + 148 * inputCount + 43 * outputCount;
129
133
  }
130
134
  return Math.ceil(txSize); // We don't allow floating value
131
135
  }
@@ -156,3 +160,13 @@ export function isValidAddress(address: string, currency?: Currency) {
156
160
  const crypto = cryptoFactory(currency);
157
161
  return crypto.validateAddress(address);
158
162
  }
163
+
164
+ export function isTaprootAddress(address: string, currency?: Currency) {
165
+ if (currency === "bitcoin") {
166
+ return cryptoFactory("bitcoin").isTaprootAddress(address);
167
+ } else if (currency === "bitcoin_testnet") {
168
+ return cryptoFactory("bitcoin_testnet").isTaprootAddress(address);
169
+ } else {
170
+ return false;
171
+ }
172
+ }
@@ -263,7 +263,7 @@ class BitcoinLikeWallet {
263
263
  return [
264
264
  btc.splitTransaction(
265
265
  i.txHex,
266
- segwit,
266
+ true,
267
267
  false, // FIXME hasTimestamp needed for LL-7539
268
268
  hasExtraData,
269
269
  additionals
@@ -1,6 +1,6 @@
1
1
  import { maxBy, range, some } from "lodash";
2
2
  import BigNumber from "bignumber.js";
3
- import { Address, IStorage } from "./storage/types";
3
+ import { TX, Address, IStorage } from "./storage/types";
4
4
  import EventEmitter from "./utils/eventemitter";
5
5
  import { IExplorer } from "./explorer/types";
6
6
  import { ICrypto } from "./crypto/types";
@@ -20,9 +20,9 @@ class Xpub extends EventEmitter {
20
20
 
21
21
  derivationMode: string;
22
22
 
23
- freshAddress: { [key: string]: string } = {};
23
+ freshAddress: string;
24
24
 
25
- freshAddressIndex: { [key: string]: number } = {};
25
+ freshAddressIndex: number;
26
26
 
27
27
  // https://github.com/bitcoinjs/bitcoinjs-lib/blob/27a840aac4a12338f1e40c54f3759bbd7a559944/src/bufferutils.js#L24
28
28
  // only works with number so we need to be sure to pass correct numbers
@@ -54,6 +54,8 @@ class Xpub extends EventEmitter {
54
54
  this.crypto = crypto;
55
55
  this.xpub = xpub;
56
56
  this.derivationMode = derivationMode;
57
+ this.freshAddress = "";
58
+ this.freshAddressIndex = 0;
57
59
  }
58
60
 
59
61
  async syncAddress(account: number, index: number) {
@@ -78,9 +80,7 @@ class Xpub extends EventEmitter {
78
80
 
79
81
  // TODO handle eventual reorg case using lastBlock
80
82
 
81
- let added = 0;
82
83
  let total = 0;
83
-
84
84
  try {
85
85
  // TODO perf: bad : looping in the tx array
86
86
  await this.checkAddressReorg(account, index);
@@ -95,20 +95,7 @@ class Xpub extends EventEmitter {
95
95
  if (hasPendings) {
96
96
  await this.storage.removePendingTxs({ account, index });
97
97
  }
98
-
99
- // eslint-disable-next-line no-cond-assign,no-await-in-loop
100
- while (
101
- (added = await this.fetchHydrateAndStoreNewTxs(address, account, index))
102
- ) {
103
- total += added;
104
- }
105
-
106
- const pendingTxs = await this.explorer.getPendings({
107
- address,
108
- account,
109
- index,
110
- });
111
- await this.storage.appendTxs(pendingTxs);
98
+ total = await this.fetchHydrateAndStoreNewTxs(address, account, index);
112
99
  } catch (e) {
113
100
  this.emitSyncedFailed(data);
114
101
  throw e;
@@ -120,11 +107,8 @@ class Xpub extends EventEmitter {
120
107
  account,
121
108
  index,
122
109
  });
123
- if (lastTx) {
124
- this.freshAddressIndex[account] = Math.max(
125
- this.freshAddressIndex[account],
126
- index + 1
127
- );
110
+ if (account === 0 && lastTx) {
111
+ this.freshAddressIndex = Math.max(this.freshAddressIndex, index + 1);
128
112
  }
129
113
  return !!lastTx;
130
114
  }
@@ -137,13 +121,6 @@ class Xpub extends EventEmitter {
137
121
  }
138
122
 
139
123
  async syncAccount(account: number) {
140
- this.freshAddressIndex[account] = 0;
141
- this.freshAddress[account] = this.crypto.getAddress(
142
- this.derivationMode,
143
- this.xpub,
144
- account,
145
- 0
146
- );
147
124
  await this.whenSynced("account", account.toString());
148
125
 
149
126
  this.emitSyncing({
@@ -174,26 +151,19 @@ class Xpub extends EventEmitter {
174
151
  account,
175
152
  index,
176
153
  });
177
- this.freshAddress[account] = this.crypto.getAddress(
178
- this.derivationMode,
179
- this.xpub,
180
- account,
181
- this.freshAddressIndex[account]
182
- );
183
154
  return index;
184
155
  }
185
156
 
186
157
  // TODO : test fail case + incremental
187
158
  async sync() {
188
159
  await this.whenSynced("all");
189
-
190
160
  this.emitSyncing({ type: "all" });
191
-
161
+ this.freshAddressIndex = 0;
192
162
  let account = 0;
193
-
194
163
  try {
195
164
  // eslint-disable-next-line no-await-in-loop
196
- while (await this.syncAccount(account)) {
165
+ while (account < 2 && (await this.syncAccount(account))) {
166
+ // account=0 for receive address; account=1 for change address. No need to handle account>1
197
167
  account += 1;
198
168
  }
199
169
  } catch (e) {
@@ -202,7 +172,12 @@ class Xpub extends EventEmitter {
202
172
  }
203
173
 
204
174
  this.emitSynced({ type: "all", account });
205
-
175
+ this.freshAddress = this.crypto.getAddress(
176
+ this.derivationMode,
177
+ this.xpub,
178
+ 0,
179
+ this.freshAddressIndex
180
+ );
206
181
  return account;
207
182
  }
208
183
 
@@ -440,18 +415,27 @@ class Xpub extends EventEmitter {
440
415
  account: number,
441
416
  index: number
442
417
  ) {
443
- const lastTx = await this.storage.getLastTx({
444
- account,
445
- index,
446
- confirmed: true,
447
- });
418
+ let pendingTxs: TX[] = [];
419
+ let txs: TX[] = [];
420
+ let inserted = 0;
421
+ do {
422
+ const lastTx = await this.storage.getLastTx({
423
+ account,
424
+ index,
425
+ confirmed: true,
426
+ });
448
427
 
449
- const txs = await this.explorer.getAddressTxsSinceLastTxBlock(
450
- this.txsSyncArraySize,
451
- { address, account, index },
452
- lastTx
453
- );
454
- const inserted = await this.storage.appendTxs(txs);
428
+ txs = await this.explorer.getAddressTxsSinceLastTxBlock(
429
+ this.txsSyncArraySize,
430
+ { address, account, index },
431
+ lastTx
432
+ );
433
+ if (pendingTxs.length === 0) {
434
+ pendingTxs = txs.filter((tx) => !tx.block);
435
+ }
436
+ inserted += await this.storage.appendTxs(txs.filter((tx) => tx.block)); // only insert not pending tx
437
+ } while (txs.length - pendingTxs.length >= this.txsSyncArraySize); // check whether page is full, if not, it is the last page
438
+ inserted += await this.storage.appendTxs(pendingTxs);
455
439
  return inserted;
456
440
  }
457
441