@ledgerhq/coin-framework 2.5.0 → 2.6.0-next.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 (110) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/jest.config.js +1 -0
  3. package/lib/account/balanceHistoryCache.d.ts +14 -0
  4. package/lib/account/balanceHistoryCache.d.ts.map +1 -1
  5. package/lib/account/balanceHistoryCache.js +5 -3
  6. package/lib/account/balanceHistoryCache.js.map +1 -1
  7. package/lib/account/helpers.d.ts.map +1 -1
  8. package/lib/account/helpers.js +0 -13
  9. package/lib/account/helpers.js.map +1 -1
  10. package/lib/account/index.d.ts +1 -1
  11. package/lib/account/index.d.ts.map +1 -1
  12. package/lib/account/index.js +2 -1
  13. package/lib/account/index.js.map +1 -1
  14. package/lib/account/index.test.d.ts +2 -0
  15. package/lib/account/index.test.d.ts.map +1 -0
  16. package/lib/{account.test.js → account/index.test.js} +31 -31
  17. package/lib/account/index.test.js.map +1 -0
  18. package/lib/api/types.d.ts +3 -3
  19. package/lib/api/types.d.ts.map +1 -1
  20. package/lib/bridge/jsHelpers.d.ts +2 -2
  21. package/lib/bridge/jsHelpers.d.ts.map +1 -1
  22. package/lib/bridge/jsHelpers.js +1 -1
  23. package/lib/bridge/jsHelpers.js.map +1 -1
  24. package/lib/bridge/jsHelpers.test.js +348 -102
  25. package/lib/bridge/jsHelpers.test.js.map +1 -1
  26. package/lib/derivation.d.ts +1 -2
  27. package/lib/derivation.d.ts.map +1 -1
  28. package/lib/derivation.js +48 -32
  29. package/lib/derivation.js.map +1 -1
  30. package/lib/derivation.test.js +168 -62
  31. package/lib/derivation.test.js.map +1 -1
  32. package/lib/mocks/account.d.ts +1 -0
  33. package/lib/mocks/account.d.ts.map +1 -1
  34. package/lib/mocks/account.js.map +1 -1
  35. package/lib/nft/support.d.ts +4 -7
  36. package/lib/nft/support.d.ts.map +1 -1
  37. package/lib/nft/support.js +3 -6
  38. package/lib/nft/support.js.map +1 -1
  39. package/lib/operation.d.ts.map +1 -1
  40. package/lib/operation.js +3 -0
  41. package/lib/operation.js.map +1 -1
  42. package/lib/utils.js +1 -1
  43. package/lib/utils.js.map +1 -1
  44. package/lib/utils.test.js +6 -0
  45. package/lib/utils.test.js.map +1 -1
  46. package/lib-es/account/balanceHistoryCache.d.ts +14 -0
  47. package/lib-es/account/balanceHistoryCache.d.ts.map +1 -1
  48. package/lib-es/account/balanceHistoryCache.js +3 -2
  49. package/lib-es/account/balanceHistoryCache.js.map +1 -1
  50. package/lib-es/account/helpers.d.ts.map +1 -1
  51. package/lib-es/account/helpers.js +0 -13
  52. package/lib-es/account/helpers.js.map +1 -1
  53. package/lib-es/account/index.d.ts +1 -1
  54. package/lib-es/account/index.d.ts.map +1 -1
  55. package/lib-es/account/index.js +1 -1
  56. package/lib-es/account/index.js.map +1 -1
  57. package/lib-es/account/index.test.d.ts +2 -0
  58. package/lib-es/account/index.test.d.ts.map +1 -0
  59. package/lib-es/{account.test.js → account/index.test.js} +6 -6
  60. package/lib-es/account/index.test.js.map +1 -0
  61. package/lib-es/api/types.d.ts +3 -3
  62. package/lib-es/api/types.d.ts.map +1 -1
  63. package/lib-es/bridge/jsHelpers.d.ts +2 -2
  64. package/lib-es/bridge/jsHelpers.d.ts.map +1 -1
  65. package/lib-es/bridge/jsHelpers.js +1 -1
  66. package/lib-es/bridge/jsHelpers.js.map +1 -1
  67. package/lib-es/bridge/jsHelpers.test.js +349 -103
  68. package/lib-es/bridge/jsHelpers.test.js.map +1 -1
  69. package/lib-es/derivation.d.ts +1 -2
  70. package/lib-es/derivation.d.ts.map +1 -1
  71. package/lib-es/derivation.js +42 -22
  72. package/lib-es/derivation.js.map +1 -1
  73. package/lib-es/derivation.test.js +169 -63
  74. package/lib-es/derivation.test.js.map +1 -1
  75. package/lib-es/mocks/account.d.ts +1 -0
  76. package/lib-es/mocks/account.d.ts.map +1 -1
  77. package/lib-es/mocks/account.js.map +1 -1
  78. package/lib-es/nft/support.d.ts +4 -7
  79. package/lib-es/nft/support.d.ts.map +1 -1
  80. package/lib-es/nft/support.js +3 -6
  81. package/lib-es/nft/support.js.map +1 -1
  82. package/lib-es/operation.d.ts.map +1 -1
  83. package/lib-es/operation.js +3 -0
  84. package/lib-es/operation.js.map +1 -1
  85. package/lib-es/utils.js +1 -1
  86. package/lib-es/utils.js.map +1 -1
  87. package/lib-es/utils.test.js +6 -0
  88. package/lib-es/utils.test.js.map +1 -1
  89. package/package.json +5 -5
  90. package/src/account/balanceHistoryCache.ts +4 -2
  91. package/src/account/helpers.ts +0 -15
  92. package/src/{account.test.ts → account/index.test.ts} +7 -7
  93. package/src/account/index.ts +5 -1
  94. package/src/api/types.ts +6 -3
  95. package/src/bridge/jsHelpers.test.ts +401 -104
  96. package/src/bridge/jsHelpers.ts +3 -2
  97. package/src/derivation.test.ts +188 -70
  98. package/src/derivation.ts +55 -27
  99. package/src/mocks/account.ts +1 -0
  100. package/src/nft/support.ts +8 -14
  101. package/src/operation.ts +3 -0
  102. package/src/utils.test.ts +6 -0
  103. package/src/utils.ts +1 -1
  104. package/lib/account.test.d.ts +0 -2
  105. package/lib/account.test.d.ts.map +0 -1
  106. package/lib/account.test.js.map +0 -1
  107. package/lib-es/account.test.d.ts +0 -2
  108. package/lib-es/account.test.d.ts.map +0 -1
  109. package/lib-es/account.test.js.map +0 -1
  110. /package/src/{__snapshots__/account.test.ts.snap → account/__snapshots__/index.test.ts.snap} +0 -0
@@ -1,138 +1,435 @@
1
+ import { getCryptoCurrencyById, listCryptoCurrencies } from "@ledgerhq/cryptoassets/currencies";
2
+ import type {
3
+ Account,
4
+ Operation,
5
+ SyncConfig,
6
+ TokenAccount,
7
+ TransactionCommon,
8
+ } from "@ledgerhq/types-live";
1
9
  import BigNumber from "bignumber.js";
2
10
  import { firstValueFrom } from "rxjs";
3
- import type { Account, SyncConfig, TransactionCommon } from "@ledgerhq/types-live";
4
- import { listCryptoCurrencies } from "@ledgerhq/cryptoassets/currencies";
5
- import { AccountShapeInfo, updateTransaction, makeSync, bip32asBuffer } from "./jsHelpers";
6
-
7
- describe("jsHelpers", () => {
8
- describe("updateTransaction", () => {
9
- it("should not update the transaction object", () => {
10
- const transaction: TransactionCommon = {
11
- recipient: "0x17733CAb76d9A2112576443F21735789733B1ca3",
12
- amount: new BigNumber("10000000000000"),
13
- };
11
+ import {
12
+ AccountShapeInfo,
13
+ bip32asBuffer,
14
+ makeScanAccounts,
15
+ makeSync,
16
+ updateTransaction,
17
+ } from "./jsHelpers";
18
+ import { createEmptyHistoryCache } from "../account/balanceHistoryCache";
14
19
 
15
- const updatedTransaction = updateTransaction(transaction, {
16
- recipient: "0x17733CAb76d9A2112576443F21735789733B1ca3",
17
- amount: new BigNumber("10000000000000"),
18
- });
20
+ describe("updateTransaction", () => {
21
+ it("should not update the transaction object", () => {
22
+ const transaction: TransactionCommon = {
23
+ recipient: "0x17733CAb76d9A2112576443F21735789733B1ca3",
24
+ amount: new BigNumber("10000000000000"),
25
+ };
19
26
 
20
- expect(transaction).toBe(updatedTransaction);
27
+ const updatedTransaction = updateTransaction(transaction, {
28
+ recipient: "0x17733CAb76d9A2112576443F21735789733B1ca3",
29
+ amount: new BigNumber("10000000000000"),
21
30
  });
22
31
 
23
- it("should update the transaction object", () => {
24
- const transaction: TransactionCommon = {
25
- recipient: "0x17733CAb76d9A2112576443F21735789733B1ca3",
26
- amount: new BigNumber("10000000000000"),
27
- };
32
+ expect(transaction).toBe(updatedTransaction);
33
+ });
28
34
 
29
- const updatedTransaction = updateTransaction(transaction, {
30
- recipient: "0x17733CAb76d9A2112576443F21735789733B1ca3",
31
- amount: new BigNumber("20000000000000"),
32
- });
35
+ it("should update the transaction object", () => {
36
+ const transaction: TransactionCommon = {
37
+ recipient: "0x17733CAb76d9A2112576443F21735789733B1ca3",
38
+ amount: new BigNumber("10000000000000"),
39
+ };
40
+
41
+ const updatedTransaction = updateTransaction(transaction, {
42
+ recipient: "0x17733CAb76d9A2112576443F21735789733B1ca3",
43
+ amount: new BigNumber("20000000000000"),
44
+ });
33
45
 
34
- expect(transaction).not.toBe(updatedTransaction);
46
+ expect(transaction).not.toBe(updatedTransaction);
35
47
 
36
- expect(updatedTransaction).toEqual({
37
- ...transaction,
38
- amount: new BigNumber("20000000000000"),
39
- });
48
+ expect(updatedTransaction).toEqual({
49
+ ...transaction,
50
+ amount: new BigNumber("20000000000000"),
40
51
  });
41
52
  });
53
+ });
42
54
 
43
- describe("makeSync", () => {
44
- it("returns a function to update account that give a new instance of account", async () => {
45
- // Given
46
- const account = createAccount({
47
- id: "12",
48
- creationDate: new Date("2024-05-12T17:04:12"),
49
- lastSyncDate: new Date("2024-05-12T17:04:12"),
50
- });
55
+ describe("makeSync", () => {
56
+ it("returns a function to update account that give a new instance of account", async () => {
57
+ // Given
58
+ const account = createAccount({
59
+ id: "12",
60
+ creationDate: new Date("2024-05-12T17:04:12"),
61
+ lastSyncDate: new Date("2024-05-12T17:04:12"),
62
+ });
51
63
 
52
- // When
53
- const accountUpdater = makeSync({
54
- getAccountShape: (_accountShape: AccountShapeInfo) => Promise.resolve({} as Account),
55
- })(account, {} as SyncConfig);
56
- const updater = await firstValueFrom(accountUpdater);
57
- const newAccount = updater(account);
64
+ // When
65
+ const accountUpdater = makeSync({
66
+ getAccountShape: (_accountShape: AccountShapeInfo) => Promise.resolve({} as Account),
67
+ })(account, {} as SyncConfig);
68
+ const updater = await firstValueFrom(accountUpdater);
69
+ const newAccount = updater(account);
58
70
 
59
- // Then
60
- const nonUpdatedFields = {
61
- ...account,
62
- id: expect.any(String),
71
+ // Then
72
+ const nonUpdatedFields = {
73
+ ...account,
74
+ id: expect.any(String),
75
+ creationDate: expect.any(Date),
76
+ lastSyncDate: expect.any(Date),
77
+ subAccounts: [],
78
+ };
79
+ expect(newAccount).toEqual(nonUpdatedFields);
80
+ expect(newAccount.id).not.toEqual(account.id);
81
+ expect(newAccount.creationDate).not.toEqual(account.creationDate);
82
+ expect(newAccount.lastSyncDate).not.toEqual(account.lastSyncDate);
83
+ });
84
+
85
+ it("returns a account with a corrected formatted id", async () => {
86
+ // Given
87
+ const account = createAccount({
88
+ id: "12",
89
+ creationDate: new Date("2024-05-12T17:04:12"),
90
+ lastSyncDate: new Date("2024-05-12T17:04:12"),
91
+ });
92
+
93
+ // When
94
+ const accountUpdater = makeSync({
95
+ getAccountShape: (_accountShape: AccountShapeInfo) => Promise.resolve({} as Account),
96
+ })(account, {} as SyncConfig);
97
+ const updater = await firstValueFrom(accountUpdater);
98
+ const newAccount = updater(account);
99
+
100
+ // Then
101
+ const expectedAccount = {
102
+ ...account,
103
+ id: "js:2:bitcoin::",
104
+ subAccounts: undefined,
105
+ };
106
+ expect(newAccount.id).toEqual(expectedAccount.id);
107
+ });
108
+ });
109
+
110
+ describe("makeScanAccounts", () => {
111
+ it("returns the first account found when there is no one used previously", async () => {
112
+ // Given
113
+ const addressResolver = {
114
+ address: "address",
115
+ path: "path",
116
+ publicKey: "publicKey",
117
+ };
118
+ const syncConfig = {
119
+ paginationConfig: {},
120
+ };
121
+ const deviceId = "deviceId";
122
+ const currency = getCryptoCurrencyById("algorand");
123
+
124
+ // When
125
+ const scanAccounts = makeScanAccounts({
126
+ getAccountShape: (accountShape, config) => {
127
+ expect(config).toBe(syncConfig);
128
+ expect(accountShape.currency).toBe(currency);
129
+ expect(accountShape.address).toBe(addressResolver.address);
130
+ expect(accountShape.derivationPath).toBe(addressResolver.path);
131
+ expect(accountShape.deviceId).toBe(deviceId);
132
+ return Promise.resolve({
133
+ id: "1234",
134
+ balanceHistoryCache: createEmptyHistoryCache(),
135
+ });
136
+ },
137
+ getAddressFn: (_deviceId, _addressOpt) => Promise.resolve(addressResolver),
138
+ });
139
+
140
+ // Then
141
+ const result = await firstValueFrom(
142
+ scanAccounts({
143
+ currency,
144
+ deviceId,
145
+ syncConfig,
146
+ }),
147
+ );
148
+
149
+ expect(result).toEqual({
150
+ account: {
151
+ id: "1234",
152
+ balance: new BigNumber("0"),
153
+ balanceHistoryCache: createEmptyHistoryCache(),
154
+ blockHeight: 0,
63
155
  creationDate: expect.any(Date),
156
+ currency,
157
+ derivationMode: "",
158
+ freshAddress: addressResolver.address,
159
+ freshAddressPath: addressResolver.path,
160
+ index: 0,
64
161
  lastSyncDate: expect.any(Date),
65
- subAccounts: [],
66
- };
67
- expect(newAccount).toEqual(nonUpdatedFields);
68
- expect(newAccount.id).not.toEqual(account.id);
69
- expect(newAccount.creationDate).not.toEqual(account.creationDate);
70
- expect(newAccount.lastSyncDate).not.toEqual(account.lastSyncDate);
162
+ operations: [],
163
+ operationsCount: 0,
164
+ pendingOperations: [],
165
+ seedIdentifier: addressResolver.publicKey,
166
+ spendableBalance: new BigNumber("0"),
167
+ swapHistory: [],
168
+ type: "Account",
169
+ used: false,
170
+ },
171
+ type: "discovered",
172
+ });
173
+ });
174
+
175
+ it("throws an error when the GetAccountShape does not return an id", async () => {
176
+ // Given
177
+ const addressResolver = {
178
+ address: "address",
179
+ path: "path",
180
+ publicKey: "publicKey",
181
+ };
182
+
183
+ // When
184
+ const scanAccounts = makeScanAccounts({
185
+ getAccountShape: () =>
186
+ Promise.resolve({
187
+ balanceHistoryCache: createEmptyHistoryCache(),
188
+ }),
189
+ getAddressFn: (_deviceId, _addressOpt) => Promise.resolve(addressResolver),
190
+ });
191
+
192
+ // Then
193
+ expect(
194
+ firstValueFrom(
195
+ scanAccounts({
196
+ currency: getCryptoCurrencyById("algorand"),
197
+ deviceId: "deviceId",
198
+ syncConfig: {
199
+ paginationConfig: {},
200
+ },
201
+ }),
202
+ ),
203
+ ).rejects.toThrow("account ID must be provided");
204
+ });
205
+
206
+ it("throws an error when the GetAccountShape returns an incorrect balance", async () => {
207
+ // Given
208
+ const addressResolver = {
209
+ address: "address",
210
+ path: "path",
211
+ publicKey: "publicKey",
212
+ };
213
+
214
+ // When
215
+ const scanAccounts = makeScanAccounts({
216
+ getAccountShape: () =>
217
+ Promise.resolve({
218
+ id: "1234",
219
+ balance: new BigNumber("NULL"),
220
+ balanceHistoryCache: createEmptyHistoryCache(),
221
+ }),
222
+ getAddressFn: (_deviceId, _addressOpt) => Promise.resolve(addressResolver),
223
+ });
224
+
225
+ // Then
226
+ expect(
227
+ firstValueFrom(
228
+ scanAccounts({
229
+ currency: getCryptoCurrencyById("algorand"),
230
+ deviceId: "deviceId",
231
+ syncConfig: {
232
+ paginationConfig: {},
233
+ },
234
+ }),
235
+ ),
236
+ ).rejects.toThrow("invalid balance NaN");
237
+ });
238
+
239
+ it.each([
240
+ {
241
+ balance: new BigNumber(1_000_000),
242
+ operationsCount: 0,
243
+ },
244
+ {
245
+ balance: new BigNumber(0),
246
+ operationsCount: 1,
247
+ },
248
+ {
249
+ balance: new BigNumber(0),
250
+ operations: [{}] as Operation[],
251
+ },
252
+ {
253
+ balance: new BigNumber(0),
254
+ operationsCount: 0,
255
+ subAccounts: [{}] as TokenAccount[],
256
+ },
257
+ ])("returns an account flag as used", async account => {
258
+ // Given
259
+ const addressResolver = {
260
+ address: "address",
261
+ path: "path",
262
+ publicKey: "publicKey",
263
+ };
264
+ const currency = getCryptoCurrencyById("algorand");
265
+
266
+ // When
267
+ const scanAccounts = makeScanAccounts({
268
+ getAccountShape: () =>
269
+ Promise.resolve({
270
+ ...account,
271
+ id: "1234",
272
+ balanceHistoryCache: createEmptyHistoryCache(),
273
+ }),
274
+ getAddressFn: (_deviceId, _addressOpt) => Promise.resolve(addressResolver),
71
275
  });
72
276
 
73
- it("returns a account with a corrected formatted id", async () => {
277
+ // Then
278
+ const result = await firstValueFrom(
279
+ scanAccounts({
280
+ currency,
281
+ deviceId: "deviceId",
282
+ syncConfig: {
283
+ paginationConfig: {},
284
+ },
285
+ }),
286
+ );
287
+
288
+ expect(result.account.used).toBe(true);
289
+ });
290
+
291
+ it.each([
292
+ {
293
+ account: {
294
+ balance: new BigNumber(0),
295
+ },
296
+ expectedUsed: false,
297
+ },
298
+ {
299
+ account: {
300
+ balance: new BigNumber(0),
301
+ used: true,
302
+ },
303
+ expectedUsed: true,
304
+ },
305
+ {
306
+ account: {
307
+ balance: new BigNumber(1_000_000),
308
+ used: false,
309
+ },
310
+ expectedUsed: false,
311
+ },
312
+ {
313
+ account: {
314
+ balance: new BigNumber(1_000_000),
315
+ },
316
+ expectedUsed: true,
317
+ },
318
+ ])(
319
+ "overrides the used flag only if not returned by the getAccountShape",
320
+ async ({ account, expectedUsed }) => {
74
321
  // Given
75
- const account = createAccount({
76
- id: "12",
77
- creationDate: new Date("2024-05-12T17:04:12"),
78
- lastSyncDate: new Date("2024-05-12T17:04:12"),
79
- });
322
+ const addressResolver = {
323
+ address: "address",
324
+ path: "path",
325
+ publicKey: "publicKey",
326
+ };
327
+ const currency = getCryptoCurrencyById("algorand");
80
328
 
81
329
  // When
82
- const accountUpdater = makeSync({
83
- getAccountShape: (_accountShape: AccountShapeInfo) => Promise.resolve({} as Account),
84
- })(account, {} as SyncConfig);
85
- const updater = await firstValueFrom(accountUpdater);
86
- const newAccount = updater(account);
330
+ const scanAccounts = makeScanAccounts({
331
+ getAccountShape: () =>
332
+ Promise.resolve({
333
+ ...account,
334
+ id: "1234",
335
+ balanceHistoryCache: createEmptyHistoryCache(),
336
+ }),
337
+ getAddressFn: (_deviceId, _addressOpt) => Promise.resolve(addressResolver),
338
+ });
87
339
 
88
340
  // Then
89
- const expectedAccount = {
90
- ...account,
91
- id: "js:2:bitcoin::",
92
- subAccounts: undefined,
93
- };
94
- expect(newAccount.id).toEqual(expectedAccount.id);
341
+ const result = await firstValueFrom(
342
+ scanAccounts({
343
+ currency,
344
+ deviceId: "deviceId",
345
+ syncConfig: {
346
+ paginationConfig: {},
347
+ },
348
+ }),
349
+ );
350
+
351
+ expect(result.account.used).toEqual(expectedUsed);
352
+ },
353
+ );
354
+
355
+ it("returns all accounts found until the first not used yet", done => {
356
+ // Given
357
+ const addressResolver = {
358
+ address: "address",
359
+ path: "path",
360
+ publicKey: "publicKey",
361
+ };
362
+
363
+ const USED_ACCOUNT_NUMN = 2;
364
+ let scanCall = 0;
365
+ const scanAccounts = makeScanAccounts({
366
+ getAccountShape: () => {
367
+ scanCall++;
368
+ const used = scanCall > USED_ACCOUNT_NUMN ? false : true;
369
+ return Promise.resolve({
370
+ used,
371
+ id: `1234${scanCall}`,
372
+ balanceHistoryCache: createEmptyHistoryCache(),
373
+ });
374
+ },
375
+ getAddressFn: (_deviceId, _addressOpt) => Promise.resolve(addressResolver),
95
376
  });
96
- });
97
377
 
98
- describe("bip32asBuffer", () => {
99
- it.each([
100
- {
101
- name: "Simple",
102
- derivationPath: "m/44'/1/1/0",
103
- expectedResult: "048000002c000000010000000100000000",
378
+ // When
379
+ let subscribeCalled = 0;
380
+ scanAccounts({
381
+ currency: getCryptoCurrencyById("algorand"),
382
+ deviceId: "deviceId",
383
+ syncConfig: {
384
+ paginationConfig: {},
104
385
  },
105
- {
106
- name: "Hardened coin type",
107
- derivationPath: "m/44'/1'/1/0",
108
- expectedResult: "048000002c800000010000000100000000",
386
+ }).subscribe({
387
+ // Then
388
+ next: ({ account, type }) => {
389
+ subscribeCalled++;
390
+ expect(type).toEqual("discovered");
391
+ expect(["12341", "12342", "12343"]).toContain(account.id);
392
+ if (account.id === "12343") {
393
+ expect(account.used).toBeFalsy();
394
+ } else {
395
+ expect(account.used).toBeTruthy();
396
+ }
109
397
  },
110
- {
111
- name: "Hardened account",
112
- derivationPath: "m/44'/1'/1'/0",
113
- expectedResult: "048000002c800000018000000100000000",
398
+ complete: () => {
399
+ try {
400
+ // Expect that the 3 account have been found, 2 existing one + the usuned one
401
+ expect(subscribeCalled).toEqual(USED_ACCOUNT_NUMN + 1);
402
+ done();
403
+ } catch (e) {
404
+ done(e);
405
+ }
114
406
  },
115
- ])("converts path for AppCoins with $name case", ({ derivationPath, expectedResult }) => {
116
- const path = bip32asBuffer(derivationPath);
117
- expect(path).toEqual(Buffer.from(expectedResult, "hex"));
118
407
  });
119
408
  });
120
409
  });
121
410
 
122
- const emptyHistoryCache = {
123
- HOUR: {
124
- latestDate: null,
125
- balances: [],
126
- },
127
- DAY: {
128
- latestDate: null,
129
- balances: [],
130
- },
131
- WEEK: {
132
- latestDate: null,
133
- balances: [],
134
- },
135
- };
411
+ describe("bip32asBuffer", () => {
412
+ it.each([
413
+ {
414
+ name: "Simple",
415
+ derivationPath: "m/44'/1/1/0",
416
+ expectedResult: "048000002c000000010000000100000000",
417
+ },
418
+ {
419
+ name: "Hardened coin type",
420
+ derivationPath: "m/44'/1'/1/0",
421
+ expectedResult: "048000002c800000010000000100000000",
422
+ },
423
+ {
424
+ name: "Hardened account",
425
+ derivationPath: "m/44'/1'/1'/0",
426
+ expectedResult: "048000002c800000018000000100000000",
427
+ },
428
+ ])("converts path for AppCoins with $name case", ({ derivationPath, expectedResult }) => {
429
+ const path = bip32asBuffer(derivationPath);
430
+ expect(path).toEqual(Buffer.from(expectedResult, "hex"));
431
+ });
432
+ });
136
433
 
137
434
  // Call once for all tests the currencies. Relies on real implementation to check also consistency.
138
435
  const bitcoinCurrency = listCryptoCurrencies(true).find(c => c.id === "bitcoin")!;
@@ -158,7 +455,7 @@ function createAccount(init: Partial<Account>): Account {
158
455
  pendingOperations: [],
159
456
  lastSyncDate: new Date(),
160
457
  // subAccounts: [],
161
- balanceHistoryCache: emptyHistoryCache,
458
+ balanceHistoryCache: createEmptyHistoryCache(),
162
459
  swapHistory: [],
163
460
  };
164
461
  }
@@ -180,6 +180,7 @@ export const makeSync =
180
180
  T extends TransactionCommon = TransactionCommon,
181
181
  A extends Account = Account,
182
182
  U extends TransactionStatusCommon = TransactionStatusCommon,
183
+ O extends Operation = Operation,
183
184
  R extends AccountRaw = AccountRaw,
184
185
  >({
185
186
  getAccountShape,
@@ -189,7 +190,7 @@ export const makeSync =
189
190
  getAccountShape: GetAccountShape<A>;
190
191
  postSync?: (initial: A, synced: A) => A;
191
192
  shouldMergeOps?: boolean;
192
- }): AccountBridge<T, A, U, R>["sync"] =>
193
+ }): AccountBridge<T, A, U, O, R>["sync"] =>
193
194
  (initial, syncConfig): Observable<AccountUpdater<A>> =>
194
195
  new Observable((o: Observer<(acc: A) => A>) => {
195
196
  async function main() {
@@ -384,7 +385,7 @@ export const makeScanAccounts =
384
385
  account.balanceHistoryCache = generateHistoryFromOperations(account);
385
386
  }
386
387
 
387
- if (!account.used) {
388
+ if (accountShape.used === undefined) {
388
389
  account.used = !isAccountEmpty(account);
389
390
  }
390
391