@ledgerhq/hw-app-kaspa 1.3.0-nightly.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/.prettierrc +4 -0
  2. package/.turbo/turbo-build.log +4 -0
  3. package/CHANGELOG.md +7 -0
  4. package/LICENSE.txt +21 -0
  5. package/README.md +105 -0
  6. package/jest.config.ts +10 -0
  7. package/lib/Kaspa.d.ts +59 -0
  8. package/lib/Kaspa.d.ts.map +1 -0
  9. package/lib/Kaspa.js +185 -0
  10. package/lib/Kaspa.js.map +1 -0
  11. package/lib/base32.d.ts +11 -0
  12. package/lib/base32.d.ts.map +1 -0
  13. package/lib/base32.js +36 -0
  14. package/lib/base32.js.map +1 -0
  15. package/lib/index.d.ts +4 -0
  16. package/lib/index.d.ts.map +1 -0
  17. package/lib/index.js +13 -0
  18. package/lib/index.js.map +1 -0
  19. package/lib/kaspa-util.d.ts +3 -0
  20. package/lib/kaspa-util.d.ts.map +1 -0
  21. package/lib/kaspa-util.js +102 -0
  22. package/lib/kaspa-util.js.map +1 -0
  23. package/lib/kaspaHwTransaction.d.ts +89 -0
  24. package/lib/kaspaHwTransaction.d.ts.map +1 -0
  25. package/lib/kaspaHwTransaction.js +157 -0
  26. package/lib/kaspaHwTransaction.js.map +1 -0
  27. package/lib-es/Kaspa.d.ts +59 -0
  28. package/lib-es/Kaspa.d.ts.map +1 -0
  29. package/lib-es/Kaspa.js +179 -0
  30. package/lib-es/Kaspa.js.map +1 -0
  31. package/lib-es/base32.d.ts +11 -0
  32. package/lib-es/base32.d.ts.map +1 -0
  33. package/lib-es/base32.js +33 -0
  34. package/lib-es/base32.js.map +1 -0
  35. package/lib-es/index.d.ts +4 -0
  36. package/lib-es/index.d.ts.map +1 -0
  37. package/lib-es/index.js +4 -0
  38. package/lib-es/index.js.map +1 -0
  39. package/lib-es/kaspa-util.d.ts +3 -0
  40. package/lib-es/kaspa-util.d.ts.map +1 -0
  41. package/lib-es/kaspa-util.js +95 -0
  42. package/lib-es/kaspa-util.js.map +1 -0
  43. package/lib-es/kaspaHwTransaction.d.ts +89 -0
  44. package/lib-es/kaspaHwTransaction.d.ts.map +1 -0
  45. package/lib-es/kaspaHwTransaction.js +150 -0
  46. package/lib-es/kaspaHwTransaction.js.map +1 -0
  47. package/package.json +43 -0
  48. package/src/Kaspa.ts +261 -0
  49. package/src/base32.ts +36 -0
  50. package/src/index.ts +9 -0
  51. package/src/kaspa-util.ts +107 -0
  52. package/src/kaspaHwTransaction.ts +244 -0
  53. package/tests/kaspa.test.ts +724 -0
  54. package/tsconfig.json +25 -0
@@ -0,0 +1,724 @@
1
+ import {
2
+ openTransportReplayer,
3
+ RecordStore,
4
+ } from "@ledgerhq/hw-transport-mocker";
5
+ import Kaspa from "../src/Kaspa";
6
+ import {
7
+ KaspaHwTransaction,
8
+ TransactionInput,
9
+ TransactionOutput,
10
+ } from "../lib/kaspaHwTransaction";
11
+
12
+ describe("kaspa", () => {
13
+ it("getVersion", async () => {
14
+ const transport = await openTransportReplayer(
15
+ RecordStore.fromString(`
16
+ => e004000000
17
+ <= 0105069000
18
+ `),
19
+ );
20
+ const kaspa = new Kaspa(transport);
21
+ const result = await kaspa.getVersion();
22
+ expect(result).toEqual({
23
+ version: "1.5.6",
24
+ });
25
+ });
26
+
27
+ it("getPublicKey without display (implicit)", async () => {
28
+ const transport = await openTransportReplayer(
29
+ RecordStore.fromString(`
30
+ => e005000015058000002c8001b207800000000000000000000000
31
+ <= deadbeef9000
32
+ `),
33
+ );
34
+ const kaspa = new Kaspa(transport);
35
+ const response = await kaspa.getAddress("44'/111111'/0'/0/0");
36
+ const publicKey = response.publicKey;
37
+ const address = response.address;
38
+ console.log(address);
39
+ expect(publicKey).toEqual("deadbeef");
40
+ });
41
+
42
+ it("getPublicKey without display", async () => {
43
+ const transport = await openTransportReplayer(
44
+ RecordStore.fromString(`
45
+ => e005000015058000002c8001b207800000000000000000000000
46
+ <= deadbeef9000
47
+ `),
48
+ );
49
+ const kaspa = new Kaspa(transport);
50
+ const publicKey = (await kaspa.getAddress("44'/111111'/0'/0/0", false))
51
+ .publicKey;
52
+ expect(publicKey).toEqual("deadbeef");
53
+ });
54
+
55
+ it("getPublicKey with display", async () => {
56
+ const transport = await openTransportReplayer(
57
+ RecordStore.fromString(`
58
+ => e005010015058000002c8001b207800000000000000000000000
59
+ <= deadbeef9000
60
+ `),
61
+ );
62
+ const kaspa = new Kaspa(transport);
63
+ const publicKey = (await kaspa.getAddress("44'/111111'/0'/0/0", true))
64
+ .publicKey;
65
+ expect(publicKey).toEqual("deadbeef");
66
+ });
67
+
68
+ it("getAddress", async () => {
69
+ const transport = await openTransportReplayer(
70
+ RecordStore.fromString(`
71
+ => e005010015058000002c8001b207800000000000000000000000
72
+ <= 4104600d27e2c82127e38ee0b7317e45c8f683a47f5075d14b6b379d26ed81b08815e6f942844f514018b4e7de02cf6582e1ed65f1960d39dca389409fb439d13594208f3ddd2b84a83d9db8a7375a59bd1ec69be7ed57882eb9abef694761171f2af79000
73
+ `),
74
+ );
75
+ const kaspa = new Kaspa(transport);
76
+ const address = (await kaspa.getAddress("44'/111111'/0'/0/0", true))
77
+ .address;
78
+
79
+ expect(address).toEqual(
80
+ "kaspa:qpsq6flzeqsj0cuwuzmnzlj9ermg8frl2p6azjmtx7wjdmvpkzyp24kndspxw",
81
+ );
82
+ });
83
+
84
+ it("signTransaction with simple data", async () => {
85
+ const transport = await openTransportReplayer(
86
+ RecordStore.fromString(`
87
+ => e00600800d00000101010203040580000000
88
+ <= 9000
89
+ => e00601802a000000000010a1d02011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac
90
+ <= 9000
91
+ => e00602002e000000000010c8e040b022362f1a303518e2b49f86f87a317c87b514ca0f3d08ad2e7cf49d08cc70000000000000
92
+ <= 000040ec4a7f581dc2450ab43b412a67bdfdafa6f98281f854a1508852042e41ef86695ec7f0fa36122193fa201ce783618710d65c85cf94640cb93e965f5158fd84a32000112233445566778899aabbccddeeff00112233445566778899aabbccddeeff9000
93
+ `),
94
+ );
95
+ const kaspa = new Kaspa(transport);
96
+
97
+ const txin = new TransactionInput({
98
+ prevTxId:
99
+ "40b022362f1a303518e2b49f86f87a317c87b514ca0f3d08ad2e7cf49d08cc70",
100
+ value: 1100000,
101
+ addressType: 0,
102
+ addressIndex: 0,
103
+ outpointIndex: 0,
104
+ });
105
+
106
+ const txout = new TransactionOutput({
107
+ value: 1090000,
108
+ scriptPublicKey:
109
+ "2011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac",
110
+ });
111
+
112
+ const tx = new KaspaHwTransaction({
113
+ version: 0,
114
+ inputs: [txin],
115
+ outputs: [txout],
116
+ changeAddressType: 1,
117
+ changeAddressIndex: 0x02030405,
118
+ });
119
+
120
+ try {
121
+ await kaspa.signTransaction(tx);
122
+ expect(txin.signature).toEqual(
123
+ "ec4a7f581dc2450ab43b412a67bdfdafa6f98281f854a1508852042e41ef86695ec7f0fa36122193fa201ce783618710d65c85cf94640cb93e965f5158fd84a3",
124
+ );
125
+ expect(txin.sighash).toEqual(
126
+ "00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff",
127
+ );
128
+ } catch (e) {
129
+ console.error(e);
130
+ expect(e).toBe(null);
131
+ }
132
+ });
133
+
134
+ it("signTransaction with multi inputs", async () => {
135
+ const transport = await openTransportReplayer(
136
+ RecordStore.fromString(`
137
+ => e00600800d00000102010203040580000000
138
+ <= 9000
139
+ => e00601802a000000000010a1d02011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac
140
+ <= 9000
141
+ => e00602802e000000000010c8e040b022362f1a303518e2b49f86f87a317c87b514ca0f3d08ad2e7cf49d08cc70000000000000
142
+ <= 9000
143
+ => e00602002e000000000010c8e040aa22362f1a303518e2b49f86f87a317c87b514ca0f3d08ad2e7cf49d08cc70000000000000
144
+ <= 010040ec4a7f581dc2450ab43b412a67bdfdafa6f98281f854a1508852042e41ef86695ec7f0fa36122193fa201ce783618710d65c85cf94640cb93e965f5158fd84a32000112233445566778899aabbccddeeff00112233445566778899aabbccddeeff9000
145
+ => e006030000
146
+ <= 000140b33f7f581dc2450ab43b412a67bdfdafa6f98281f854a1508852042e41ef86695ec7f0fa36122193fa201ce783618710d65c85cf94640cb93e965f5158fd84a32000112233445566778899aabbccddeeff00112233445566778899aabbccddeeff9000
147
+ `),
148
+ );
149
+ const kaspa = new Kaspa(transport);
150
+
151
+ const txin1 = new TransactionInput({
152
+ prevTxId:
153
+ "40b022362f1a303518e2b49f86f87a317c87b514ca0f3d08ad2e7cf49d08cc70",
154
+ value: 1100000,
155
+ addressType: 0,
156
+ addressIndex: 0,
157
+ outpointIndex: 0,
158
+ });
159
+
160
+ const txin2 = new TransactionInput({
161
+ prevTxId:
162
+ "40aa22362f1a303518e2b49f86f87a317c87b514ca0f3d08ad2e7cf49d08cc70",
163
+ value: 1100000,
164
+ addressType: 0,
165
+ addressIndex: 0,
166
+ outpointIndex: 0,
167
+ });
168
+
169
+ const txout = new TransactionOutput({
170
+ value: 1090000,
171
+ scriptPublicKey:
172
+ "2011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac",
173
+ });
174
+
175
+ const tx = new KaspaHwTransaction({
176
+ version: 0,
177
+ inputs: [txin1, txin2],
178
+ outputs: [txout],
179
+ changeAddressType: 1,
180
+ changeAddressIndex: 0x02030405,
181
+ });
182
+
183
+ try {
184
+ await kaspa.signTransaction(tx);
185
+ expect(txin1.signature).toEqual(
186
+ "ec4a7f581dc2450ab43b412a67bdfdafa6f98281f854a1508852042e41ef86695ec7f0fa36122193fa201ce783618710d65c85cf94640cb93e965f5158fd84a3",
187
+ );
188
+ expect(txin1.sighash).toEqual(
189
+ "00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff",
190
+ );
191
+
192
+ expect(txin2.signature).toEqual(
193
+ "b33f7f581dc2450ab43b412a67bdfdafa6f98281f854a1508852042e41ef86695ec7f0fa36122193fa201ce783618710d65c85cf94640cb93e965f5158fd84a3",
194
+ );
195
+ expect(txin2.sighash).toEqual(
196
+ "00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff",
197
+ );
198
+ } catch (e) {
199
+ console.error(e);
200
+ expect(e).toBe(null);
201
+ }
202
+ });
203
+
204
+ it("signTransaction should always have sigLen = 64", async () => {
205
+ const transport = await openTransportReplayer(
206
+ RecordStore.fromString(`
207
+ => e00600800d00000101010203040580000000
208
+ <= 9000
209
+ => e00601802a000000000010a1d02011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac
210
+ <= 9000
211
+ => e00602002e000000000010c8e040b022362f1a303518e2b49f86f87a317c87b514ca0f3d08ad2e7cf49d08cc70000000000000
212
+ <= 00004100ec4a7f581dc2450ab43b412a67bdfdafa6f98281f854a1508852042e41ef86695ec7f0fa36122193fa201ce783618710d65c85cf94640cb93e965f5158fd84a32000112233445566778899aabbccddeeff00112233445566778899aabbccddeeff9000
213
+ `),
214
+ );
215
+ const kaspa = new Kaspa(transport);
216
+
217
+ const txin = new TransactionInput({
218
+ prevTxId:
219
+ "40b022362f1a303518e2b49f86f87a317c87b514ca0f3d08ad2e7cf49d08cc70",
220
+ value: 1100000,
221
+ addressType: 0,
222
+ addressIndex: 0,
223
+ outpointIndex: 0,
224
+ });
225
+
226
+ const txout = new TransactionOutput({
227
+ value: 1090000,
228
+ scriptPublicKey:
229
+ "2011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac",
230
+ });
231
+
232
+ const tx = new KaspaHwTransaction({
233
+ version: 0,
234
+ inputs: [txin],
235
+ outputs: [txout],
236
+ changeAddressType: 1,
237
+ changeAddressIndex: 0x02030405,
238
+ });
239
+
240
+ let err: any = null;
241
+ try {
242
+ await kaspa.signTransaction(tx);
243
+ } catch (e) {
244
+ err = e;
245
+ }
246
+
247
+ expect(err.name).not.toBe("RecordStoreWrongAPDU");
248
+ expect(err).not.toBe(null);
249
+ });
250
+
251
+ it("signTransaction should always have sighashLen = 32", async () => {
252
+ const transport = await openTransportReplayer(
253
+ RecordStore.fromString(`
254
+ => e00600800d00000101010203040580000000
255
+ <= 9000
256
+ => e00601802a000000000010a1d02011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac
257
+ <= 9000
258
+ => e00602002e000000000010c8e040b022362f1a303518e2b49f86f87a317c87b514ca0f3d08ad2e7cf49d08cc70000000000000
259
+ <= 000040ec4a7f581dc2450ab43b412a67bdfdafa6f98281f854a1508852042e41ef86695ec7f0fa36122193fa201ce783618710d65c85cf94640cb93e965f5158fd84a3210000112233445566778899aabbccddeeff00112233445566778899aabbccddeeff9000
260
+ `),
261
+ );
262
+ const kaspa = new Kaspa(transport);
263
+
264
+ const txin = new TransactionInput({
265
+ prevTxId:
266
+ "40b022362f1a303518e2b49f86f87a317c87b514ca0f3d08ad2e7cf49d08cc70",
267
+ value: 1100000,
268
+ addressType: 0,
269
+ addressIndex: 0,
270
+ outpointIndex: 0,
271
+ });
272
+
273
+ const txout = new TransactionOutput({
274
+ value: 1090000,
275
+ scriptPublicKey:
276
+ "2011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac",
277
+ });
278
+
279
+ const tx = new KaspaHwTransaction({
280
+ version: 0,
281
+ inputs: [txin],
282
+ outputs: [txout],
283
+ changeAddressType: 1,
284
+ changeAddressIndex: 0x02030405,
285
+ });
286
+
287
+ let err: any = null;
288
+ try {
289
+ await kaspa.signTransaction(tx);
290
+ } catch (e) {
291
+ err = e;
292
+ }
293
+
294
+ expect(err.name).not.toBe("RecordStoreWrongAPDU");
295
+ expect(err).not.toBe(null);
296
+ });
297
+
298
+ it("Transaction should set default change address index and type", () => {
299
+ const tx = new KaspaHwTransaction({
300
+ version: 0,
301
+ inputs: [],
302
+ outputs: [],
303
+ });
304
+
305
+ expect(tx.changeAddressIndex).toEqual(0);
306
+ expect(tx.changeAddressType).toEqual(0);
307
+ });
308
+
309
+ it("TransactionInput should show null for signatureScript when unsigned", () => {
310
+ const txin = new TransactionInput({
311
+ prevTxId: "",
312
+ value: 0,
313
+ addressType: 0,
314
+ addressIndex: 0,
315
+ outpointIndex: 0,
316
+ });
317
+
318
+ expect(txin.toApiJSON().signatureScript).toEqual(null);
319
+
320
+ const expectedSignature =
321
+ "ec4a7f581dc2450ab43b412a67bdfdafa6f98281f854a1508852042e41ef86695ec7f0fa36122193fa201ce783618710d65c85cf94640cb93e965f5158fd84a3";
322
+ txin.setSignature(expectedSignature);
323
+
324
+ expect(txin.toApiJSON().signatureScript).toEqual(
325
+ `41${expectedSignature}01`,
326
+ );
327
+ });
328
+
329
+ it("signMessage with simple data", async () => {
330
+ const expectedSignature =
331
+ "ec4a7f581dc2450ab43b412a67bdfdafa6f98281f854a1508852042e41ef86695ec7f0fa36122193fa201ce783618710d65c85cf94640cb93e965f5158fd84a3";
332
+ const expectedMessageHash =
333
+ "00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff";
334
+ const transport = await openTransportReplayer(
335
+ RecordStore.fromString(`
336
+ => e0070000160000000000800000000c48656c6c6f204b6173706121
337
+ <= 40${expectedSignature}20${expectedMessageHash}9000
338
+ `),
339
+ );
340
+ const kaspa = new Kaspa(transport);
341
+
342
+ try {
343
+ const { signature, messageHash } = await kaspa.signMessage(
344
+ "Hello Kaspa!",
345
+ 0,
346
+ 0,
347
+ );
348
+ expect(signature).toEqual(expectedSignature);
349
+ expect(messageHash).toEqual(messageHash);
350
+ } catch (e) {
351
+ console.error(e);
352
+ expect(e).toBe(null);
353
+ }
354
+ });
355
+
356
+ it("signMessage requires valid address index", async () => {
357
+ const expectedSignature =
358
+ "ec4a7f581dc2450ab43b412a67bdfdafa6f98281f854a1508852042e41ef86695ec7f0fa36122193fa201ce783618710d65c85cf94640cb93e965f5158fd84a3";
359
+ const expectedMessageHash =
360
+ "00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff";
361
+ const transport = await openTransportReplayer(
362
+ RecordStore.fromString(`
363
+ => e00700001600000000008000000000c48656c6c6f204b6173706121
364
+ <= 40${expectedSignature}20${expectedMessageHash}9000
365
+ `),
366
+ );
367
+ const kaspa = new Kaspa(transport);
368
+
369
+ let err: any = null;
370
+ try {
371
+ const { signature, messageHash } = await kaspa.signMessage(
372
+ "Hello Kaspa!",
373
+ 0,
374
+ -1,
375
+ );
376
+ expect(signature).toEqual(expectedSignature);
377
+ expect(messageHash).toEqual(messageHash);
378
+ } catch (e) {
379
+ err = e;
380
+ }
381
+
382
+ expect(err).not.toBe(null);
383
+ });
384
+
385
+ it("signMessage requires valid account", async () => {
386
+ const expectedSignature =
387
+ "ec4a7f581dc2450ab43b412a67bdfdafa6f98281f854a1508852042e41ef86695ec7f0fa36122193fa201ce783618710d65c85cf94640cb93e965f5158fd84a3";
388
+ const expectedMessageHash =
389
+ "00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff";
390
+ const transport = await openTransportReplayer(
391
+ RecordStore.fromString(`
392
+ => e00700001600000000008000000000c48656c6c6f204b6173706121
393
+ <= 40${expectedSignature}20${expectedMessageHash}9000
394
+ `),
395
+ );
396
+ const kaspa = new Kaspa(transport);
397
+
398
+ let err: any = null;
399
+ try {
400
+ const { signature, messageHash } = await kaspa.signMessage(
401
+ "Hello Kaspa!",
402
+ 0,
403
+ 0,
404
+ 0x80000000 - 1,
405
+ );
406
+ expect(signature).toEqual(expectedSignature);
407
+ expect(messageHash).toEqual(messageHash);
408
+ } catch (e) {
409
+ err = e;
410
+ }
411
+
412
+ expect(err).not.toBe(null);
413
+
414
+ err = null;
415
+ try {
416
+ const { signature, messageHash } = await kaspa.signMessage(
417
+ "Hello Kaspa!",
418
+ 0,
419
+ 0,
420
+ 0xffffffff + 1,
421
+ );
422
+ expect(signature).toEqual(expectedSignature);
423
+ expect(messageHash).toEqual(messageHash);
424
+ } catch (e) {
425
+ err = e;
426
+ }
427
+
428
+ expect(err).not.toBe(null);
429
+ });
430
+
431
+ it("signMessage sets default values for index and account", async () => {
432
+ const expectedSignature =
433
+ "ec4a7f581dc2450ab43b412a67bdfdafa6f98281f854a1508852042e41ef86695ec7f0fa36122193fa201ce783618710d65c85cf94640cb93e965f5158fd84a3";
434
+ const expectedMessageHash =
435
+ "00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff";
436
+ const transport = await openTransportReplayer(
437
+ RecordStore.fromString(`
438
+ => e00700001600000000008000000000c48656c6c6f204b6173706121
439
+ <= 40${expectedSignature}20${expectedMessageHash}9000
440
+ `),
441
+ );
442
+ const kaspa = new Kaspa(transport);
443
+
444
+ let err: any = null;
445
+ try {
446
+ const { signature, messageHash } =
447
+ await kaspa.signMessage("Hello Kaspa!");
448
+ expect(signature).toEqual(expectedSignature);
449
+ expect(messageHash).toEqual(messageHash);
450
+ } catch (e) {
451
+ err = e;
452
+ }
453
+ });
454
+ });
455
+
456
+ describe("Transaction", () => {
457
+ it("should serialize", () => {
458
+ const txin = new TransactionInput({
459
+ prevTxId:
460
+ "40b022362f1a303518e2b49f86f87a317c87b514ca0f3d08ad2e7cf49d08cc70",
461
+ value: 1100000,
462
+ addressType: 0,
463
+ addressIndex: 0,
464
+ outpointIndex: 0,
465
+ });
466
+
467
+ const txout = new TransactionOutput({
468
+ value: 1090000,
469
+ scriptPublicKey:
470
+ "2011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac",
471
+ });
472
+
473
+ const tx = new KaspaHwTransaction({
474
+ version: 0,
475
+ inputs: [txin],
476
+ outputs: [txout],
477
+ changeAddressType: 1,
478
+ changeAddressIndex: 0x02030405,
479
+ });
480
+
481
+ const expectation = Buffer.from([
482
+ 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x03, 0x04, 0x05, 0x80, 0x00, 0x00,
483
+ 0x00,
484
+ ]);
485
+
486
+ expect(tx.serialize().equals(expectation)).toBeTruthy();
487
+ });
488
+
489
+ it("should check that change address type is 0 or 1", () => {
490
+ const txin = new TransactionInput({
491
+ prevTxId:
492
+ "40b022362f1a303518e2b49f86f87a317c87b514ca0f3d08ad2e7cf49d08cc70",
493
+ value: 1100000,
494
+ addressType: 0,
495
+ addressIndex: 0,
496
+ outpointIndex: 0,
497
+ });
498
+
499
+ const txout = new TransactionOutput({
500
+ value: 1090000,
501
+ scriptPublicKey:
502
+ "2011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac",
503
+ });
504
+
505
+ let err: any = null;
506
+ try {
507
+ new KaspaHwTransaction({
508
+ version: 0,
509
+ inputs: [txin],
510
+ outputs: [txout],
511
+ changeAddressType: 2,
512
+ changeAddressIndex: 0x02030405,
513
+ });
514
+ } catch (e) {
515
+ err = e;
516
+ }
517
+
518
+ expect(err).not.toBe(null);
519
+ });
520
+
521
+ it("should check that account is in 0x80000000 and 0xFFFFFFFF", () => {
522
+ const txin = new TransactionInput({
523
+ prevTxId:
524
+ "40b022362f1a303518e2b49f86f87a317c87b514ca0f3d08ad2e7cf49d08cc70",
525
+ value: 1100000,
526
+ addressType: 0,
527
+ addressIndex: 0,
528
+ outpointIndex: 0,
529
+ });
530
+
531
+ const txout = new TransactionOutput({
532
+ value: 1090000,
533
+ scriptPublicKey:
534
+ "2011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac",
535
+ });
536
+
537
+ let err: any = null;
538
+ try {
539
+ new KaspaHwTransaction({
540
+ version: 0,
541
+ inputs: [txin],
542
+ outputs: [txout],
543
+ changeAddressType: 0,
544
+ changeAddressIndex: 0x02030405,
545
+ account: 0x80000000 - 1,
546
+ });
547
+ } catch (e) {
548
+ err = e;
549
+ }
550
+
551
+ expect(err).not.toBe(null);
552
+ });
553
+
554
+ it("should check that change address index to be in range", () => {
555
+ const txin = new TransactionInput({
556
+ prevTxId:
557
+ "40b022362f1a303518e2b49f86f87a317c87b514ca0f3d08ad2e7cf49d08cc70",
558
+ value: 1100000,
559
+ addressType: 0,
560
+ addressIndex: 0,
561
+ outpointIndex: 0,
562
+ });
563
+
564
+ const txout = new TransactionOutput({
565
+ value: 1090000,
566
+ scriptPublicKey:
567
+ "2011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac",
568
+ });
569
+
570
+ let err: any = null;
571
+ try {
572
+ new KaspaHwTransaction({
573
+ version: 0,
574
+ inputs: [txin],
575
+ outputs: [txout],
576
+ changeAddressType: 0,
577
+ changeAddressIndex: -1,
578
+ });
579
+ } catch (e) {
580
+ err = e;
581
+ }
582
+
583
+ expect(err).not.toBe(null);
584
+ });
585
+
586
+ it("should check that API JSON is producible", () => {
587
+ const txin = new TransactionInput({
588
+ prevTxId:
589
+ "40b022362f1a303518e2b49f86f87a317c87b514ca0f3d08ad2e7cf49d08cc70",
590
+ value: 1100000,
591
+ addressType: 0,
592
+ addressIndex: 0,
593
+ outpointIndex: 0,
594
+ });
595
+
596
+ const txout = new TransactionOutput({
597
+ value: 1090000,
598
+ scriptPublicKey:
599
+ "2011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac",
600
+ });
601
+
602
+ const tx = new KaspaHwTransaction({
603
+ version: 0,
604
+ inputs: [txin],
605
+ outputs: [txout],
606
+ changeAddressType: 1,
607
+ changeAddressIndex: 0x02030405,
608
+ });
609
+
610
+ const json = tx.toApiJSON().transaction;
611
+ expect(json.version).toEqual(0);
612
+ expect(json.inputs.length).toEqual(1);
613
+ expect(json.outputs.length).toEqual(1);
614
+ expect(json.lockTime).toEqual(0);
615
+ expect(json.subnetworkId).toEqual(
616
+ "0000000000000000000000000000000000000000",
617
+ );
618
+ });
619
+ });
620
+
621
+ describe("TransactionInput", () => {
622
+ it("should allow for highest possible kaspa value", () => {
623
+ let err: any = null;
624
+
625
+ try {
626
+ const txin1 = new TransactionInput({
627
+ prevTxId:
628
+ "40b022362f1a303518e2b49f86f87a317c87b514ca0f3d08ad2e7cf49d08cc70",
629
+ value: 18446744073709551615,
630
+ addressType: 0,
631
+ addressIndex: 0,
632
+ outpointIndex: 0,
633
+ });
634
+
635
+ txin1.serialize();
636
+
637
+ txin1.toApiJSON();
638
+ } catch (e) {
639
+ err = e;
640
+ }
641
+
642
+ expect(err).toBe(null);
643
+ });
644
+ });
645
+
646
+ describe("TransactionOutput", () => {
647
+ it("should throw no error if only scriptPublicKey and value is set", () => {
648
+ let err: any = null;
649
+ try {
650
+ new TransactionOutput({
651
+ value: 1090000,
652
+ scriptPublicKey:
653
+ "2011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac",
654
+ });
655
+ } catch (e) {
656
+ err = e;
657
+ }
658
+
659
+ expect(err).toBe(null);
660
+ });
661
+
662
+ it("should serialize value and scriptPublicKey", () => {
663
+ const serial = new TransactionOutput({
664
+ value: 1090000,
665
+ scriptPublicKey:
666
+ "2011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac",
667
+ }).serialize();
668
+
669
+ const expectation = Buffer.from([
670
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xa1, 0xd0, 0x20, 0x11, 0xa7, 0x21,
671
+ 0x5f, 0x66, 0x8e, 0x92, 0x10, 0x13, 0xeb, 0x7a, 0xac, 0x9b, 0x7e, 0x64,
672
+ 0xb9, 0xec, 0x6e, 0x75, 0x7c, 0x1b, 0x64, 0x8e, 0x89, 0x38, 0x8c, 0x91,
673
+ 0x9f, 0x67, 0x6a, 0xa8, 0x8c, 0xac,
674
+ ]);
675
+
676
+ expect(serial.equals(expectation)).toBeTruthy();
677
+ });
678
+
679
+ it("should throw errors if value is < 0 or > 0xFFFFFFFFFFFFFFFF", () => {
680
+ let err: any = null;
681
+ try {
682
+ new TransactionOutput({
683
+ value: 0,
684
+ scriptPublicKey:
685
+ "2011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac",
686
+ });
687
+ } catch (e) {
688
+ err = e;
689
+ }
690
+
691
+ expect(err).not.toBe(null);
692
+
693
+ try {
694
+ new TransactionOutput({
695
+ value: 0x10000000000000000,
696
+ scriptPublicKey:
697
+ "2011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac",
698
+ });
699
+ } catch (e) {
700
+ err = e;
701
+ }
702
+
703
+ expect(err).not.toBe(null);
704
+ });
705
+
706
+ it("should allow for highest possible kaspa value", () => {
707
+ let err: any = null;
708
+ try {
709
+ const output = new TransactionOutput({
710
+ value: Number.MAX_SAFE_INTEGER,
711
+ scriptPublicKey:
712
+ "2011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac",
713
+ });
714
+
715
+ output.serialize();
716
+
717
+ output.toApiJSON();
718
+ } catch (e) {
719
+ err = e;
720
+ }
721
+
722
+ expect(err).toBe(null);
723
+ });
724
+ });