mainnet-js 0.4.30 → 0.4.31

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 (62) hide show
  1. package/dist/index.html +1 -1
  2. package/dist/main/constant.d.ts +1 -0
  3. package/dist/main/constant.js +17 -1
  4. package/dist/main/constant.js.map +1 -1
  5. package/dist/main/util/getAddrsByXpubKey.d.ts +22 -0
  6. package/dist/main/util/getAddrsByXpubKey.js +79 -0
  7. package/dist/main/util/getAddrsByXpubKey.js.map +1 -0
  8. package/dist/main/util/getXPubKey.d.ts +1 -0
  9. package/dist/main/util/getXPubKey.js +26 -0
  10. package/dist/main/util/getXPubKey.js.map +1 -0
  11. package/dist/main/util/index.d.ts +5 -3
  12. package/dist/main/util/index.js +13 -6
  13. package/dist/main/util/index.js.map +1 -1
  14. package/dist/main/wallet/Base.d.ts +3 -1
  15. package/dist/main/wallet/Base.js +3 -1
  16. package/dist/main/wallet/Base.js.map +1 -1
  17. package/dist/main/wallet/Wif.d.ts +4 -0
  18. package/dist/main/wallet/Wif.js +72 -3
  19. package/dist/main/wallet/Wif.js.map +1 -1
  20. package/dist/main/wallet/interface.d.ts +4 -0
  21. package/dist/main/wallet/model.d.ts +16 -0
  22. package/dist/main/wallet/model.js +18 -1
  23. package/dist/main/wallet/model.js.map +1 -1
  24. package/dist/mainnet-0.4.31.js +2 -0
  25. package/dist/{mainnet-0.4.30.js.LICENSE.txt → mainnet-0.4.31.js.LICENSE.txt} +0 -0
  26. package/dist/module/constant.d.ts +1 -0
  27. package/dist/module/constant.js +16 -0
  28. package/dist/module/constant.js.map +1 -1
  29. package/dist/module/util/getAddrsByXpubKey.d.ts +22 -0
  30. package/dist/module/util/getAddrsByXpubKey.js +71 -0
  31. package/dist/module/util/getAddrsByXpubKey.js.map +1 -0
  32. package/dist/module/util/getXPubKey.d.ts +1 -0
  33. package/dist/module/util/getXPubKey.js +22 -0
  34. package/dist/module/util/getXPubKey.js.map +1 -0
  35. package/dist/module/util/index.d.ts +5 -3
  36. package/dist/module/util/index.js +5 -3
  37. package/dist/module/util/index.js.map +1 -1
  38. package/dist/module/wallet/Base.d.ts +3 -1
  39. package/dist/module/wallet/Base.js +3 -1
  40. package/dist/module/wallet/Base.js.map +1 -1
  41. package/dist/module/wallet/Wif.d.ts +4 -0
  42. package/dist/module/wallet/Wif.js +76 -7
  43. package/dist/module/wallet/Wif.js.map +1 -1
  44. package/dist/module/wallet/interface.d.ts +4 -0
  45. package/dist/module/wallet/model.d.ts +16 -0
  46. package/dist/module/wallet/model.js +16 -0
  47. package/dist/module/wallet/model.js.map +1 -1
  48. package/dist/tsconfig.browser.tsbuildinfo +1 -1
  49. package/dist/tsconfig.tsbuildinfo +1 -1
  50. package/package.json +1 -1
  51. package/src/constant.ts +17 -0
  52. package/src/util/getAddrsByXpubKey.test.ts +115 -0
  53. package/src/util/getAddrsByXpubKey.ts +98 -0
  54. package/src/util/getXPubKey.ts +36 -0
  55. package/src/util/index.ts +10 -3
  56. package/src/wallet/Base.ts +4 -1
  57. package/src/wallet/Wif.test.ts +173 -1
  58. package/src/wallet/Wif.ts +100 -15
  59. package/src/wallet/createWallet.test.ts +2 -0
  60. package/src/wallet/interface.ts +4 -0
  61. package/src/wallet/model.ts +22 -0
  62. package/dist/mainnet-0.4.30.js +0 -2
@@ -3,7 +3,7 @@ import { bchParam } from "../chain";
3
3
  import { BalanceResponse } from "../util/balanceObjectFromSatoshi";
4
4
  import { UnitEnum } from "../enum";
5
5
  import { initProviders, disconnectProviders } from "../network/Connection";
6
- import { DUST_UTXO_THRESHOLD as DUST } from "../constant";
6
+ import { DERIVATION_PATHS, DUST_UTXO_THRESHOLD as DUST } from "../constant";
7
7
  import { delay } from "../util/delay";
8
8
  import { OpReturnData, SendResponse } from "./model";
9
9
  import { ElectrumRawTransaction } from "../network/interface";
@@ -183,6 +183,9 @@ describe(`Mnemonic wallet creation`, () => {
183
183
  "04aaeb52dd7494c361049de67cc680e83ebcbbbdbeb13637d92cd845f70308af5e9370164133294e5fd1679672fe7866c307daf97281a28f66dca7cbb52919824f",
184
184
  publicKeyHash: "d986ed01b7a22225a70edbf2ba7cfb63a15cb3aa",
185
185
 
186
+ parentXPubKey:
187
+ "xpub6BosfCnifzxcFwrSzQiqu2DBVTshkCXacvNsWGYJVVhhawA7d4R5WSWGFNbi8Aw6ZRc1brxMyWMzG3DSSSSoekkudhUd9yLb6qx39T9nMdj",
188
+ parentDerivationPath: "m/44'/0'/0'",
186
189
  derivationPath: "m/44'/0'/0'/0/0",
187
190
  seed: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
188
191
 
@@ -208,6 +211,7 @@ describe(`Mnemonic wallet creation`, () => {
208
211
  "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
209
212
  );
210
213
  expect(w.getSeed().derivationPath).toBe("m/44'/145'/0'/0/0");
214
+ expect(w.getSeed().parentDerivationPath).toBe("m/44'/145'/0'");
211
215
  });
212
216
  test("Expect '11x abandon about' to have the correct key, seed and path from regtest wallet", async () => {
213
217
  let w = await RegTestWallet.fromId(
@@ -226,6 +230,172 @@ describe(`Mnemonic wallet creation`, () => {
226
230
  });
227
231
  });
228
232
 
233
+ describe(`XPubKey path derivation`, () => {
234
+ test("Expect '11x abandon about' to have the correct xpubs for common derivation paths, seed and path", async () => {
235
+ let w = await Wallet.fromId(
236
+ "seed:mainnet:abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
237
+ );
238
+ let commonPaths = await w.deriveHdPaths(DERIVATION_PATHS);
239
+ expect(commonPaths).toStrictEqual([
240
+ {
241
+ path: "m/0",
242
+ xPubKey:
243
+ "xpub68jrRzQfUmwSaf5Y37Yd5uwfnMRxiR14M3HBonDr91GB7GKEh7R9Mvu2UeCtbASfXZ9FdNo9FwFx6a37HNXUDiXVQFXuadXmevRBa3y7rL8",
244
+ },
245
+ {
246
+ path: "m/0'",
247
+ xPubKey:
248
+ "xpub68jrRzQopSUQm76hJ6TNtiJMJfhj38u1X12xCzExrw388hcN443UVnYpswdUkV7vPJ3KayiCdp3Q5E23s4wvkucohVTh7eSstJdBFyn2DMx",
249
+ },
250
+ {
251
+ path: "m/0'/0",
252
+ xPubKey:
253
+ "xpub6A7PsGUCo9qsn1jhZVB68WKWU9bTt1Wu7fzRqhczRbJ3u3xsF1bJmWBL1MvygTtrfmvNw1adLzmRjQHtDCJDXAHFa4K3wELpGGqEXL4e6d4",
254
+ },
255
+ {
256
+ path: "m/0'/0'",
257
+ xPubKey:
258
+ "xpub6A7PsGUM8pNqwy9AceVuFK6KxY88FhvFMRGP9fjEDKA3P4WpR1zyHH3Lmczj7eorx4RbDC4Qttd8C7HhLA2W9LsxxZzXo1DMCwJFb3zZKZ8",
259
+ },
260
+ {
261
+ path: "m/0'/0'/0'",
262
+ xPubKey:
263
+ "xpub6BiChRN7aqq51RA7RnAmKhqKdGckPncrHWLrj1xoj6ZMfdMJ1dX4Ysh9V3yEhpFCpC3BapjR83xPKY693XXTEU6qgWU3qZs78WBHA15uhYf",
264
+ },
265
+ {
266
+ path: "m/44'/0'/0'",
267
+ xPubKey:
268
+ "xpub6BosfCnifzxcFwrSzQiqu2DBVTshkCXacvNsWGYJVVhhawA7d4R5WSWGFNbi8Aw6ZRc1brxMyWMzG3DSSSSoekkudhUd9yLb6qx39T9nMdj",
269
+ },
270
+ {
271
+ path: "m/44'/0'/0'/0",
272
+ xPubKey:
273
+ "xpub6ELHKXNimKbxMCytPh7EdC2QXx46T9qLDJWGnTraz1H9kMMFdcduoU69wh9cxP12wDxqAAfbaESWGYt5rREsX1J8iR2TEunvzvddduAPYcY",
274
+ },
275
+ {
276
+ path: "m/44'/145'/0'",
277
+ xPubKey:
278
+ "xpub6ByHsPNSQXTWZ7PLESMY2FufyYWtLXagSUpMQq7Un96SiThZH2iJB1X7pwviH1WtKVeDP6K8d6xxFzzoaFzF3s8BKCZx8oEDdDkNnp4owAZ",
279
+ },
280
+ {
281
+ path: "m/44'/145'/0'/0",
282
+ xPubKey:
283
+ "xpub6F2iaK2JUPcgrZ6RTGH6t8VybLPu1XzfrHsDsaKvK6NfULznU6i6aw6ZoefDW2DpNruSLw73RwQg46qvpqB3eryeJJ2tkFCF4Z6gbr8Pjja",
284
+ },
285
+ {
286
+ path: "m/44'/245'/0",
287
+ xPubKey:
288
+ "xpub6Ch34ms5osevEtkEZX81n8EG4c6vgHWGH1gQXBG2uf2Tihb1eed4H1wozLfZB31mV9JD7mymYTQxcLKFFjZHdM5NGdH2Ud1ksSkfwSFjjCg",
289
+ },
290
+ {
291
+ path: "m/44'/245'/0'",
292
+ xPubKey:
293
+ "xpub6Ch34msE9YBtQV7pZrLyRXHwocrpJtNN4KDG8bbyxyhGmEM5MirtqkiH4h9dvnVJ3MekET3w2Fkvej3fyo8WLz9bRPyDynDf6NXNfuydhv1",
294
+ },
295
+ {
296
+ path: "m/44'/245'/0'/0",
297
+ xPubKey:
298
+ "xpub6FFeETss5Zwkw78NDAibKEaGxigU3bgYzLihcqbQqqTyb6jorR9mgR9AexYydxmiPU8koAf5ndaQPjPWK3sDz1wjBjf2TkLbD982S9PWd9Z",
299
+ },
300
+ ]);
301
+ });
302
+ test("Expect '11x abandon about' to return 'protected' for root path", async () => {
303
+ expect.assertions(1);
304
+ try {
305
+ let w = await Wallet.fromId(
306
+ "seed:mainnet:abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
307
+ );
308
+ let commonPaths = await w.deriveHdPaths(["m"]);
309
+ } catch (e: any) {
310
+ expect(e.message).toBe(
311
+ "Storing or sharing of parent public key may lead to loss of funds. Storing or sharing *root* parent public keys is strongly discouraged, although all parent keys have risk. See: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#implications"
312
+ );
313
+ }
314
+ });
315
+
316
+ test("Expect '11x abandon about' to have the correct xpubs for common derivation paths, seed and path", async () => {
317
+ let w = await Wallet.fromId(
318
+ "seed:mainnet:abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
319
+ );
320
+ let commonPaths = await w.getXPubKeys();
321
+ expect(commonPaths).toStrictEqual([
322
+ {
323
+ path: "m/0",
324
+ xPubKey:
325
+ "xpub68jrRzQfUmwSaf5Y37Yd5uwfnMRxiR14M3HBonDr91GB7GKEh7R9Mvu2UeCtbASfXZ9FdNo9FwFx6a37HNXUDiXVQFXuadXmevRBa3y7rL8",
326
+ },
327
+ {
328
+ path: "m/0'",
329
+ xPubKey:
330
+ "xpub68jrRzQopSUQm76hJ6TNtiJMJfhj38u1X12xCzExrw388hcN443UVnYpswdUkV7vPJ3KayiCdp3Q5E23s4wvkucohVTh7eSstJdBFyn2DMx",
331
+ },
332
+ {
333
+ path: "m/0'/0",
334
+ xPubKey:
335
+ "xpub6A7PsGUCo9qsn1jhZVB68WKWU9bTt1Wu7fzRqhczRbJ3u3xsF1bJmWBL1MvygTtrfmvNw1adLzmRjQHtDCJDXAHFa4K3wELpGGqEXL4e6d4",
336
+ },
337
+ {
338
+ path: "m/0'/0'",
339
+ xPubKey:
340
+ "xpub6A7PsGUM8pNqwy9AceVuFK6KxY88FhvFMRGP9fjEDKA3P4WpR1zyHH3Lmczj7eorx4RbDC4Qttd8C7HhLA2W9LsxxZzXo1DMCwJFb3zZKZ8",
341
+ },
342
+ {
343
+ path: "m/0'/0'/0'",
344
+ xPubKey:
345
+ "xpub6BiChRN7aqq51RA7RnAmKhqKdGckPncrHWLrj1xoj6ZMfdMJ1dX4Ysh9V3yEhpFCpC3BapjR83xPKY693XXTEU6qgWU3qZs78WBHA15uhYf",
346
+ },
347
+ {
348
+ path: "m/44'/0'/0'",
349
+ xPubKey:
350
+ "xpub6BosfCnifzxcFwrSzQiqu2DBVTshkCXacvNsWGYJVVhhawA7d4R5WSWGFNbi8Aw6ZRc1brxMyWMzG3DSSSSoekkudhUd9yLb6qx39T9nMdj",
351
+ },
352
+ {
353
+ path: "m/44'/0'/0'/0",
354
+ xPubKey:
355
+ "xpub6ELHKXNimKbxMCytPh7EdC2QXx46T9qLDJWGnTraz1H9kMMFdcduoU69wh9cxP12wDxqAAfbaESWGYt5rREsX1J8iR2TEunvzvddduAPYcY",
356
+ },
357
+ {
358
+ path: "m/44'/145'/0'",
359
+ xPubKey:
360
+ "xpub6ByHsPNSQXTWZ7PLESMY2FufyYWtLXagSUpMQq7Un96SiThZH2iJB1X7pwviH1WtKVeDP6K8d6xxFzzoaFzF3s8BKCZx8oEDdDkNnp4owAZ",
361
+ },
362
+ {
363
+ path: "m/44'/145'/0'/0",
364
+ xPubKey:
365
+ "xpub6F2iaK2JUPcgrZ6RTGH6t8VybLPu1XzfrHsDsaKvK6NfULznU6i6aw6ZoefDW2DpNruSLw73RwQg46qvpqB3eryeJJ2tkFCF4Z6gbr8Pjja",
366
+ },
367
+ {
368
+ path: "m/44'/245'/0",
369
+ xPubKey:
370
+ "xpub6Ch34ms5osevEtkEZX81n8EG4c6vgHWGH1gQXBG2uf2Tihb1eed4H1wozLfZB31mV9JD7mymYTQxcLKFFjZHdM5NGdH2Ud1ksSkfwSFjjCg",
371
+ },
372
+ {
373
+ path: "m/44'/245'/0'",
374
+ xPubKey:
375
+ "xpub6Ch34msE9YBtQV7pZrLyRXHwocrpJtNN4KDG8bbyxyhGmEM5MirtqkiH4h9dvnVJ3MekET3w2Fkvej3fyo8WLz9bRPyDynDf6NXNfuydhv1",
376
+ },
377
+ {
378
+ path: "m/44'/245'/0'/0",
379
+ xPubKey:
380
+ "xpub6FFeETss5Zwkw78NDAibKEaGxigU3bgYzLihcqbQqqTyb6jorR9mgR9AexYydxmiPU8koAf5ndaQPjPWK3sDz1wjBjf2TkLbD982S9PWd9Z",
381
+ },
382
+ ]);
383
+ });
384
+ test("Expect '11x abandon about' to return 'protected' for root path", async () => {
385
+ expect.assertions(1);
386
+ try {
387
+ let w = await Wallet.fromId(
388
+ "seed:mainnet:abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
389
+ );
390
+ let commonPaths = await w.getXPubKeys(["m"]);
391
+ } catch (e: any) {
392
+ expect(e.message).toBe(
393
+ "Storing or sharing of parent public key may lead to loss of funds. Storing or sharing *root* parent public keys is strongly discouraged, although all parent keys have risk. See: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#implications"
394
+ );
395
+ }
396
+ });
397
+ });
398
+
229
399
  describe(`Watch only Wallets`, () => {
230
400
  test("Create a watch only testnet wallet from string id", async () => {
231
401
  let w = await TestNetWallet.fromId(
@@ -242,6 +412,8 @@ describe(`Watch only Wallets`, () => {
242
412
  isTestnet: true,
243
413
  name: "",
244
414
  network: "testnet",
415
+ parentDerivationPath: undefined,
416
+ parentXPubKey: undefined,
245
417
  privateKey: undefined,
246
418
  privateKeyWif: undefined,
247
419
  publicKey: undefined,
package/src/wallet/Wif.ts CHANGED
@@ -1,11 +1,18 @@
1
1
  //#region Imports
2
2
  // Stable
3
- import { instantiateSecp256k1, instantiateSha256 } from "@bitauth/libauth";
3
+ import {
4
+ deriveHdPublicNodeIdentifier,
5
+ encodeHdPublicKey,
6
+ HdKeyNetwork,
7
+ instantiateSecp256k1,
8
+ instantiateSha256,
9
+ } from "@bitauth/libauth";
4
10
 
5
11
  // Unstable?
6
12
  import {
7
13
  binToHex,
8
14
  CashAddressNetworkPrefix,
15
+ deriveHdPublicNode,
9
16
  decodePrivateKeyWif,
10
17
  encodePrivateKeyWif,
11
18
  deriveHdPrivateNodeFromSeed,
@@ -40,6 +47,7 @@ import {
40
47
  SendResponse,
41
48
  UtxoItem,
42
49
  UtxoResponse,
50
+ XPubKey,
43
51
  } from "./model";
44
52
 
45
53
  import {
@@ -92,7 +100,8 @@ import { generateRandomBytes } from "../util/randomBytes";
92
100
  import { SignedMessageI, SignedMessage } from "../message";
93
101
  import ElectrumNetworkProvider from "../network/ElectrumNetworkProvider";
94
102
  import { amountInSatoshi } from "../util/amountInSatoshi";
95
- import { DUST_UTXO_THRESHOLD } from "../constant";
103
+ import { getXPubKey } from "../util/getXPubKey";
104
+ import { DERIVATION_PATHS, DUST_UTXO_THRESHOLD } from "../constant";
96
105
 
97
106
  //#endregion Imports
98
107
 
@@ -105,6 +114,8 @@ const sha256Promise = instantiateSha256();
105
114
  export class Wallet extends BaseWallet {
106
115
  cashaddr?: string;
107
116
  derivationPath: string = "m/44'/0'/0'/0/0";
117
+ parentDerivationPath: string = "m/44'/0'/0'";
118
+ parentXPubKey?: string;
108
119
  privateKey?: Uint8Array;
109
120
  publicKeyCompressed?: Uint8Array;
110
121
  privateKeyWif?: string;
@@ -186,6 +197,10 @@ export class Wallet extends BaseWallet {
186
197
  network: this.network as any,
187
198
  seed: this.mnemonic ? this.getSeed().seed : undefined,
188
199
  derivationPath: this.mnemonic ? this.getSeed().derivationPath : undefined,
200
+ parentDerivationPath: this.mnemonic
201
+ ? this.getSeed().parentDerivationPath
202
+ : undefined,
203
+ parentXPubKey: this.parentXPubKey ? this.parentXPubKey : undefined,
189
204
  publicKey: this.publicKey ? binToHex(this.publicKey!) : undefined,
190
205
  publicKeyHash: binToHex(this.publicKeyHash!),
191
206
  privateKey: this.privateKey ? binToHex(this.privateKey!) : undefined,
@@ -319,19 +334,25 @@ export class Wallet extends BaseWallet {
319
334
  }
320
335
 
321
336
  private async _generateMnemonic() {
322
- const crypto = await instantiateBIP32Crypto();
323
337
  this.mnemonic = generateMnemonic();
324
338
  let seed = mnemonicToSeedSync(this.mnemonic!);
339
+ let network = this.isTestnet ? "testnet" : "mainnet";
340
+ this.parentXPubKey = await getXPubKey(
341
+ seed,
342
+ this.parentDerivationPath,
343
+ network
344
+ );
345
+
346
+ const crypto = await instantiateBIP32Crypto();
325
347
  let hdNode = deriveHdPrivateNodeFromSeed(crypto, seed);
326
348
  if (!hdNode.valid) {
327
349
  throw Error("Invalid private key derived from mnemonic seed");
328
350
  }
329
351
 
330
- let zerothChild = deriveHdPath(
331
- crypto,
332
- hdNode,
333
- this.derivationPath
334
- ) as HdPrivateNodeValid;
352
+ let zerothChild = deriveHdPath(crypto, hdNode, this.derivationPath);
353
+ if (typeof zerothChild === "string") {
354
+ throw Error(zerothChild);
355
+ }
335
356
  this.privateKey = zerothChild.privateKey;
336
357
 
337
358
  this.walletType = WalletTypeEnum.Seed;
@@ -353,6 +374,18 @@ export class Wallet extends BaseWallet {
353
374
  return super.fromId(walletId);
354
375
  };
355
376
 
377
+ public async getXPubKeys(paths?) {
378
+ if (this.mnemonic) {
379
+ if (paths) {
380
+ let xPubKeys = await this.deriveHdPaths(paths);
381
+ return [xPubKeys];
382
+ } else {
383
+ return await this.deriveHdPaths(DERIVATION_PATHS);
384
+ }
385
+ } else {
386
+ throw Error("xpubkeys can only be derived from seed type wallets.");
387
+ }
388
+ }
356
389
  // Initialize wallet from a mnemonic phrase
357
390
  protected async fromSeed(
358
391
  mnemonic: string,
@@ -369,20 +402,73 @@ export class Wallet extends BaseWallet {
369
402
  }
370
403
  if (derivationPath) {
371
404
  this.derivationPath = derivationPath;
405
+
406
+ // If the derivation path is for the first account child, set the parent derivation path
407
+ let path = derivationPath.split("/");
408
+ if (path.slice(-2).join("/") == "0/0") {
409
+ this.parentDerivationPath = path.slice(0, -2).join("/");
410
+ }
372
411
  }
373
412
 
374
- let zerothChild = deriveHdPath(
375
- crypto,
376
- hdNode,
377
- this.derivationPath
378
- ) as HdPrivateNodeValid;
413
+ let zerothChild = deriveHdPath(crypto, hdNode, this.derivationPath);
414
+ if (typeof zerothChild === "string") {
415
+ throw Error(zerothChild);
416
+ }
379
417
  this.privateKey = zerothChild.privateKey;
380
418
 
419
+ let network = this.isTestnet ? "testnet" : "mainnet";
420
+ this.parentXPubKey = await getXPubKey(
421
+ seed,
422
+ this.parentDerivationPath,
423
+ network
424
+ );
425
+
381
426
  this.walletType = WalletTypeEnum.Seed;
382
427
  await this.deriveInfo();
383
428
  return this;
384
429
  }
385
430
 
431
+ // Get common xpub paths from zerothChild privateKey
432
+ public async deriveHdPaths(hdPaths: string[]): Promise<any[]> {
433
+ const crypto = await instantiateBIP32Crypto();
434
+ let seed = mnemonicToSeedSync(this.mnemonic!);
435
+ let hdNode = deriveHdPrivateNodeFromSeed(crypto, seed);
436
+ if (!hdNode.valid) {
437
+ throw Error("Invalid private key derived from mnemonic seed");
438
+ }
439
+
440
+ let result: any[] = [];
441
+
442
+ for (const path of hdPaths) {
443
+ if (path === "m") {
444
+ throw Error(
445
+ "Storing or sharing of parent public key may lead to loss of funds. Storing or sharing *root* parent public keys is strongly discouraged, although all parent keys have risk. See: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#implications"
446
+ );
447
+ }
448
+ let childNode = deriveHdPath(crypto, hdNode, path);
449
+ if (typeof childNode === "string") {
450
+ throw Error(childNode);
451
+ }
452
+ let node = deriveHdPublicNode(crypto, childNode);
453
+ if (typeof node === "string") {
454
+ throw Error(node);
455
+ }
456
+ let xPubKey = encodeHdPublicKey(crypto, {
457
+ network: this.network as HdKeyNetwork,
458
+ node: node,
459
+ });
460
+ let key = new XPubKey({
461
+ path: path,
462
+ xPubKey: xPubKey,
463
+ });
464
+
465
+ result.push(await key.ready());
466
+ }
467
+ return await Promise.all(result).then((result) => {
468
+ return result;
469
+ });
470
+ }
471
+
386
472
  // Initialize a watch only wallet from a cash addr
387
473
  protected async watchOnly(address: string): Promise<this> {
388
474
  this.walletType = WalletTypeEnum.Watch;
@@ -417,8 +503,7 @@ export class Wallet extends BaseWallet {
417
503
  const sha256 = await sha256Promise;
418
504
  let wifResult = decodePrivateKeyWif(sha256, secret);
419
505
 
420
- const hasError = typeof wifResult === "string";
421
- if (hasError) {
506
+ if (typeof wifResult === "string") {
422
507
  throw Error(wifResult as string);
423
508
  }
424
509
  let resultData: PrivateKeyI = wifResult as PrivateKeyI;
@@ -66,6 +66,8 @@ describe(`Named Wallets`, () => {
66
66
  isTestnet: true,
67
67
  name: "",
68
68
  network: "testnet",
69
+ parentDerivationPath: undefined,
70
+ parentXPubKey: undefined,
69
71
  privateKey: undefined,
70
72
  privateKeyWif: undefined,
71
73
  publicKey: undefined,
@@ -23,6 +23,7 @@ export interface WalletResponseI {
23
23
  privkey?: string;
24
24
  seed?: string;
25
25
  derivationPath?: string;
26
+ parentDerivationPath?: string;
26
27
  }
27
28
 
28
29
  export interface WalletInfoI {
@@ -32,6 +33,8 @@ export interface WalletInfoI {
32
33
  network: NetworkEnum;
33
34
  seed?: string;
34
35
  derivationPath?: string;
36
+ parentDerivationPath?: string;
37
+ parentXPubKey?: string;
35
38
  publicKey?: string;
36
39
  publicKeyHash?: string;
37
40
  privateKey?: string;
@@ -52,6 +55,7 @@ export interface SendRequestOptionsI {
52
55
  export interface MnemonicI {
53
56
  seed: string;
54
57
  derivationPath: string;
58
+ parentDerivationPath: string;
55
59
  }
56
60
 
57
61
  export interface WalletI {
@@ -159,3 +159,25 @@ export class SendResponse {
159
159
  this.explorerUrl = explorerUrl;
160
160
  }
161
161
  }
162
+
163
+ export class XPubKey {
164
+ path: string;
165
+ xPubKey: string;
166
+
167
+ constructor({ path, xPubKey }: { path: string; xPubKey: string }) {
168
+ this.path = path;
169
+ this.xPubKey = xPubKey;
170
+ }
171
+
172
+ public async ready() {
173
+ await this.xPubKey;
174
+ return this.asObject();
175
+ }
176
+
177
+ public asObject() {
178
+ return {
179
+ path: this.path,
180
+ xPubKey: this.xPubKey,
181
+ };
182
+ }
183
+ }