mainnet-js 0.4.29 → 0.4.33

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 +31 -10
  18. package/dist/main/wallet/Wif.js +173 -78
  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.33.js +2 -0
  25. package/dist/{mainnet-0.4.29.js.LICENSE.txt → mainnet-0.4.33.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 +31 -10
  42. package/dist/module/wallet/Wif.js +177 -82
  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 +191 -2
  58. package/src/wallet/Wif.ts +267 -133
  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.29.js +0 -2
package/src/constant.ts CHANGED
@@ -8,3 +8,20 @@ export const MEMPOOL_CHAIN_LIMIT = 50;
8
8
 
9
9
  // time in milliseconds to cache the usd exchange rate
10
10
  export const EXCHANGE_RATE_TTL = 250000;
11
+
12
+ // list of common derivation paths
13
+ // a la: https://github.com/Electron-Cash/Electron-Cash/blob/1de24c509992cfebc22217a2a77c862c2b02bc54/electroncash_gui/qt/installwizard.py#L624
14
+ export const DERIVATION_PATHS = [
15
+ "m/0",
16
+ "m/0'",
17
+ "m/0'/0",
18
+ "m/0'/0'",
19
+ "m/0'/0'/0'",
20
+ "m/44'/0'/0'",
21
+ "m/44'/0'/0'/0",
22
+ "m/44'/145'/0'",
23
+ "m/44'/145'/0'/0",
24
+ "m/44'/245'/0",
25
+ "m/44'/245'/0'",
26
+ "m/44'/245'/0'/0",
27
+ ];
@@ -0,0 +1,115 @@
1
+ import {
2
+ derivePublicNodeCashaddr,
3
+ getAddrsByXpubKey,
4
+ getXpubKeyInfo,
5
+ } from "./getAddrsByXpubKey";
6
+
7
+ test("Should decode xpubInfo", async () => {
8
+ let xpub =
9
+ "xpub6BosfCnifzxcFwrSzQiqu2DBVTshkCXacvNsWGYJVVhhawA7d4R5WSWGFNbi8Aw6ZRc1brxMyWMzG3DSSSSoekkudhUd9yLb6qx39T9nMdj";
10
+ let xpubInfo = await getXpubKeyInfo(xpub);
11
+ expect(xpubInfo.version).toBe("mainnet");
12
+ expect(xpubInfo.depth).toBe(3);
13
+ expect(xpubInfo.parentFingerprint).toBe("155bca59");
14
+ expect(xpubInfo.childNumber).toBe(2147483648);
15
+ expect(xpubInfo.chain).toBe(
16
+ "3da4bc190a2680111d31fadfdc905f2a7f6ce77c6f109919116f253d43445219"
17
+ );
18
+ expect(xpubInfo.data).toBe(
19
+ "03774c910fcf07fa96886ea794f0d5caed9afe30b44b83f7e213bb92930e7df4bd"
20
+ );
21
+ expect(xpubInfo.fingerprint).toBe("6cc9f252");
22
+ });
23
+
24
+ test("Should derive cashaddr given xpub and path", async () => {
25
+ // abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about
26
+ // m/0
27
+ const xpub =
28
+ "xpub6F2iaK2JUPcgrZ6RTGH6t8VybLPu1XzfrHsDsaKvK6NfULznU6i6aw6ZoefDW2DpNruSLw73RwQg46qvpqB3eryeJJ2tkFCF4Z6gbr8Pjja";
29
+ const vectors = [
30
+ // M/44'/145'/0'/0/0
31
+ "bitcoincash:qqyx49mu0kkn9ftfj6hje6g2wfer34yfnq5tahq3q6",
32
+ // M/44'/145'/0'/0/1
33
+ "bitcoincash:qp8sfdhgjlq68hlzka9lcsxtcnvuvnd0xqxugfzzc5",
34
+ // M/44'/145'/0'/0/2
35
+ "bitcoincash:qqkuy34ntrye9a2h4xpdstcu4aq5wfrwscjtaphenr",
36
+ ];
37
+
38
+ for (let i = 0; i < vectors.length; i++) {
39
+ // M/44'/145'/0'/0/
40
+ const publicNode = await derivePublicNodeCashaddr(xpub, 0, `M/${i}`);
41
+ expect(publicNode).toBe(vectors[i]);
42
+ }
43
+ });
44
+
45
+ test("Should derive list of cashaddrs from m/44'/0'/0' given xpub path, & limit", async () => {
46
+ // abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about
47
+ // m/44'/0'/0'
48
+ const xpub =
49
+ "xpub6BosfCnifzxcFwrSzQiqu2DBVTshkCXacvNsWGYJVVhhawA7d4R5WSWGFNbi8Aw6ZRc1brxMyWMzG3DSSSSoekkudhUd9yLb6qx39T9nMdj";
50
+ const vectors = [
51
+ // m/44'/0'/0'/0/0
52
+ "bitcoincash:qrvcdmgpk73zyfd8pmdl9wnuld36zh9n4gms8s0u59",
53
+ // m/44'/0'/0'/0/1
54
+ "bitcoincash:qp4wzvqu73x22ft4r5tk8tz0aufdz9fescwtpcmhc7",
55
+ // m/44'/0'/0'/0/2
56
+ "bitcoincash:qr0kwqzf2h3wvjjhn4pg895lrxwp96wqgyhkksq2nh",
57
+ ];
58
+
59
+ // M/44'/0'/0'/0/i
60
+ const cashaddrs = await getAddrsByXpubKey(xpub, "0/0", 3);
61
+ expect(cashaddrs).toStrictEqual(vectors);
62
+ });
63
+
64
+ test("Should derive list of change cashaddrs from m/44'/0'/0' given xpub path, & limit", async () => {
65
+ // abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about
66
+ // m/44'/0'/0'
67
+ const xpub =
68
+ "xpub6BosfCnifzxcFwrSzQiqu2DBVTshkCXacvNsWGYJVVhhawA7d4R5WSWGFNbi8Aw6ZRc1brxMyWMzG3DSSSSoekkudhUd9yLb6qx39T9nMdj";
69
+ const vectors = [
70
+ // m/44'/0'/0'/1/0
71
+ "bitcoincash:qzawj0yw07mgys3dy3uqkxsj54gwlapg7gjw8v3grk",
72
+ // m/44'/0'/0'/1/1
73
+ "bitcoincash:qqsqv86eghvyxcq7q5eg3l3ad7r7xcff65egddy9e9",
74
+ // m/44'/0'/0'/1/2
75
+ "bitcoincash:qrdexhxheryn7n2kf2s7g9kypfe0ynakrqm3j0f69w",
76
+ ];
77
+
78
+ // M/44'/0'/0'/0/i
79
+ const cashaddrs = await getAddrsByXpubKey(xpub, "1/0", 3);
80
+ expect(cashaddrs).toStrictEqual(vectors);
81
+ });
82
+
83
+ test("Should derive list of cashaddrs from M/44'/145'/0' given xpub, path and limit", async () => {
84
+ // abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about
85
+ // m/0
86
+ const xpub =
87
+ "xpub6ByHsPNSQXTWZ7PLESMY2FufyYWtLXagSUpMQq7Un96SiThZH2iJB1X7pwviH1WtKVeDP6K8d6xxFzzoaFzF3s8BKCZx8oEDdDkNnp4owAZ";
88
+ const vectors = [
89
+ // M/44'/145'/0'/0/0
90
+ "bitcoincash:qqyx49mu0kkn9ftfj6hje6g2wfer34yfnq5tahq3q6",
91
+ // M/44'/145'/0'/0/1
92
+ "bitcoincash:qp8sfdhgjlq68hlzka9lcsxtcnvuvnd0xqxugfzzc5",
93
+ // M/44'/145'/0'/0/2
94
+ "bitcoincash:qqkuy34ntrye9a2h4xpdstcu4aq5wfrwscjtaphenr",
95
+ ];
96
+
97
+ const cashaddrs = await getAddrsByXpubKey(xpub, "0/0", 3);
98
+ expect(cashaddrs).toStrictEqual(vectors);
99
+ });
100
+ test("Should derive list of change cashaddrs from M/44'/145'/0' given xpub, path and limit", async () => {
101
+ // abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about
102
+ // m/0
103
+ const xpub =
104
+ "xpub6ByHsPNSQXTWZ7PLESMY2FufyYWtLXagSUpMQq7Un96SiThZH2iJB1X7pwviH1WtKVeDP6K8d6xxFzzoaFzF3s8BKCZx8oEDdDkNnp4owAZ";
105
+ const vectors = [
106
+ // M/44'/145'/0'/1/0
107
+ "bitcoincash:qr8aeharupyrmhfu0d4tdmsnc5y8cfk47y6qrsjsrx",
108
+ // M/44'/145'/0'/1/1
109
+ "bitcoincash:qr88m3rp5nd5aerz5rh9lzly9u5pevykagwscmjk0c",
110
+ // M/44'/145'/0'/1/2
111
+ "bitcoincash:qp57cex47jtyhedphe2c63gpzsx3zs0ryvejxly47d",
112
+ ];
113
+ const cashaddrs = await getAddrsByXpubKey(xpub, "1/0", 3);
114
+ expect(cashaddrs).toStrictEqual(vectors);
115
+ });
@@ -0,0 +1,98 @@
1
+ import {
2
+ deriveHdPublicNodeChild,
3
+ decodeHdPublicKey,
4
+ encodeHdPublicKey,
5
+ encodeCashAddress,
6
+ instantiateBIP32Crypto,
7
+ deriveHdPath,
8
+ HdPublicNode,
9
+ HdKeyNetwork,
10
+ binToHex,
11
+ } from "@bitauth/libauth";
12
+ import path from "path";
13
+
14
+ import { hash160 } from "./hash160";
15
+
16
+ export async function getAddrsByXpubKey(
17
+ xpub: string,
18
+ path: string,
19
+ count: number
20
+ ) {
21
+ let pathComponents = path.split("/");
22
+ let rootStr = pathComponents.shift()!;
23
+ let root: number;
24
+ if (rootStr === "M" || rootStr === "m") {
25
+ rootStr = pathComponents.shift()!;
26
+ }
27
+ root = parseInt(rootStr);
28
+ let result: any = [];
29
+
30
+ const start = parseInt(pathComponents.pop()!);
31
+ const end = start + count;
32
+ for (let curr = start; curr < end; curr++) {
33
+ let childPath = ["M", root, ...pathComponents, curr].join("/");
34
+ result.push(derivePublicNodeCashaddr(xpub, root, childPath));
35
+ }
36
+ return await Promise.all(result).then((result) => {
37
+ return result;
38
+ });
39
+ }
40
+
41
+ export async function getAddrsByXpubKeyObject(obj) {
42
+ return await getAddrsByXpubKey(obj.xpubkey, obj.path, obj.count);
43
+ }
44
+
45
+ export async function derivePublicNodeCashaddr(
46
+ xpub,
47
+ index: number,
48
+ path?: string
49
+ ) {
50
+ const crypto = await instantiateBIP32Crypto();
51
+ const publicParent = await decodeHdPublicKey(crypto, xpub);
52
+
53
+ if (typeof publicParent === "string") {
54
+ throw new Error(publicParent);
55
+ }
56
+ let prefix = publicParent.network === "mainnet" ? "bitcoincash" : "bchtest";
57
+
58
+ let node = await deriveHdPublicNodeChild(crypto, publicParent.node, index);
59
+ if (typeof node === "string") {
60
+ throw new Error(node);
61
+ }
62
+
63
+ let cashaddr;
64
+ if (typeof path === "string") {
65
+ if (path[0] !== "M") {
66
+ throw Error("use M for public path derivation");
67
+ }
68
+ let childNode = deriveHdPath(crypto, publicParent.node, path);
69
+ if (typeof childNode === "string") {
70
+ throw new Error(childNode);
71
+ } else {
72
+ let childPkh = await hash160(childNode.publicKey);
73
+ cashaddr = encodeCashAddress(prefix, 0, childPkh);
74
+ }
75
+ }
76
+ return cashaddr;
77
+ }
78
+
79
+ export async function getXpubKeyInfo(hdPublicKey) {
80
+ const crypto = await instantiateBIP32Crypto();
81
+ let node = decodeHdPublicKey(crypto, hdPublicKey);
82
+ if (typeof node === "string") {
83
+ throw new Error(node);
84
+ }
85
+ return {
86
+ version: node.network,
87
+ depth: node.node.depth,
88
+ parentFingerprint: binToHex(node.node.parentFingerprint),
89
+ childNumber: node.node.childIndex,
90
+ chain: binToHex(node.node.chainCode),
91
+ data: binToHex(node.node.publicKey),
92
+ fingerprint: binToHex((await hash160(node.node.publicKey)).slice(0, 4)),
93
+ };
94
+ }
95
+
96
+ export async function getXpubKeyInfoObject(obj) {
97
+ return await getXpubKeyInfo(obj.xpubkey);
98
+ }
@@ -0,0 +1,36 @@
1
+ import {
2
+ deriveHdPrivateNodeFromSeed,
3
+ deriveHdPath,
4
+ deriveHdPublicNode,
5
+ encodeHdPublicKey,
6
+ HdKeyNetwork,
7
+ instantiateBIP32Crypto,
8
+ hexToBin,
9
+ } from "@bitauth/libauth";
10
+
11
+ export async function getXPubKey(
12
+ seed: Uint8Array | string,
13
+ derivationPath: string,
14
+ network: string
15
+ ) {
16
+ if (typeof seed === "string") {
17
+ seed = hexToBin(seed);
18
+ }
19
+ const crypto = await instantiateBIP32Crypto();
20
+ let hdNode = deriveHdPrivateNodeFromSeed(crypto, seed);
21
+ if (!hdNode.valid) {
22
+ throw Error("Invalid private key derived from mnemonic seed");
23
+ }
24
+
25
+ let node = deriveHdPath(crypto, hdNode, derivationPath);
26
+ if (typeof node === "string") {
27
+ throw node;
28
+ }
29
+ let parentPublicNode = deriveHdPublicNode(crypto, node);
30
+
31
+ let xPubKey = encodeHdPublicKey(crypto, {
32
+ network: network as HdKeyNetwork,
33
+ node: parentPublicNode,
34
+ });
35
+ return xPubKey;
36
+ }
package/src/util/index.ts CHANGED
@@ -1,16 +1,23 @@
1
1
  export { amountInSatoshi } from "./amountInSatoshi";
2
2
  export { asSendRequestObject } from "./asSendRequestObject";
3
- export { btoa, atob } from "./base64";
3
+ export { atob, btoa } from "./base64";
4
4
  export { convert, convertObject } from "./convert";
5
5
  export { delay } from "./delay";
6
+ export { derivedNetwork } from "./deriveNetwork";
7
+ export { derivePublicKeyHash } from "./derivePublicKeyHash";
8
+ export {
9
+ getAddrsByXpubKey,
10
+ getAddrsByXpubKeyObject,
11
+ getXpubKeyInfo,
12
+ getXpubKeyInfoObject,
13
+ } from "../util/getAddrsByXpubKey";
6
14
  export { getRuntimePlatform, RuntimePlatform } from "./getRuntimePlatform";
7
15
  export { getUsdRate } from "./getUsdRate";
8
16
  export { ExchangeRate } from "../rate/ExchangeRate";
9
- export { derivedNetwork } from "./deriveNetwork";
10
- export { derivePublicKeyHash } from "./derivePublicKeyHash";
11
17
  export { sanitizeAddress } from "./sanitizeAddress";
12
18
  export { sanitizeUnit } from "./sanitizeUnit";
13
19
  export { getRandomInt } from "./randomInt";
20
+ export { getXPubKey } from "../util/getXPubKey";
14
21
  import * as randomValues from "./randomValues";
15
22
  export { randomValues };
16
23
  export { sumUtxoValue } from "./sumUtxoValue";
@@ -14,6 +14,8 @@ import { WalletTypeEnum } from "./enum";
14
14
  export class BaseWallet implements WalletI {
15
15
  provider?: any;
16
16
  derivationPath: string = "m/44'/0'/0'/0/0";
17
+ parentDerivationPath: string = "m/44'/0'/0'";
18
+ parentXPubKey?: string;
17
19
  mnemonic?: string;
18
20
  address?: string;
19
21
  privateKey?: any;
@@ -69,6 +71,7 @@ export class BaseWallet implements WalletI {
69
71
  return {
70
72
  seed: this.mnemonic,
71
73
  derivationPath: this.derivationPath,
74
+ parentDerivationPath: this.parentDerivationPath,
72
75
  };
73
76
  }
74
77
  //#endregion Accessors
@@ -182,7 +185,7 @@ export class BaseWallet implements WalletI {
182
185
  /**
183
186
  * replaceNamed - replace (recover) named wallet with a new walletId
184
187
  *
185
- * If wallet with a provided name does not exist yet, it will be creted with a `walletId` supplied
188
+ * If wallet with a provided name does not exist yet, it will be created with a `walletId` supplied
186
189
  * If wallet exists it will be overwritten without exception
187
190
  *
188
191
  * @param name user friendly wallet alias
@@ -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,
@@ -311,7 +483,6 @@ describe(`Watch only Wallets`, () => {
311
483
  let aliceBalance = await alice.send([
312
484
  { cashaddr: alice.cashaddr!, value: 526, unit: "sat" },
313
485
  ]);
314
- expect(aliceBalance.explorerUrl!).toContain("explorer.bitcoin.com");
315
486
  expect(aliceBalance.balance!.sat!).toBeGreaterThan(5000);
316
487
  }
317
488
  });
@@ -329,6 +500,24 @@ describe(`Watch only Wallets`, () => {
329
500
  expect(aliceBalance.sat).toBeGreaterThan(2000);
330
501
  }
331
502
  });
503
+
504
+ test("Should get last transaction", async () => {
505
+ const aliceWif = `wif:regtest:${process.env.PRIVATE_WIF!}`;
506
+ const aliceWallet = await RegTestWallet.fromId(aliceWif);
507
+ const bobWallet = await RegTestWallet.newRandom();
508
+
509
+ expect(await bobWallet.getLastTransaction()).toBeNull();
510
+
511
+ await aliceWallet.send([
512
+ {
513
+ cashaddr: bobWallet.cashaddr!,
514
+ value: 2000,
515
+ unit: "satoshis",
516
+ },
517
+ ]);
518
+
519
+ expect(await bobWallet.getLastTransaction()).not.toBeNull();
520
+ });
332
521
  });
333
522
  describe(`Wallet subscriptions`, () => {
334
523
  test("Should wait for transaction", async () => {