@ledgerhq/hw-app-btc 10.16.0-nightly.20260115024415 → 10.16.0-nightly.20260116124336

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 (120) hide show
  1. package/CHANGELOG.md +5 -3
  2. package/README.md +106 -56
  3. package/lib/Btc.d.ts +37 -0
  4. package/lib/Btc.d.ts.map +1 -1
  5. package/lib/Btc.js +30 -2
  6. package/lib/Btc.js.map +1 -1
  7. package/lib/BtcNew.d.ts +84 -0
  8. package/lib/BtcNew.d.ts.map +1 -1
  9. package/lib/BtcNew.js +326 -9
  10. package/lib/BtcNew.js.map +1 -1
  11. package/lib/createTransaction.d.ts.map +1 -1
  12. package/lib/createTransaction.js +3 -2
  13. package/lib/createTransaction.js.map +1 -1
  14. package/lib/getTrustedInputBIP143.d.ts +1 -2
  15. package/lib/getTrustedInputBIP143.d.ts.map +1 -1
  16. package/lib/getTrustedInputBIP143.js +1 -1
  17. package/lib/getTrustedInputBIP143.js.map +1 -1
  18. package/lib/newops/accounttype.d.ts +3 -3
  19. package/lib/newops/accounttype.d.ts.map +1 -1
  20. package/lib/newops/accounttype.js +15 -14
  21. package/lib/newops/accounttype.js.map +1 -1
  22. package/lib/newops/appClient.d.ts +1 -1
  23. package/lib/newops/appClient.d.ts.map +1 -1
  24. package/lib/newops/clientCommands.js +2 -2
  25. package/lib/newops/clientCommands.js.map +1 -1
  26. package/lib/newops/merkelizedPsbt.d.ts +1 -1
  27. package/lib/newops/merkelizedPsbt.d.ts.map +1 -1
  28. package/lib/newops/merkelizedPsbt.js +1 -1
  29. package/lib/newops/merkelizedPsbt.js.map +1 -1
  30. package/lib/newops/policy.js +2 -2
  31. package/lib/newops/policy.js.map +1 -1
  32. package/lib/newops/psbtExtractor.d.ts +1 -1
  33. package/lib/newops/psbtExtractor.d.ts.map +1 -1
  34. package/lib/newops/psbtExtractor.js +3 -3
  35. package/lib/newops/psbtExtractor.js.map +1 -1
  36. package/lib/newops/psbtFinalizer.d.ts +1 -1
  37. package/lib/newops/psbtFinalizer.d.ts.map +1 -1
  38. package/lib/newops/psbtFinalizer.js +5 -6
  39. package/lib/newops/psbtFinalizer.js.map +1 -1
  40. package/lib/signP2SHTransaction.d.ts.map +1 -1
  41. package/lib/signP2SHTransaction.js +3 -2
  42. package/lib/signP2SHTransaction.js.map +1 -1
  43. package/lib-es/Btc.d.ts +37 -0
  44. package/lib-es/Btc.d.ts.map +1 -1
  45. package/lib-es/Btc.js +30 -2
  46. package/lib-es/Btc.js.map +1 -1
  47. package/lib-es/BtcNew.d.ts +84 -0
  48. package/lib-es/BtcNew.d.ts.map +1 -1
  49. package/lib-es/BtcNew.js +325 -8
  50. package/lib-es/BtcNew.js.map +1 -1
  51. package/lib-es/createTransaction.d.ts.map +1 -1
  52. package/lib-es/createTransaction.js +3 -2
  53. package/lib-es/createTransaction.js.map +1 -1
  54. package/lib-es/getTrustedInputBIP143.d.ts +1 -2
  55. package/lib-es/getTrustedInputBIP143.d.ts.map +1 -1
  56. package/lib-es/getTrustedInputBIP143.js +1 -1
  57. package/lib-es/getTrustedInputBIP143.js.map +1 -1
  58. package/lib-es/newops/accounttype.d.ts +3 -3
  59. package/lib-es/newops/accounttype.d.ts.map +1 -1
  60. package/lib-es/newops/accounttype.js +11 -10
  61. package/lib-es/newops/accounttype.js.map +1 -1
  62. package/lib-es/newops/appClient.d.ts +1 -1
  63. package/lib-es/newops/appClient.d.ts.map +1 -1
  64. package/lib-es/newops/clientCommands.js +1 -1
  65. package/lib-es/newops/clientCommands.js.map +1 -1
  66. package/lib-es/newops/merkelizedPsbt.d.ts +1 -1
  67. package/lib-es/newops/merkelizedPsbt.d.ts.map +1 -1
  68. package/lib-es/newops/merkelizedPsbt.js +1 -1
  69. package/lib-es/newops/merkelizedPsbt.js.map +1 -1
  70. package/lib-es/newops/policy.js +1 -1
  71. package/lib-es/newops/policy.js.map +1 -1
  72. package/lib-es/newops/psbtExtractor.d.ts +1 -1
  73. package/lib-es/newops/psbtExtractor.d.ts.map +1 -1
  74. package/lib-es/newops/psbtExtractor.js +1 -1
  75. package/lib-es/newops/psbtExtractor.js.map +1 -1
  76. package/lib-es/newops/psbtFinalizer.d.ts +1 -1
  77. package/lib-es/newops/psbtFinalizer.d.ts.map +1 -1
  78. package/lib-es/newops/psbtFinalizer.js +1 -2
  79. package/lib-es/newops/psbtFinalizer.js.map +1 -1
  80. package/lib-es/signP2SHTransaction.d.ts.map +1 -1
  81. package/lib-es/signP2SHTransaction.js +3 -2
  82. package/lib-es/signP2SHTransaction.js.map +1 -1
  83. package/package.json +6 -6
  84. package/src/Btc.ts +41 -2
  85. package/src/BtcNew.ts +483 -9
  86. package/src/createTransaction.ts +4 -3
  87. package/src/getTrustedInputBIP143.ts +0 -2
  88. package/src/newops/accounttype.ts +11 -12
  89. package/src/newops/appClient.ts +1 -1
  90. package/src/newops/clientCommands.ts +1 -1
  91. package/src/newops/merkelizedPsbt.ts +1 -1
  92. package/src/newops/policy.ts +1 -1
  93. package/src/newops/psbtExtractor.ts +1 -2
  94. package/src/newops/psbtFinalizer.ts +1 -2
  95. package/src/signP2SHTransaction.ts +3 -2
  96. package/tests/Btc.test.ts +848 -20
  97. package/tests/newops/BtcNew.signMessage.test.ts +35 -0
  98. package/tests/newops/BtcNew.signPsbtBuffer.test.ts +391 -0
  99. package/tests/newops/BtcNew.test.ts +13 -1
  100. package/tests/newops/integrationtools.ts +1 -1
  101. package/lib/buffertools.d.ts +0 -31
  102. package/lib/buffertools.d.ts.map +0 -1
  103. package/lib/buffertools.js +0 -129
  104. package/lib/buffertools.js.map +0 -1
  105. package/lib/newops/psbtv2.d.ts +0 -150
  106. package/lib/newops/psbtv2.d.ts.map +0 -1
  107. package/lib/newops/psbtv2.js +0 -469
  108. package/lib/newops/psbtv2.js.map +0 -1
  109. package/lib-es/buffertools.d.ts +0 -31
  110. package/lib-es/buffertools.d.ts.map +0 -1
  111. package/lib-es/buffertools.js +0 -119
  112. package/lib-es/buffertools.js.map +0 -1
  113. package/lib-es/newops/psbtv2.d.ts +0 -150
  114. package/lib-es/newops/psbtv2.d.ts.map +0 -1
  115. package/lib-es/newops/psbtv2.js +0 -464
  116. package/lib-es/newops/psbtv2.js.map +0 -1
  117. package/src/buffertools.ts +0 -137
  118. package/src/newops/psbtv2.ts +0 -525
  119. package/tests/buffertools.test.ts +0 -25
  120. package/tests/newops/psbtv2.test.ts +0 -15
package/tests/Btc.test.ts CHANGED
@@ -1,36 +1,864 @@
1
+ import { openTransportReplayer, RecordStore } from "@ledgerhq/hw-transport-mocker";
1
2
  import Btc from "../src/Btc";
2
3
  import BtcNew from "../src/BtcNew";
3
4
  import BtcOld from "../src/BtcOld";
4
- import { getAppAndVersion } from "../src/getAppAndVersion";
5
+ import type { Transaction } from "../src/types";
6
+ import type { AddressFormat } from "../src/getWalletPublicKey";
5
7
 
8
+ // Mock the getAppAndVersion module
6
9
  jest.mock("../src/getAppAndVersion");
7
- jest.mock("../src/BtcNew");
8
- jest.mock("../src/BtcOld");
9
- jest.mock("../src/newops/appClient");
10
+
11
+ import { getAppAndVersion, checkIsBtcLegacy } from "../src/getAppAndVersion";
12
+
13
+ const mockedGetAppAndVersion = getAppAndVersion as jest.MockedFunction<typeof getAppAndVersion>;
14
+ const mockedCheckIsBtcLegacy = checkIsBtcLegacy as jest.MockedFunction<typeof checkIsBtcLegacy>;
10
15
 
11
16
  describe("Btc", () => {
12
- let mockTransport: any;
13
- let mockedGetAppAndVersion: jest.Mock;
17
+ let btc: Btc;
14
18
 
15
19
  beforeEach(() => {
16
20
  jest.clearAllMocks();
17
- mockTransport = {
18
- decorateAppAPIMethods: jest.fn(),
19
- };
20
- mockedGetAppAndVersion = jest.mocked(getAppAndVersion);
21
21
  });
22
22
 
23
- it("should use BtcNew for 'Bitcoin Recovery' app", async () => {
24
- mockedGetAppAndVersion.mockResolvedValue({
25
- name: "Bitcoin Recovery",
26
- version: "2.4.0",
23
+ const mockAppAndVersion = (name: string, version: string) => ({
24
+ name,
25
+ version,
26
+ flags: Buffer.from([]),
27
+ });
28
+
29
+ const SAMPLE_TX_HEX =
30
+ "01000000014ea60aeac5252c14291d428915bd7ccd1bfc4af009f4d4dc57ae597ed0420b71010000008a47304402201f36a12c240dbf9e566bc04321050b1984cd6eaf6caee8f02bb0bfec08e3354b022012ee2aeadcbbfd1e92959f57c15c1c6debb757b798451b104665aa3010569b49014104090b15bde569386734abf2a2b99f9ca6a50656627e77de663ca7325702769986cf26cc9dd7fdea0af432c8e2becc867c932e1b9dd742f2a108997c2252e2bdebffffffff0281b72e00000000001976a91472a5d75c8d2d0565b656a5232703b167d50d5a2b88aca0860100000000001976a9144533f5fb9b4817f713c48f0bfe96b9f50c476c9b88ac00000000";
31
+
32
+ describe("constructor", () => {
33
+ it("should create a Btc instance with default parameters", async () => {
34
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
35
+ btc = new Btc({ transport });
36
+
37
+ expect(btc).toBeInstanceOf(Btc);
38
+ // Default currency is bitcoin, which uses BtcNew
39
+ expect(btc["_impl"]).toBeInstanceOf(BtcNew);
40
+ });
41
+
42
+ it("should create a Btc instance with bitcoin currency", async () => {
43
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
44
+ btc = new Btc({ transport, currency: "bitcoin" });
45
+
46
+ expect(btc).toBeInstanceOf(Btc);
47
+ expect(btc["_impl"]).toBeInstanceOf(BtcNew);
48
+ });
49
+
50
+ it("should create a Btc instance with bitcoin_testnet currency", async () => {
51
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
52
+ btc = new Btc({ transport, currency: "bitcoin_testnet" });
53
+
54
+ expect(btc).toBeInstanceOf(Btc);
55
+ expect(btc["_impl"]).toBeInstanceOf(BtcNew);
56
+ });
57
+
58
+ it("should create a Btc instance with bitcoin_regtest currency", async () => {
59
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
60
+ btc = new Btc({ transport, currency: "bitcoin_regtest" });
61
+
62
+ expect(btc).toBeInstanceOf(Btc);
63
+ expect(btc["_impl"]).toBeInstanceOf(BtcNew);
64
+ });
65
+
66
+ it("should create a Btc instance with qtum currency", async () => {
67
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
68
+ btc = new Btc({ transport, currency: "qtum" });
69
+
70
+ expect(btc).toBeInstanceOf(Btc);
71
+ expect(btc["_impl"]).toBeInstanceOf(BtcNew);
72
+ });
73
+
74
+ it("should create a Btc instance with legacy currency (litecoin)", async () => {
75
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
76
+ btc = new Btc({ transport, currency: "litecoin" });
77
+
78
+ expect(btc).toBeInstanceOf(Btc);
79
+ expect(btc["_impl"]).toBeInstanceOf(BtcOld);
80
+ });
81
+
82
+ it("should accept custom scrambleKey parameter", async () => {
83
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
84
+ btc = new Btc({ transport, scrambleKey: "CUSTOM" });
85
+
86
+ expect(btc).toBeInstanceOf(Btc);
87
+ // Default currency is bitcoin, which uses BtcNew
88
+ expect(btc["_impl"]).toBeInstanceOf(BtcNew);
89
+ });
90
+ });
91
+
92
+ describe("getWalletXpub", () => {
93
+ it("should call changeImplIfNecessary and delegate to implementation", async () => {
94
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
95
+ btc = new Btc({ transport, currency: "bitcoin" });
96
+
97
+ const mockGetWalletXpub = jest.fn().mockResolvedValue("xpub...");
98
+ (BtcNew as jest.MockedClass<typeof BtcNew>).prototype.getWalletXpub = mockGetWalletXpub;
99
+
100
+ mockedGetAppAndVersion.mockResolvedValue(mockAppAndVersion("Bitcoin", "2.1.0"));
101
+
102
+ const result = await btc.getWalletXpub({ path: "44'/0'/0'", xpubVersion: 0x0488b21e });
103
+
104
+ expect(result).toBe("xpub...");
105
+ });
106
+
107
+ it("should work with different xpub versions", async () => {
108
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
109
+ btc = new Btc({ transport, currency: "bitcoin" });
110
+
111
+ const mockGetWalletXpub = jest.fn().mockResolvedValue("xpub...");
112
+ (BtcNew as jest.MockedClass<typeof BtcNew>).prototype.getWalletXpub = mockGetWalletXpub;
113
+
114
+ mockedGetAppAndVersion.mockResolvedValue(mockAppAndVersion("Bitcoin", "2.1.0"));
115
+
116
+ await btc.getWalletXpub({ path: "84'/0'/0'", xpubVersion: 0x04b24746 });
117
+
118
+ expect(mockGetWalletXpub).toHaveBeenCalledWith({
119
+ path: "84'/0'/0'",
120
+ xpubVersion: 0x04b24746,
121
+ });
122
+ });
123
+ });
124
+
125
+ describe("getWalletPublicKey", () => {
126
+ it("should get wallet public key with default options", async () => {
127
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
128
+ btc = new Btc({ transport, currency: "bitcoin" });
129
+
130
+ const mockResult = {
131
+ publicKey: "04...",
132
+ bitcoinAddress: "1...",
133
+ chainCode: "00...",
134
+ };
135
+ const mockGetWalletPublicKey = jest.fn().mockResolvedValue(mockResult);
136
+ (BtcNew as jest.MockedClass<typeof BtcNew>).prototype.getWalletPublicKey =
137
+ mockGetWalletPublicKey;
138
+
139
+ mockedGetAppAndVersion.mockResolvedValue(mockAppAndVersion("Bitcoin", "2.1.0"));
140
+
141
+ const result = await btc.getWalletPublicKey("44'/0'/0'/0/0");
142
+
143
+ expect(result).toEqual(mockResult);
144
+ expect(mockGetWalletPublicKey).toHaveBeenCalledWith("44'/0'/0'/0/0", {});
145
+ });
146
+
147
+ it("should get wallet public key with verify option", async () => {
148
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
149
+ btc = new Btc({ transport, currency: "bitcoin" });
150
+
151
+ const mockGetWalletPublicKey = jest.fn().mockResolvedValue({
152
+ publicKey: "04...",
153
+ bitcoinAddress: "1...",
154
+ chainCode: "00...",
155
+ });
156
+ (BtcNew as jest.MockedClass<typeof BtcNew>).prototype.getWalletPublicKey =
157
+ mockGetWalletPublicKey;
158
+
159
+ mockedGetAppAndVersion.mockResolvedValue(mockAppAndVersion("Bitcoin", "2.1.0"));
160
+
161
+ await btc.getWalletPublicKey("44'/0'/0'/0/0", { verify: true });
162
+
163
+ expect(mockGetWalletPublicKey).toHaveBeenCalledWith("44'/0'/0'/0/0", { verify: true });
164
+ });
165
+
166
+ it("should get wallet public key with format option", async () => {
167
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
168
+ btc = new Btc({ transport, currency: "bitcoin" });
169
+
170
+ const mockGetWalletPublicKey = jest.fn().mockResolvedValue({
171
+ publicKey: "04...",
172
+ bitcoinAddress: "3...",
173
+ chainCode: "00...",
174
+ });
175
+ (BtcNew as jest.MockedClass<typeof BtcNew>).prototype.getWalletPublicKey =
176
+ mockGetWalletPublicKey;
177
+
178
+ mockedGetAppAndVersion.mockResolvedValue(mockAppAndVersion("Bitcoin", "2.1.0"));
179
+
180
+ await btc.getWalletPublicKey("49'/0'/0'/0/0", { format: "p2sh" });
181
+
182
+ expect(mockGetWalletPublicKey).toHaveBeenCalledWith("49'/0'/0'/0/0", { format: "p2sh" });
183
+ });
184
+
185
+ it("should get wallet public key with bech32 format", async () => {
186
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
187
+ btc = new Btc({ transport, currency: "bitcoin" });
188
+
189
+ const mockGetWalletPublicKey = jest.fn().mockResolvedValue({
190
+ publicKey: "04...",
191
+ bitcoinAddress: "bc1...",
192
+ chainCode: "00...",
193
+ });
194
+ (BtcNew as jest.MockedClass<typeof BtcNew>).prototype.getWalletPublicKey =
195
+ mockGetWalletPublicKey;
196
+
197
+ mockedGetAppAndVersion.mockResolvedValue(mockAppAndVersion("Bitcoin", "2.1.0"));
198
+
199
+ await btc.getWalletPublicKey("84'/0'/0'/0/0", { format: "bech32" });
200
+
201
+ expect(mockGetWalletPublicKey).toHaveBeenCalledWith("84'/0'/0'/0/0", { format: "bech32" });
202
+ });
203
+
204
+ it("should get wallet public key with bech32m format", async () => {
205
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
206
+ btc = new Btc({ transport, currency: "bitcoin" });
207
+
208
+ const mockGetWalletPublicKey = jest.fn().mockResolvedValue({
209
+ publicKey: "04...",
210
+ bitcoinAddress: "bc1p...",
211
+ chainCode: "00...",
212
+ });
213
+ (BtcNew as jest.MockedClass<typeof BtcNew>).prototype.getWalletPublicKey =
214
+ mockGetWalletPublicKey;
215
+
216
+ mockedGetAppAndVersion.mockResolvedValue(mockAppAndVersion("Bitcoin", "2.1.0"));
217
+
218
+ await btc.getWalletPublicKey("86'/0'/0'/0/0", { format: "bech32m" });
219
+
220
+ expect(mockGetWalletPublicKey).toHaveBeenCalledWith("86'/0'/0'/0/0", { format: "bech32m" });
221
+ });
222
+
223
+ it("should get wallet public key with cashaddr format", async () => {
224
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
225
+ btc = new Btc({ transport, currency: "bitcoin" });
226
+
227
+ const mockGetWalletPublicKey = jest.fn().mockResolvedValue({
228
+ publicKey: "04...",
229
+ bitcoinAddress: "bitcoincash:...",
230
+ chainCode: "00...",
231
+ });
232
+ (BtcNew as jest.MockedClass<typeof BtcNew>).prototype.getWalletPublicKey =
233
+ mockGetWalletPublicKey;
234
+
235
+ mockedGetAppAndVersion.mockResolvedValue(mockAppAndVersion("Bitcoin", "2.1.0"));
236
+
237
+ await btc.getWalletPublicKey("44'/145'/0'/0/0", { format: "cashaddr" });
238
+
239
+ expect(mockGetWalletPublicKey).toHaveBeenCalledWith("44'/145'/0'/0/0", {
240
+ format: "cashaddr",
241
+ });
242
+ });
243
+
244
+ it("should handle deprecated signature with boolean verify", async () => {
245
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
246
+ btc = new Btc({ transport, currency: "bitcoin" });
247
+
248
+ const mockGetWalletPublicKey = jest.fn().mockResolvedValue({
249
+ publicKey: "04...",
250
+ bitcoinAddress: "1...",
251
+ chainCode: "00...",
252
+ });
253
+ (BtcNew as jest.MockedClass<typeof BtcNew>).prototype.getWalletPublicKey =
254
+ mockGetWalletPublicKey;
255
+
256
+ mockedGetAppAndVersion.mockResolvedValue(mockAppAndVersion("Bitcoin", "2.1.0"));
257
+
258
+ // Suppress console.warn for this test
259
+ const consoleWarnSpy = jest.spyOn(console, "warn").mockImplementation();
260
+
261
+ // @ts-expect-error Testing deprecated signature
262
+ await btc.getWalletPublicKey("44'/0'/0'/0/0", true, false);
263
+
264
+ expect(consoleWarnSpy).toHaveBeenCalledWith(expect.stringContaining("deprecated signature"));
265
+ expect(mockGetWalletPublicKey).toHaveBeenCalledWith("44'/0'/0'/0/0", {
266
+ verify: true,
267
+ format: "legacy",
268
+ });
269
+
270
+ consoleWarnSpy.mockRestore();
271
+ });
272
+ });
273
+
274
+ describe("signMessage", () => {
275
+ it("should sign a message", async () => {
276
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
277
+ btc = new Btc({ transport, currency: "bitcoin" });
278
+
279
+ const mockResult = { v: 27, r: "r...", s: "s..." };
280
+ const mockSignMessage = jest.fn().mockResolvedValue(mockResult);
281
+ (BtcNew as jest.MockedClass<typeof BtcNew>).prototype.signMessage = mockSignMessage;
282
+
283
+ mockedGetAppAndVersion.mockResolvedValue(mockAppAndVersion("Bitcoin", "2.1.0"));
284
+
285
+ const messageHex = Buffer.from("test message").toString("hex");
286
+ const result = await btc.signMessage("44'/0'/0'/0/0", messageHex);
287
+
288
+ expect(result).toEqual(mockResult);
289
+ expect(mockSignMessage).toHaveBeenCalledWith({
290
+ path: "44'/0'/0'/0/0",
291
+ messageHex,
292
+ });
293
+ });
294
+
295
+ it("should sign a message with different paths", async () => {
296
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
297
+ btc = new Btc({ transport, currency: "bitcoin" });
298
+
299
+ const mockResult = { v: 27, r: "r...", s: "s..." };
300
+ const mockSignMessage = jest.fn().mockResolvedValue(mockResult);
301
+ (BtcNew as jest.MockedClass<typeof BtcNew>).prototype.signMessage = mockSignMessage;
302
+
303
+ mockedGetAppAndVersion.mockResolvedValue(mockAppAndVersion("Bitcoin", "2.1.0"));
304
+
305
+ const messageHex = Buffer.from("test").toString("hex");
306
+ await btc.signMessage("84'/0'/0'/0/0", messageHex);
307
+
308
+ expect(mockSignMessage).toHaveBeenCalledWith({
309
+ path: "84'/0'/0'/0/0",
310
+ messageHex,
311
+ });
312
+ });
313
+ });
314
+
315
+ describe("createPaymentTransaction", () => {
316
+ it("should create a payment transaction", async () => {
317
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
318
+ btc = new Btc({ transport, currency: "bitcoin" });
319
+
320
+ const mockCreatePaymentTransaction = jest.fn().mockResolvedValue("01000000...");
321
+ (BtcNew as jest.MockedClass<typeof BtcNew>).prototype.createPaymentTransaction =
322
+ mockCreatePaymentTransaction;
323
+
324
+ mockedGetAppAndVersion.mockResolvedValue(mockAppAndVersion("Bitcoin", "2.1.0"));
325
+
326
+ const tx = { inputs: [], outputs: [] } as any as Transaction;
327
+ const arg = {
328
+ inputs: [
329
+ [tx, 0, undefined, undefined] as [
330
+ Transaction,
331
+ number,
332
+ string | null | undefined,
333
+ number | null | undefined,
334
+ ],
335
+ ],
336
+ associatedKeysets: ["44'/0'/0'"],
337
+ outputScriptHex: "01...",
338
+ additionals: [],
339
+ };
340
+
341
+ const result = await btc.createPaymentTransaction(arg);
342
+
343
+ expect(result).toBe("01000000...");
344
+ expect(mockCreatePaymentTransaction).toHaveBeenCalledWith(arg);
345
+ });
346
+
347
+ it("should throw error for deprecated multi-argument signature", async () => {
348
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
349
+ btc = new Btc({ transport, currency: "bitcoin" });
350
+
351
+ await expect(
352
+ // @ts-expect-error Testing deprecated signature
353
+ btc.createPaymentTransaction([], [], "", ""),
354
+ ).rejects.toThrow("multi argument signature is deprecated");
355
+ });
356
+
357
+ it("should create a payment transaction with segwit", async () => {
358
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
359
+ btc = new Btc({ transport, currency: "bitcoin" });
360
+
361
+ const mockCreatePaymentTransaction = jest.fn().mockResolvedValue("01000000...");
362
+ (BtcNew as jest.MockedClass<typeof BtcNew>).prototype.createPaymentTransaction =
363
+ mockCreatePaymentTransaction;
364
+
365
+ mockedGetAppAndVersion.mockResolvedValue(mockAppAndVersion("Bitcoin", "2.1.0"));
366
+
367
+ const tx = { inputs: [], outputs: [] } as any as Transaction;
368
+ const arg = {
369
+ inputs: [
370
+ [tx, 0, undefined, undefined] as [
371
+ Transaction,
372
+ number,
373
+ string | null | undefined,
374
+ number | null | undefined,
375
+ ],
376
+ ],
377
+ associatedKeysets: ["84'/0'/0'"],
378
+ outputScriptHex: "01...",
379
+ segwit: true,
380
+ additionals: [],
381
+ };
382
+
383
+ await btc.createPaymentTransaction(arg);
384
+
385
+ expect(mockCreatePaymentTransaction).toHaveBeenCalledWith(arg);
386
+ });
387
+
388
+ it("should create a payment transaction with additionals", async () => {
389
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
390
+ btc = new Btc({ transport, currency: "bitcoin" });
391
+
392
+ const mockCreatePaymentTransaction = jest.fn().mockResolvedValue("01000000...");
393
+ (BtcNew as jest.MockedClass<typeof BtcNew>).prototype.createPaymentTransaction =
394
+ mockCreatePaymentTransaction;
395
+
396
+ mockedGetAppAndVersion.mockResolvedValue(mockAppAndVersion("Bitcoin", "2.1.0"));
397
+
398
+ const tx = { inputs: [], outputs: [] } as any as Transaction;
399
+ const arg = {
400
+ inputs: [
401
+ [tx, 0, undefined, undefined] as [
402
+ Transaction,
403
+ number,
404
+ string | null | undefined,
405
+ number | null | undefined,
406
+ ],
407
+ ],
408
+ associatedKeysets: ["84'/0'/0'"],
409
+ outputScriptHex: "01...",
410
+ additionals: ["bech32"],
411
+ };
412
+
413
+ await btc.createPaymentTransaction(arg);
414
+
415
+ expect(mockCreatePaymentTransaction).toHaveBeenCalledWith(arg);
416
+ });
417
+ });
418
+
419
+ describe("signPsbtBuffer", () => {
420
+ it("should sign a PSBT buffer", async () => {
421
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
422
+ btc = new Btc({ transport, currency: "bitcoin" });
423
+
424
+ const mockResult = { psbt: Buffer.from("psbt"), tx: "01000000..." };
425
+ const mockSignPsbtBuffer = jest.fn().mockResolvedValue(mockResult);
426
+ (BtcNew as jest.MockedClass<typeof BtcNew>).prototype.signPsbtBuffer = mockSignPsbtBuffer;
427
+
428
+ const psbtBuffer = Buffer.from("psbt");
429
+ const result = await btc.signPsbtBuffer(psbtBuffer);
430
+
431
+ expect(result).toEqual(mockResult);
432
+ expect(mockSignPsbtBuffer).toHaveBeenCalledWith(psbtBuffer, undefined);
433
+ });
434
+
435
+ it("should sign a PSBT buffer with options", async () => {
436
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
437
+ btc = new Btc({ transport, currency: "bitcoin" });
438
+
439
+ const mockResult = { psbt: Buffer.from("psbt"), tx: "01000000..." };
440
+ const mockSignPsbtBuffer = jest.fn().mockResolvedValue(mockResult);
441
+ (BtcNew as jest.MockedClass<typeof BtcNew>).prototype.signPsbtBuffer = mockSignPsbtBuffer;
442
+
443
+ const psbtBuffer = Buffer.from("psbt");
444
+ const opts = {
445
+ finalizePsbt: true,
446
+ accountPath: "m/84'/0'/0'",
447
+ addressFormat: "bech32" as AddressFormat,
448
+ };
449
+
450
+ await btc.signPsbtBuffer(psbtBuffer, opts);
451
+
452
+ expect(mockSignPsbtBuffer).toHaveBeenCalledWith(psbtBuffer, opts);
453
+ });
454
+
455
+ it("should throw error when using BtcOld implementation", async () => {
456
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
457
+ btc = new Btc({ transport, currency: "litecoin" });
458
+
459
+ const psbtBuffer = Buffer.from("psbt");
460
+
461
+ await expect(btc.signPsbtBuffer(psbtBuffer)).rejects.toThrow(
462
+ "signPsbtBuffer is not supported with the legacy Bitcoin app",
463
+ );
464
+ });
465
+ });
466
+
467
+ describe("signP2SHTransaction", () => {
468
+ it("should sign a P2SH transaction", async () => {
469
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
470
+ btc = new Btc({ transport, currency: "bitcoin" });
471
+
472
+ const tx = { inputs: [], outputs: [] } as any as Transaction;
473
+ const arg = {
474
+ inputs: [
475
+ [tx, 0, "522103...", undefined] as [
476
+ Transaction,
477
+ number,
478
+ string | null | undefined,
479
+ number | null | undefined,
480
+ ],
481
+ ],
482
+ associatedKeysets: ["44'/0'/0'"],
483
+ outputScriptHex: "01...",
484
+ };
485
+
486
+ // Since signP2SHTransaction is called directly on transport, we just ensure it doesn't throw
487
+ await expect(btc.signP2SHTransaction(arg)).rejects.toThrow();
488
+ });
489
+ });
490
+
491
+ describe("splitTransaction", () => {
492
+ it("should split a transaction", async () => {
493
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
494
+ btc = new Btc({ transport, currency: "bitcoin" });
495
+
496
+ const txHex = SAMPLE_TX_HEX;
497
+ const result = btc.splitTransaction(txHex);
498
+
499
+ expect(result).toBeDefined();
500
+ });
501
+
502
+ it("should split a transaction with segwit support", async () => {
503
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
504
+ btc = new Btc({ transport, currency: "bitcoin" });
505
+
506
+ const txHex = SAMPLE_TX_HEX;
507
+ const result = btc.splitTransaction(txHex, true);
508
+
509
+ expect(result).toBeDefined();
510
+ });
511
+
512
+ it("should split a transaction with extra data", async () => {
513
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
514
+ btc = new Btc({ transport, currency: "bitcoin" });
515
+
516
+ const txHex = SAMPLE_TX_HEX;
517
+ const result = btc.splitTransaction(txHex, false, true);
518
+
519
+ expect(result).toBeDefined();
520
+ });
521
+
522
+ it("should split a transaction with additionals", async () => {
523
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
524
+ btc = new Btc({ transport, currency: "bitcoin" });
525
+
526
+ const txHex = SAMPLE_TX_HEX;
527
+ const result = btc.splitTransaction(txHex, false, false, ["zcash"]);
528
+
529
+ expect(result).toBeDefined();
530
+ });
531
+ });
532
+
533
+ describe("serializeTransactionOutputs", () => {
534
+ it("should serialize transaction outputs", async () => {
535
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
536
+ btc = new Btc({ transport, currency: "bitcoin" });
537
+
538
+ const tx = {
539
+ version: Buffer.from([1, 0, 0, 0]),
540
+ inputs: [],
541
+ outputs: [
542
+ {
543
+ amount: Buffer.from([0x00, 0xe1, 0xf5, 0x05, 0x00, 0x00, 0x00, 0x00]),
544
+ script: Buffer.from("76a914", "hex"),
545
+ },
546
+ ],
547
+ } as any as Transaction;
548
+
549
+ const result = btc.serializeTransactionOutputs(tx);
550
+
551
+ expect(result).toBeInstanceOf(Buffer);
552
+ });
553
+ });
554
+
555
+ describe("getTrustedInput", () => {
556
+ it("should get trusted input", async () => {
557
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
558
+ btc = new Btc({ transport, currency: "bitcoin" });
559
+
560
+ const tx = {
561
+ version: Buffer.from([1, 0, 0, 0]),
562
+ inputs: [],
563
+ outputs: [],
564
+ } as any as Transaction;
565
+
566
+ await expect(btc.getTrustedInput(0, tx)).rejects.toThrow();
567
+ });
568
+
569
+ it("should get trusted input with additionals", async () => {
570
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
571
+ btc = new Btc({ transport, currency: "bitcoin" });
572
+
573
+ const tx = {
574
+ version: Buffer.from([1, 0, 0, 0]),
575
+ inputs: [],
576
+ outputs: [],
577
+ } as any as Transaction;
578
+
579
+ await expect(btc.getTrustedInput(0, tx, ["zcash"])).rejects.toThrow();
580
+ });
581
+ });
582
+
583
+ describe("getTrustedInputBIP143", () => {
584
+ it("should get trusted input BIP143", async () => {
585
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
586
+ btc = new Btc({ transport, currency: "bitcoin" });
587
+
588
+ const tx = {
589
+ version: Buffer.from([1, 0, 0, 0]),
590
+ inputs: [],
591
+ outputs: [
592
+ {
593
+ amount: Buffer.from([0, 0, 0, 0, 0, 0, 0, 0]),
594
+ script: Buffer.alloc(0),
595
+ },
596
+ ],
597
+ locktime: Buffer.from([0, 0, 0, 0]),
598
+ } as any as Transaction;
599
+
600
+ const result = btc.getTrustedInputBIP143(0, tx);
601
+
602
+ expect(result).toBeDefined();
603
+ expect(typeof result).toBe("string");
604
+ });
605
+
606
+ it("should get trusted input BIP143 with additionals", async () => {
607
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
608
+ btc = new Btc({ transport, currency: "bitcoin" });
609
+
610
+ const tx = {
611
+ version: Buffer.from([1, 0, 0, 0]),
612
+ inputs: [],
613
+ outputs: [
614
+ {
615
+ amount: Buffer.from([0, 0, 0, 0, 0, 0, 0, 0]),
616
+ script: Buffer.alloc(0),
617
+ },
618
+ ],
619
+ locktime: Buffer.from([0, 0, 0, 0]),
620
+ } as any as Transaction;
621
+
622
+ const result = btc.getTrustedInputBIP143(0, tx, ["abc"]);
623
+
624
+ expect(result).toBeDefined();
625
+ expect(typeof result).toBe("string");
626
+ });
627
+ });
628
+
629
+ describe("changeImplIfNecessary", () => {
630
+ it("should return BtcOld when already using BtcOld", async () => {
631
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
632
+ btc = new Btc({ transport, currency: "litecoin" });
633
+
634
+ const impl = await btc.changeImplIfNecessary();
635
+
636
+ expect(impl).toBeInstanceOf(BtcOld);
637
+ });
638
+
639
+ it("should switch to BtcOld for Bitcoin app version < 2.1.0", async () => {
640
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
641
+ btc = new Btc({ transport, currency: "bitcoin" });
642
+
643
+ mockedGetAppAndVersion.mockResolvedValue(mockAppAndVersion("Bitcoin", "2.0.0"));
644
+
645
+ const impl = await btc.changeImplIfNecessary();
646
+
647
+ expect(impl).toBeInstanceOf(BtcOld);
648
+ });
649
+
650
+ it("should keep BtcNew for Bitcoin app version >= 2.1.0", async () => {
651
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
652
+ btc = new Btc({ transport, currency: "bitcoin" });
653
+
654
+ mockedGetAppAndVersion.mockResolvedValue(mockAppAndVersion("Bitcoin", "2.1.0"));
655
+
656
+ const impl = await btc.changeImplIfNecessary();
657
+
658
+ expect(impl).toBeInstanceOf(BtcNew);
659
+ });
660
+
661
+ it("should switch to BtcOld for Bitcoin Test app version < 2.1.0", async () => {
662
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
663
+ btc = new Btc({ transport, currency: "bitcoin_testnet" });
664
+
665
+ mockedGetAppAndVersion.mockResolvedValue(mockAppAndVersion("Bitcoin Test", "2.0.5"));
666
+
667
+ const impl = await btc.changeImplIfNecessary();
668
+
669
+ expect(impl).toBeInstanceOf(BtcOld);
670
+ });
671
+
672
+ it("should keep BtcNew for Bitcoin Test app version >= 2.1.0", async () => {
673
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
674
+ btc = new Btc({ transport, currency: "bitcoin_testnet" });
675
+
676
+ mockedGetAppAndVersion.mockResolvedValue(mockAppAndVersion("Bitcoin Test", "2.2.0"));
677
+
678
+ const impl = await btc.changeImplIfNecessary();
679
+
680
+ expect(impl).toBeInstanceOf(BtcNew);
27
681
  });
28
682
 
29
- const btc = new Btc({ transport: mockTransport, currency: "bitcoin" });
30
- const impl = await btc.changeImplIfNecessary();
31
- // Check that getAppAndVersion has been called
32
- expect(mockedGetAppAndVersion).toHaveBeenCalledWith(mockTransport);
33
- expect(impl).toBeInstanceOf(BtcNew);
34
- expect(impl).not.toBeInstanceOf(BtcOld);
683
+ it("should use BtcOld for Bitcoin Legacy app", async () => {
684
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
685
+ btc = new Btc({ transport, currency: "bitcoin" });
686
+
687
+ mockedGetAppAndVersion.mockResolvedValue(mockAppAndVersion("Bitcoin Legacy", "2.5.0"));
688
+
689
+ const impl = await btc.changeImplIfNecessary();
690
+
691
+ expect(impl).toBeInstanceOf(BtcOld);
692
+ });
693
+
694
+ it("should use BtcOld for Bitcoin Test Legacy app", async () => {
695
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
696
+ btc = new Btc({ transport, currency: "bitcoin_testnet" });
697
+
698
+ mockedGetAppAndVersion.mockResolvedValue(mockAppAndVersion("Bitcoin Test Legacy", "2.5.0"));
699
+
700
+ const impl = await btc.changeImplIfNecessary();
701
+
702
+ expect(impl).toBeInstanceOf(BtcOld);
703
+ });
704
+
705
+ it("should use BtcOld for Exchange app with legacy Bitcoin", async () => {
706
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
707
+ btc = new Btc({ transport, currency: "bitcoin" });
708
+
709
+ mockedGetAppAndVersion.mockResolvedValue(mockAppAndVersion("Exchange", "1.0.0"));
710
+ mockedCheckIsBtcLegacy.mockResolvedValue(true);
711
+
712
+ const impl = await btc.changeImplIfNecessary();
713
+
714
+ expect(impl).toBeInstanceOf(BtcOld);
715
+ expect(mockedCheckIsBtcLegacy).toHaveBeenCalledWith(transport);
716
+ });
717
+
718
+ it("should keep BtcNew for Exchange app with new Bitcoin", async () => {
719
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
720
+ btc = new Btc({ transport, currency: "bitcoin" });
721
+
722
+ mockedGetAppAndVersion.mockResolvedValue(mockAppAndVersion("Exchange", "1.0.0"));
723
+ mockedCheckIsBtcLegacy.mockResolvedValue(false);
724
+
725
+ const impl = await btc.changeImplIfNecessary();
726
+
727
+ expect(impl).toBeInstanceOf(BtcNew);
728
+ expect(mockedCheckIsBtcLegacy).toHaveBeenCalledWith(transport);
729
+ });
730
+
731
+ it("should switch to BtcOld for Qtum app version < 3.0.0", async () => {
732
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
733
+ btc = new Btc({ transport, currency: "qtum" });
734
+
735
+ mockedGetAppAndVersion.mockResolvedValue(mockAppAndVersion("Qtum", "2.9.0"));
736
+
737
+ const impl = await btc.changeImplIfNecessary();
738
+
739
+ expect(impl).toBeInstanceOf(BtcOld);
740
+ });
741
+
742
+ it("should keep BtcNew for Qtum app version >= 3.0.0", async () => {
743
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
744
+ btc = new Btc({ transport, currency: "qtum" });
745
+
746
+ mockedGetAppAndVersion.mockResolvedValue(mockAppAndVersion("Qtum", "3.0.0"));
747
+
748
+ const impl = await btc.changeImplIfNecessary();
749
+
750
+ expect(impl).toBeInstanceOf(BtcNew);
751
+ });
752
+
753
+ it("should keep BtcNew for Bitcoin Recovery app", async () => {
754
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
755
+ btc = new Btc({ transport, currency: "bitcoin" });
756
+
757
+ mockedGetAppAndVersion.mockResolvedValue(mockAppAndVersion("Bitcoin Recovery", "1.0.0"));
758
+
759
+ const impl = await btc.changeImplIfNecessary();
760
+
761
+ expect(impl).toBeInstanceOf(BtcNew);
762
+ });
763
+
764
+ it("should use BtcOld for unknown app names", async () => {
765
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
766
+ btc = new Btc({ transport, currency: "bitcoin" });
767
+
768
+ mockedGetAppAndVersion.mockResolvedValue(mockAppAndVersion("Unknown App", "1.0.0"));
769
+
770
+ const impl = await btc.changeImplIfNecessary();
771
+
772
+ expect(impl).toBeInstanceOf(BtcOld);
773
+ });
774
+
775
+ it("should cache the implementation decision", async () => {
776
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
777
+ btc = new Btc({ transport, currency: "bitcoin" });
778
+
779
+ mockedGetAppAndVersion.mockResolvedValue(mockAppAndVersion("Bitcoin", "2.1.0"));
780
+
781
+ // First call
782
+ const impl1 = await btc.changeImplIfNecessary();
783
+
784
+ // Second call
785
+ const impl2 = await btc.changeImplIfNecessary();
786
+
787
+ expect(impl1).toBe(impl2);
788
+ // getAppAndVersion should be called twice
789
+ expect(mockedGetAppAndVersion).toHaveBeenCalledTimes(2);
790
+ });
791
+ });
792
+
793
+ describe("error handling", () => {
794
+ it("should handle transport errors gracefully", async () => {
795
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
796
+ btc = new Btc({ transport, currency: "bitcoin" });
797
+
798
+ mockedGetAppAndVersion.mockRejectedValue(new Error("Transport error"));
799
+
800
+ await expect(
801
+ btc.getWalletXpub({ path: "44'/0'/0'", xpubVersion: 0x0488b21e }),
802
+ ).rejects.toThrow("Transport error");
803
+ });
804
+
805
+ it("should handle implementation errors gracefully", async () => {
806
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
807
+ btc = new Btc({ transport, currency: "bitcoin" });
808
+
809
+ mockedGetAppAndVersion.mockResolvedValue(mockAppAndVersion("Bitcoin", "2.1.0"));
810
+
811
+ const mockGetWalletXpub = jest.fn().mockRejectedValue(new Error("Implementation error"));
812
+ (BtcNew as jest.MockedClass<typeof BtcNew>).prototype.getWalletXpub = mockGetWalletXpub;
813
+
814
+ await expect(
815
+ btc.getWalletXpub({ path: "44'/0'/0'", xpubVersion: 0x0488b21e }),
816
+ ).rejects.toThrow("Implementation error");
817
+ });
818
+ });
819
+
820
+ describe("method chaining and state", () => {
821
+ it("should maintain state across multiple calls", async () => {
822
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
823
+ btc = new Btc({ transport, currency: "bitcoin" });
824
+
825
+ const mockGetWalletXpub = jest.fn().mockResolvedValue("xpub1");
826
+ (BtcNew as jest.MockedClass<typeof BtcNew>).prototype.getWalletXpub = mockGetWalletXpub;
827
+
828
+ mockedGetAppAndVersion.mockResolvedValue(mockAppAndVersion("Bitcoin", "2.1.0"));
829
+
830
+ await btc.getWalletXpub({ path: "44'/0'/0'", xpubVersion: 0x0488b21e });
831
+ await btc.getWalletXpub({ path: "44'/0'/1'", xpubVersion: 0x0488b21e });
832
+
833
+ expect(mockGetWalletXpub).toHaveBeenCalledTimes(2);
834
+ });
835
+
836
+ it("should handle multiple method calls in sequence", async () => {
837
+ const transport = await openTransportReplayer(RecordStore.fromString(""));
838
+ btc = new Btc({ transport, currency: "bitcoin" });
839
+
840
+ const mockGetWalletXpub = jest.fn().mockResolvedValue("xpub1");
841
+ const mockGetWalletPublicKey = jest.fn().mockResolvedValue({
842
+ publicKey: "04...",
843
+ bitcoinAddress: "1...",
844
+ chainCode: "00...",
845
+ });
846
+ const mockSignMessage = jest.fn().mockResolvedValue({ v: 27, r: "r", s: "s" });
847
+
848
+ (BtcNew as jest.MockedClass<typeof BtcNew>).prototype.getWalletXpub = mockGetWalletXpub;
849
+ (BtcNew as jest.MockedClass<typeof BtcNew>).prototype.getWalletPublicKey =
850
+ mockGetWalletPublicKey;
851
+ (BtcNew as jest.MockedClass<typeof BtcNew>).prototype.signMessage = mockSignMessage;
852
+
853
+ mockedGetAppAndVersion.mockResolvedValue(mockAppAndVersion("Bitcoin", "2.1.0"));
854
+
855
+ await btc.getWalletXpub({ path: "44'/0'/0'", xpubVersion: 0x0488b21e });
856
+ await btc.getWalletPublicKey("44'/0'/0'/0/0");
857
+ await btc.signMessage("44'/0'/0'/0/0", Buffer.from("test").toString("hex"));
858
+
859
+ expect(mockGetWalletXpub).toHaveBeenCalledTimes(1);
860
+ expect(mockGetWalletPublicKey).toHaveBeenCalledTimes(1);
861
+ expect(mockSignMessage).toHaveBeenCalledTimes(1);
862
+ });
35
863
  });
36
864
  });