@ledgerhq/coin-canton 0.12.0-nightly.20251211024123 → 0.12.0-nightly.20251212024049

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 (94) hide show
  1. package/CHANGELOG.md +15 -9
  2. package/lib/bridge/buildSubAccounts.d.ts +23 -0
  3. package/lib/bridge/buildSubAccounts.d.ts.map +1 -0
  4. package/lib/bridge/buildSubAccounts.js +80 -0
  5. package/lib/bridge/buildSubAccounts.js.map +1 -0
  6. package/lib/bridge/getTransactionStatus.d.ts.map +1 -1
  7. package/lib/bridge/getTransactionStatus.js +45 -24
  8. package/lib/bridge/getTransactionStatus.js.map +1 -1
  9. package/lib/bridge/index.d.ts.map +1 -1
  10. package/lib/bridge/index.js +2 -0
  11. package/lib/bridge/index.js.map +1 -1
  12. package/lib/bridge/prepareTransaction.d.ts.map +1 -1
  13. package/lib/bridge/prepareTransaction.js +19 -3
  14. package/lib/bridge/prepareTransaction.js.map +1 -1
  15. package/lib/bridge/signOperation.d.ts.map +1 -1
  16. package/lib/bridge/signOperation.js +3 -0
  17. package/lib/bridge/signOperation.js.map +1 -1
  18. package/lib/bridge/sync.d.ts +3 -0
  19. package/lib/bridge/sync.d.ts.map +1 -1
  20. package/lib/bridge/sync.js +115 -19
  21. package/lib/bridge/sync.js.map +1 -1
  22. package/lib/bridge/validateAddress.d.ts +3 -0
  23. package/lib/bridge/validateAddress.d.ts.map +1 -0
  24. package/lib/bridge/validateAddress.js +8 -0
  25. package/lib/bridge/validateAddress.js.map +1 -0
  26. package/lib/common-logic/account/getBalance.d.ts +1 -0
  27. package/lib/common-logic/account/getBalance.d.ts.map +1 -1
  28. package/lib/common-logic/account/getBalance.js +1 -0
  29. package/lib/common-logic/account/getBalance.js.map +1 -1
  30. package/lib/common-logic/transaction/craftTransaction.d.ts +1 -0
  31. package/lib/common-logic/transaction/craftTransaction.d.ts.map +1 -1
  32. package/lib/common-logic/transaction/craftTransaction.js +3 -0
  33. package/lib/common-logic/transaction/craftTransaction.js.map +1 -1
  34. package/lib/network/gateway.d.ts +30 -12
  35. package/lib/network/gateway.d.ts.map +1 -1
  36. package/lib/network/gateway.js +44 -1
  37. package/lib/network/gateway.js.map +1 -1
  38. package/lib/types/bridge.d.ts +2 -0
  39. package/lib/types/bridge.d.ts.map +1 -1
  40. package/lib-es/bridge/buildSubAccounts.d.ts +23 -0
  41. package/lib-es/bridge/buildSubAccounts.d.ts.map +1 -0
  42. package/lib-es/bridge/buildSubAccounts.js +74 -0
  43. package/lib-es/bridge/buildSubAccounts.js.map +1 -0
  44. package/lib-es/bridge/getTransactionStatus.d.ts.map +1 -1
  45. package/lib-es/bridge/getTransactionStatus.js +46 -25
  46. package/lib-es/bridge/getTransactionStatus.js.map +1 -1
  47. package/lib-es/bridge/index.d.ts.map +1 -1
  48. package/lib-es/bridge/index.js +2 -0
  49. package/lib-es/bridge/index.js.map +1 -1
  50. package/lib-es/bridge/prepareTransaction.d.ts.map +1 -1
  51. package/lib-es/bridge/prepareTransaction.js +19 -3
  52. package/lib-es/bridge/prepareTransaction.js.map +1 -1
  53. package/lib-es/bridge/signOperation.d.ts.map +1 -1
  54. package/lib-es/bridge/signOperation.js +3 -0
  55. package/lib-es/bridge/signOperation.js.map +1 -1
  56. package/lib-es/bridge/sync.d.ts +3 -0
  57. package/lib-es/bridge/sync.d.ts.map +1 -1
  58. package/lib-es/bridge/sync.js +115 -20
  59. package/lib-es/bridge/sync.js.map +1 -1
  60. package/lib-es/bridge/validateAddress.d.ts +3 -0
  61. package/lib-es/bridge/validateAddress.d.ts.map +1 -0
  62. package/lib-es/bridge/validateAddress.js +5 -0
  63. package/lib-es/bridge/validateAddress.js.map +1 -0
  64. package/lib-es/common-logic/account/getBalance.d.ts +1 -0
  65. package/lib-es/common-logic/account/getBalance.d.ts.map +1 -1
  66. package/lib-es/common-logic/account/getBalance.js +1 -0
  67. package/lib-es/common-logic/account/getBalance.js.map +1 -1
  68. package/lib-es/common-logic/transaction/craftTransaction.d.ts +1 -0
  69. package/lib-es/common-logic/transaction/craftTransaction.d.ts.map +1 -1
  70. package/lib-es/common-logic/transaction/craftTransaction.js +3 -0
  71. package/lib-es/common-logic/transaction/craftTransaction.js.map +1 -1
  72. package/lib-es/network/gateway.d.ts +30 -12
  73. package/lib-es/network/gateway.d.ts.map +1 -1
  74. package/lib-es/network/gateway.js +41 -0
  75. package/lib-es/network/gateway.js.map +1 -1
  76. package/lib-es/types/bridge.d.ts +2 -0
  77. package/lib-es/types/bridge.d.ts.map +1 -1
  78. package/package.json +9 -9
  79. package/src/bridge/buildSubAccounts.test.ts +120 -0
  80. package/src/bridge/buildSubAccounts.ts +132 -0
  81. package/src/bridge/getTransactionStatus.ts +53 -22
  82. package/src/bridge/index.ts +2 -0
  83. package/src/bridge/prepareTransaction.ts +29 -4
  84. package/src/bridge/signOperation.ts +4 -0
  85. package/src/bridge/sync.test.ts +237 -191
  86. package/src/bridge/sync.ts +154 -24
  87. package/src/bridge/validateAddress.test.ts +25 -0
  88. package/src/bridge/validateAddress.ts +9 -0
  89. package/src/common-logic/account/getBalance.ts +2 -0
  90. package/src/common-logic/transaction/craftTransaction.ts +5 -0
  91. package/src/network/gateway.test.ts +169 -0
  92. package/src/network/gateway.ts +83 -12
  93. package/src/types/bridge.ts +2 -0
  94. package/tsconfig.json +7 -2
@@ -1,11 +1,12 @@
1
- import { makeGetAccountShape } from "./sync";
2
- import { OperationInfo, type InstrumentBalance } from "../network/gateway";
1
+ import { makeGetAccountShape, filterDisabledTokenAccounts } from "./sync";
2
+ import { OperationInfo } from "../network/gateway";
3
3
  import * as gateway from "../network/gateway";
4
4
  import * as onboard from "./onboard";
5
5
  import * as config from "../config";
6
+ import * as accountBalance from "../common-logic/account/getBalance";
6
7
  import resolver from "../signer";
7
8
  import { AccountShapeInfo } from "@ledgerhq/coin-framework/bridge/jsHelpers";
8
- import { Account } from "@ledgerhq/types-live";
9
+ import { Account, TokenAccount } from "@ledgerhq/types-live";
9
10
  import BigNumber from "bignumber.js";
10
11
  import { CantonAccount } from "../types";
11
12
  import { createMockCantonCurrency } from "../test/fixtures";
@@ -14,34 +15,76 @@ jest.mock("../network/gateway");
14
15
  jest.mock("../signer");
15
16
  jest.mock("../config");
16
17
  jest.mock("./onboard");
18
+ jest.mock("../common-logic/account/getBalance");
19
+ jest.mock("@ledgerhq/cryptoassets/state", () => {
20
+ const store = {
21
+ findTokenByAddressInCurrency: jest.fn().mockResolvedValue(undefined),
22
+ };
23
+ return {
24
+ getCryptoAssetsStore: jest.fn(() => store),
25
+ };
26
+ });
17
27
 
18
- const mockedGetBalance = gateway.getBalance as jest.Mock;
28
+ const mockedGetBalance = accountBalance.getBalance as jest.Mock;
19
29
  const mockedGetLedgerEnd = gateway.getLedgerEnd as jest.Mock;
20
30
  const mockedGetOperations = gateway.getOperations as jest.Mock;
31
+ const mockedGetPendingTransferProposals = gateway.getPendingTransferProposals as jest.Mock;
32
+ const mockedGetCalTokensCached = gateway.getCalTokensCached as unknown as jest.Mock;
33
+ const mockedGetEnabledInstrumentsCached =
34
+ gateway.getEnabledInstrumentsCached as unknown as jest.Mock;
21
35
  const mockedResolver = resolver as jest.Mock;
22
36
  const mockedIsOnboarded = onboard.isAccountOnboarded as jest.Mock;
23
37
  const mockedIsAuthorized = onboard.isCantonCoinPreapproved as jest.Mock;
24
38
  const mockedCoinConfig = config.default.getCoinConfig as jest.Mock;
25
39
 
26
- const sampleCurrency = {
27
- id: "testcoin",
40
+ const sampleCurrency = createMockCantonCurrency();
41
+
42
+ type CantonBalance = {
43
+ value: bigint;
44
+ locked: bigint;
45
+ asset: { type: "native" } | { type: "token"; assetReference: string };
46
+ utxoCount: number;
47
+ instrumentId: string;
48
+ adminId: string;
28
49
  };
29
50
 
30
- const createMockInstrumentBalances = (): InstrumentBalance[] => [
31
- {
32
- instrument_id: "Native",
33
- amount: "1000",
34
- locked: false,
35
- utxo_count: 1,
36
- admin_id: "admin1",
37
- },
38
- ];
39
-
40
- const createMockOperationView = (): OperationInfo =>
51
+ const createMockNativeBalance = (amount: string, locked = false): CantonBalance => ({
52
+ value: BigInt(amount),
53
+ locked: locked ? BigInt(amount) : BigInt(0),
54
+ asset: { type: "native" },
55
+ utxoCount: 1,
56
+ instrumentId: "Amulet",
57
+ adminId: "native-admin",
58
+ });
59
+
60
+ const createMockTokenBalance = (
61
+ instrumentId: string,
62
+ amount: string,
63
+ locked = false,
64
+ ): CantonBalance => ({
65
+ value: BigInt(amount),
66
+ locked: locked ? BigInt(amount) : BigInt(0),
67
+ asset: { type: "token", assetReference: instrumentId },
68
+ utxoCount: 1,
69
+ instrumentId,
70
+ adminId: `admin-${instrumentId}`,
71
+ });
72
+
73
+ const createMockOperationView = (
74
+ overrides: {
75
+ instrumentId?: string;
76
+ instrumentAdmin?: string | null;
77
+ txHash?: string;
78
+ uid?: string;
79
+ type?: string;
80
+ value?: string;
81
+ operationType?: string;
82
+ } = {},
83
+ ): OperationInfo =>
41
84
  ({
42
- transaction_hash: "tx-test",
43
- uid: "uid-test",
44
- type: "Send",
85
+ transaction_hash: overrides.txHash ?? "tx-test",
86
+ uid: overrides.uid ?? "uid-test",
87
+ type: overrides.type ?? "Send",
45
88
  status: "Success",
46
89
  fee: {
47
90
  value: "5",
@@ -57,10 +100,10 @@ const createMockOperationView = (): OperationInfo =>
57
100
  {
58
101
  address: "party123",
59
102
  type: "Send",
60
- value: "100",
61
- asset: "Native",
103
+ value: overrides.value ?? "100",
104
+ asset: overrides.instrumentId ?? "Amulet",
62
105
  details: {
63
- operationType: "transfer",
106
+ operationType: overrides.operationType ?? "transfer",
64
107
  metadata: {
65
108
  reason: "test transfer",
66
109
  },
@@ -76,11 +119,11 @@ const createMockOperationView = (): OperationInfo =>
76
119
  hash: "blockhash1",
77
120
  },
78
121
  asset: {
79
- type: "native",
80
- issuer: null,
122
+ instrumentId: overrides.instrumentId ?? "Amulet",
123
+ instrumentAdmin: overrides.instrumentAdmin ?? null,
81
124
  },
82
125
  details: {
83
- operationType: "transfer",
126
+ operationType: overrides.operationType ?? "transfer",
84
127
  },
85
128
  }) as OperationInfo;
86
129
 
@@ -125,49 +168,27 @@ describe("makeGetAccountShape", () => {
125
168
  });
126
169
 
127
170
  mockedCoinConfig.mockReturnValue({
128
- nativeInstrumentId: "Native",
171
+ nativeInstrumentId: "Amulet",
129
172
  minReserve: "0",
130
173
  useGateway: true,
131
174
  });
132
175
 
133
176
  mockedIsAuthorized.mockResolvedValue(true);
134
177
  mockedGetLedgerEnd.mockResolvedValue(12345);
178
+ mockedGetPendingTransferProposals.mockResolvedValue([]);
179
+ mockedGetCalTokensCached.mockResolvedValue(new Map());
135
180
  });
136
181
 
137
182
  it("should return a valid account shape with correct balances and operations", async () => {
138
- mockedGetBalance.mockResolvedValue([
139
- {
140
- instrument_id: "Native",
141
- amount: "1000",
142
- locked: false,
143
- utxo_count: 1,
144
- },
145
- ]);
183
+ mockedGetBalance.mockResolvedValue([createMockNativeBalance("1000")]);
146
184
  mockedGetOperations.mockResolvedValue({
147
185
  operations: [
148
- {
149
- transaction_hash: "tx1",
186
+ createMockOperationView({
187
+ txHash: "tx1",
150
188
  uid: "uid1",
151
189
  type: "Send",
152
- fee: { value: "5" },
153
- transfers: [
154
- {
155
- value: "100",
156
- details: {
157
- metadata: {
158
- reason: "test transfer",
159
- },
160
- },
161
- },
162
- ],
163
- transaction_timestamp: new Date().toISOString(),
164
- senders: ["party123"],
165
- recipients: ["party456"],
166
- block: {
167
- height: 1,
168
- hash: "blockhash1",
169
- },
170
- } as OperationInfo,
190
+ value: "100",
191
+ }),
171
192
  ],
172
193
  });
173
194
  const getAccountShape = makeGetAccountShape(fakeSignerContext);
@@ -186,18 +207,8 @@ describe("makeGetAccountShape", () => {
186
207
 
187
208
  it("should handle locked balances correctly", async () => {
188
209
  mockedGetBalance.mockResolvedValue([
189
- {
190
- instrument_id: "Native",
191
- amount: "1000",
192
- locked: true,
193
- utxo_count: 1,
194
- },
195
- {
196
- instrument_id: "Native",
197
- amount: "10",
198
- locked: false,
199
- utxo_count: 1,
200
- },
210
+ createMockNativeBalance("1000", true),
211
+ createMockNativeBalance("10", false),
201
212
  ]);
202
213
 
203
214
  const getAccountShape = makeGetAccountShape(fakeSignerContext);
@@ -207,7 +218,7 @@ describe("makeGetAccountShape", () => {
207
218
 
208
219
  expect(shape).toBeDefined();
209
220
  expect(shape.balance).toEqual(BigNumber(1010));
210
- expect(shape.spendableBalance).toEqual(BigNumber(1010));
221
+ expect(shape.spendableBalance).toEqual(BigNumber(10));
211
222
  });
212
223
 
213
224
  it("should handle empty balances correctly", async () => {
@@ -224,39 +235,15 @@ describe("makeGetAccountShape", () => {
224
235
  });
225
236
 
226
237
  it("should default to FEES operation type when transferValue is 0", async () => {
227
- mockedGetBalance.mockResolvedValue([
228
- {
229
- instrument_id: "Native",
230
- amount: "1000",
231
- locked: false,
232
- utxo_count: 1,
233
- },
234
- ]);
238
+ mockedGetBalance.mockResolvedValue([createMockNativeBalance("1000")]);
235
239
  mockedGetOperations.mockResolvedValue({
236
240
  operations: [
237
- {
238
- transaction_hash: "tx2",
241
+ createMockOperationView({
242
+ txHash: "tx2",
239
243
  uid: "uid2",
240
244
  type: "Send",
241
- fee: { value: "3" },
242
- transfers: [
243
- {
244
- value: "0",
245
- details: {
246
- metadata: {
247
- reason: "fee only",
248
- },
249
- },
250
- },
251
- ],
252
- transaction_timestamp: new Date().toISOString(),
253
- senders: ["party123"],
254
- recipients: ["party456"],
255
- block: {
256
- height: 2,
257
- hash: "blockhash2",
258
- },
259
- } as OperationInfo,
245
+ value: "0",
246
+ }),
260
247
  ],
261
248
  });
262
249
 
@@ -267,44 +254,20 @@ describe("makeGetAccountShape", () => {
267
254
  expect(shape).toBeDefined();
268
255
  expect(shape.operations[0].type).toBe("FEES");
269
256
  // In this case, value should equal the fee
270
- expect(shape.operations[0].value).toEqual(BigNumber(3));
257
+ expect(shape.operations[0].value).toEqual(BigNumber(5)); // fee is 5 in createMockOperationView
271
258
  });
272
259
 
273
260
  it("should set operation type to TRANSFER_PROPOSAL when operationType is transfer-proposal", async () => {
274
- mockedGetBalance.mockResolvedValue([
275
- {
276
- instrument_id: "Native",
277
- amount: "1000",
278
- locked: false,
279
- utxo_count: 1,
280
- },
281
- ]);
261
+ mockedGetBalance.mockResolvedValue([createMockNativeBalance("1000")]);
282
262
  mockedGetOperations.mockResolvedValue({
283
263
  operations: [
284
- {
285
- transaction_hash: "tx3",
264
+ createMockOperationView({
265
+ txHash: "tx3",
286
266
  uid: "uid3",
287
267
  type: "Send",
288
- fee: { value: "5" },
289
- transfers: [
290
- {
291
- value: "200",
292
- details: {
293
- operationType: "transfer-proposal",
294
- metadata: {
295
- reason: "transfer proposal",
296
- },
297
- },
298
- },
299
- ],
300
- transaction_timestamp: new Date().toISOString(),
301
- senders: ["party123"],
302
- recipients: ["party456"],
303
- block: {
304
- height: 3,
305
- hash: "blockhash3",
306
- },
307
- } as OperationInfo,
268
+ value: "200",
269
+ operationType: "transfer-proposal",
270
+ }),
308
271
  ],
309
272
  });
310
273
 
@@ -318,40 +281,16 @@ describe("makeGetAccountShape", () => {
318
281
  });
319
282
 
320
283
  it("should set operation type to TRANSFER_REJECTED when operationType is transfer-rejected", async () => {
321
- mockedGetBalance.mockResolvedValue([
322
- {
323
- instrument_id: "Native",
324
- amount: "1000",
325
- locked: false,
326
- utxo_count: 1,
327
- },
328
- ]);
284
+ mockedGetBalance.mockResolvedValue([createMockNativeBalance("1000")]);
329
285
  mockedGetOperations.mockResolvedValue({
330
286
  operations: [
331
- {
332
- transaction_hash: "tx4",
287
+ createMockOperationView({
288
+ txHash: "tx4",
333
289
  uid: "uid4",
334
290
  type: "Send",
335
- fee: { value: "2" },
336
- transfers: [
337
- {
338
- value: "150",
339
- details: {
340
- operationType: "transfer-rejected",
341
- metadata: {
342
- reason: "transfer rejected",
343
- },
344
- },
345
- },
346
- ],
347
- transaction_timestamp: new Date().toISOString(),
348
- senders: ["party123"],
349
- recipients: ["party456"],
350
- block: {
351
- height: 4,
352
- hash: "blockhash4",
353
- },
354
- } as OperationInfo,
291
+ value: "150",
292
+ operationType: "transfer-rejected",
293
+ }),
355
294
  ],
356
295
  });
357
296
 
@@ -365,40 +304,16 @@ describe("makeGetAccountShape", () => {
365
304
  });
366
305
 
367
306
  it("should set operation type to TRANSFER_WITHDRAWN when operationType is transfer-withdrawn", async () => {
368
- mockedGetBalance.mockResolvedValue([
369
- {
370
- instrument_id: "Native",
371
- amount: "1000",
372
- locked: false,
373
- utxo_count: 1,
374
- },
375
- ]);
307
+ mockedGetBalance.mockResolvedValue([createMockNativeBalance("1000")]);
376
308
  mockedGetOperations.mockResolvedValue({
377
309
  operations: [
378
- {
379
- transaction_hash: "tx5",
310
+ createMockOperationView({
311
+ txHash: "tx5",
380
312
  uid: "uid5",
381
313
  type: "Send",
382
- fee: { value: "1" },
383
- transfers: [
384
- {
385
- value: "50",
386
- details: {
387
- operationType: "transfer-withdrawn",
388
- metadata: {
389
- reason: "transfer withdrawn",
390
- },
391
- },
392
- },
393
- ],
394
- transaction_timestamp: new Date().toISOString(),
395
- senders: ["party123"],
396
- recipients: ["party456"],
397
- block: {
398
- height: 5,
399
- hash: "blockhash5",
400
- },
401
- } as OperationInfo,
314
+ value: "50",
315
+ operationType: "transfer-withdrawn",
316
+ }),
402
317
  ],
403
318
  });
404
319
 
@@ -412,7 +327,7 @@ describe("makeGetAccountShape", () => {
412
327
  });
413
328
 
414
329
  it("should sync without device when account has xpub but no publicKey", async () => {
415
- mockedGetBalance.mockResolvedValue(createMockInstrumentBalances());
330
+ mockedGetBalance.mockResolvedValue([createMockNativeBalance("1000")]);
416
331
  mockedGetOperations.mockResolvedValue({
417
332
  operations: [createMockOperationView()],
418
333
  });
@@ -465,7 +380,7 @@ describe("makeGetAccountShape", () => {
465
380
  });
466
381
 
467
382
  it("should sync without device when account has both xpub and publicKey", async () => {
468
- mockedGetBalance.mockResolvedValue(createMockInstrumentBalances());
383
+ mockedGetBalance.mockResolvedValue([createMockNativeBalance("1000")]);
469
384
  mockedGetOperations.mockResolvedValue({
470
385
  operations: [createMockOperationView()],
471
386
  });
@@ -491,3 +406,134 @@ describe("makeGetAccountShape", () => {
491
406
  expect(mockedResolver).not.toHaveBeenCalled();
492
407
  });
493
408
  });
409
+
410
+ describe("filterDisabledTokenAccounts", () => {
411
+ const currency = createMockCantonCurrency();
412
+
413
+ const createMockTokenAccount = (contractAddress: string): TokenAccount => ({
414
+ type: "TokenAccount",
415
+ id: `token-account-${contractAddress}`,
416
+ parentId: "parent-account-id",
417
+ token: {
418
+ type: "TokenCurrency",
419
+ id: `token-id-${contractAddress}`,
420
+ contractAddress,
421
+ name: "Test Token",
422
+ ticker: "TEST",
423
+ decimals: 18,
424
+ parentCurrency: currency,
425
+ } as any,
426
+ balance: new BigNumber(100),
427
+ spendableBalance: new BigNumber(100),
428
+ operationsCount: 0,
429
+ operations: [],
430
+ pendingOperations: [],
431
+ balanceHistoryCache: {
432
+ HOUR: { latestDate: null, balances: [] },
433
+ DAY: { latestDate: null, balances: [] },
434
+ WEEK: { latestDate: null, balances: [] },
435
+ },
436
+ swapHistory: [],
437
+ creationDate: new Date(),
438
+ });
439
+
440
+ beforeEach(() => {
441
+ jest.clearAllMocks();
442
+ });
443
+
444
+ it("should return empty array when subAccounts is undefined", async () => {
445
+ const result = await filterDisabledTokenAccounts(currency, undefined);
446
+ expect(result).toEqual([]);
447
+ expect(mockedGetEnabledInstrumentsCached).not.toHaveBeenCalled();
448
+ });
449
+
450
+ it("should return empty array when subAccounts is empty", async () => {
451
+ const result = await filterDisabledTokenAccounts(currency, []);
452
+ expect(result).toEqual([]);
453
+ expect(mockedGetEnabledInstrumentsCached).not.toHaveBeenCalled();
454
+ });
455
+
456
+ it("should filter out disabled token accounts", async () => {
457
+ const enabledTokenId = "0xenabled";
458
+ const disabledTokenId = "0xdisabled";
459
+ const enabledTokenAccount = createMockTokenAccount(enabledTokenId);
460
+ const disabledTokenAccount = createMockTokenAccount(disabledTokenId);
461
+
462
+ mockedGetEnabledInstrumentsCached.mockResolvedValue([enabledTokenId]);
463
+
464
+ const result = await filterDisabledTokenAccounts(currency, [
465
+ enabledTokenAccount,
466
+ disabledTokenAccount,
467
+ ]);
468
+
469
+ expect(result).toHaveLength(1);
470
+ expect(result[0]).toBe(enabledTokenAccount);
471
+ expect(mockedGetEnabledInstrumentsCached).toHaveBeenCalledWith(currency);
472
+ });
473
+
474
+ it("should keep enabled token accounts", async () => {
475
+ const enabledTokenId1 = "0xenabled1";
476
+ const enabledTokenId2 = "0xenabled2";
477
+ const enabledTokenAccount1 = createMockTokenAccount(enabledTokenId1);
478
+ const enabledTokenAccount2 = createMockTokenAccount(enabledTokenId2);
479
+
480
+ mockedGetEnabledInstrumentsCached.mockResolvedValue([enabledTokenId1, enabledTokenId2]);
481
+
482
+ const result = await filterDisabledTokenAccounts(currency, [
483
+ enabledTokenAccount1,
484
+ enabledTokenAccount2,
485
+ ]);
486
+
487
+ expect(result).toHaveLength(2);
488
+ expect(result).toContain(enabledTokenAccount1);
489
+ expect(result).toContain(enabledTokenAccount2);
490
+ });
491
+
492
+ it("should not keep token accounts without contractAddress", async () => {
493
+ const tokenAccountWithoutAddress = {
494
+ ...createMockTokenAccount("0xtest"),
495
+ token: {
496
+ ...createMockTokenAccount("0xtest").token,
497
+ contractAddress: "",
498
+ },
499
+ };
500
+ const enabledTokenAccount = createMockTokenAccount("0xenabled");
501
+
502
+ mockedGetEnabledInstrumentsCached.mockResolvedValue(["0xenabled"]);
503
+
504
+ const result = await filterDisabledTokenAccounts(currency, [
505
+ tokenAccountWithoutAddress,
506
+ enabledTokenAccount,
507
+ ]);
508
+
509
+ expect(result).toHaveLength(1);
510
+ expect(result[0]).toBe(enabledTokenAccount);
511
+ expect(result).not.toContain(tokenAccountWithoutAddress);
512
+ });
513
+
514
+ it("should return empty array when API fails", async () => {
515
+ const enabledTokenAccount = createMockTokenAccount("0xenabled");
516
+ const disabledTokenAccount = createMockTokenAccount("0xdisabled");
517
+
518
+ mockedGetEnabledInstrumentsCached.mockRejectedValue(new Error("Network error"));
519
+
520
+ const result = await filterDisabledTokenAccounts(currency, [
521
+ enabledTokenAccount,
522
+ disabledTokenAccount,
523
+ ]);
524
+
525
+ expect(result).toEqual([]);
526
+ expect(mockedGetEnabledInstrumentsCached).toHaveBeenCalledWith(currency);
527
+ });
528
+
529
+ it("should handle empty enabled instruments list", async () => {
530
+ const tokenAccount1 = createMockTokenAccount("0xtoken1");
531
+ const tokenAccount2 = createMockTokenAccount("0xtoken2");
532
+
533
+ mockedGetEnabledInstrumentsCached.mockResolvedValue([]);
534
+
535
+ const result = await filterDisabledTokenAccounts(currency, [tokenAccount1, tokenAccount2]);
536
+
537
+ expect(result).toEqual([]);
538
+ });
539
+ });