@ledgerhq/cryptoassets 13.32.0-nightly.1 → 13.32.0-nightly.2

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 (75) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.unimportedrc.json +3 -6
  3. package/CHANGELOG.md +16 -0
  4. package/lib/abandonseed.d.ts.map +1 -1
  5. package/lib/abandonseed.js +1 -0
  6. package/lib/abandonseed.js.map +1 -1
  7. package/lib/cal-client/entities/index.d.ts +125 -0
  8. package/lib/cal-client/entities/index.d.ts.map +1 -0
  9. package/lib/cal-client/entities/index.js +57 -0
  10. package/lib/cal-client/entities/index.js.map +1 -0
  11. package/lib/cal-client/hooks/index.d.ts +2 -0
  12. package/lib/cal-client/hooks/index.d.ts.map +1 -0
  13. package/lib/cal-client/hooks/index.js +18 -0
  14. package/lib/cal-client/hooks/index.js.map +1 -0
  15. package/lib/cal-client/hooks/useTokensData.d.ts +11 -0
  16. package/lib/cal-client/hooks/useTokensData.d.ts.map +1 -0
  17. package/lib/cal-client/hooks/useTokensData.js +35 -0
  18. package/lib/cal-client/hooks/useTokensData.js.map +1 -0
  19. package/lib/cal-client/state-manager/api.d.ts +842 -0
  20. package/lib/cal-client/state-manager/api.d.ts.map +1 -0
  21. package/lib/cal-client/state-manager/api.js +87 -0
  22. package/lib/cal-client/state-manager/api.js.map +1 -0
  23. package/lib/cal-client/state-manager/fields.d.ts +2 -0
  24. package/lib/cal-client/state-manager/fields.d.ts.map +1 -0
  25. package/lib/cal-client/state-manager/fields.js +24 -0
  26. package/lib/cal-client/state-manager/fields.js.map +1 -0
  27. package/lib/cal-client/state-manager/types.d.ts +36 -0
  28. package/lib/cal-client/state-manager/types.d.ts.map +1 -0
  29. package/lib/cal-client/state-manager/types.js +8 -0
  30. package/lib/cal-client/state-manager/types.js.map +1 -0
  31. package/lib/currencies.d.ts.map +1 -1
  32. package/lib/currencies.js +52 -0
  33. package/lib/currencies.js.map +1 -1
  34. package/lib-es/abandonseed.d.ts.map +1 -1
  35. package/lib-es/abandonseed.js +1 -0
  36. package/lib-es/abandonseed.js.map +1 -1
  37. package/lib-es/cal-client/entities/index.d.ts +125 -0
  38. package/lib-es/cal-client/entities/index.d.ts.map +1 -0
  39. package/lib-es/cal-client/entities/index.js +54 -0
  40. package/lib-es/cal-client/entities/index.js.map +1 -0
  41. package/lib-es/cal-client/hooks/index.d.ts +2 -0
  42. package/lib-es/cal-client/hooks/index.d.ts.map +1 -0
  43. package/lib-es/cal-client/hooks/index.js +2 -0
  44. package/lib-es/cal-client/hooks/index.js.map +1 -0
  45. package/lib-es/cal-client/hooks/useTokensData.d.ts +11 -0
  46. package/lib-es/cal-client/hooks/useTokensData.d.ts.map +1 -0
  47. package/lib-es/cal-client/hooks/useTokensData.js +31 -0
  48. package/lib-es/cal-client/hooks/useTokensData.js.map +1 -0
  49. package/lib-es/cal-client/state-manager/api.d.ts +842 -0
  50. package/lib-es/cal-client/state-manager/api.d.ts.map +1 -0
  51. package/lib-es/cal-client/state-manager/api.js +84 -0
  52. package/lib-es/cal-client/state-manager/api.js.map +1 -0
  53. package/lib-es/cal-client/state-manager/fields.d.ts +2 -0
  54. package/lib-es/cal-client/state-manager/fields.d.ts.map +1 -0
  55. package/lib-es/cal-client/state-manager/fields.js +21 -0
  56. package/lib-es/cal-client/state-manager/fields.js.map +1 -0
  57. package/lib-es/cal-client/state-manager/types.d.ts +36 -0
  58. package/lib-es/cal-client/state-manager/types.d.ts.map +1 -0
  59. package/lib-es/cal-client/state-manager/types.js +5 -0
  60. package/lib-es/cal-client/state-manager/types.js.map +1 -0
  61. package/lib-es/currencies.d.ts.map +1 -1
  62. package/lib-es/currencies.js +52 -0
  63. package/lib-es/currencies.js.map +1 -1
  64. package/package.json +6 -3
  65. package/src/abandonseed.ts +1 -0
  66. package/src/cal-client/MIGRATION_GUIDE.md +276 -0
  67. package/src/cal-client/README.md +125 -0
  68. package/src/cal-client/entities/index.ts +59 -0
  69. package/src/cal-client/hooks/__tests__/useTokensData.test.ts +222 -0
  70. package/src/cal-client/hooks/index.ts +1 -0
  71. package/src/cal-client/hooks/useTokensData.ts +47 -0
  72. package/src/cal-client/state-manager/api.ts +97 -0
  73. package/src/cal-client/state-manager/fields.ts +20 -0
  74. package/src/cal-client/state-manager/types.ts +39 -0
  75. package/src/currencies.ts +52 -0
@@ -0,0 +1,276 @@
1
+ # CAL Client Migration Guide
2
+
3
+ ## Overview
4
+
5
+ Migrate from `listTokens()` and `listTokensForCryptoCurrency()` to the CAL Client API.
6
+
7
+ > **Remove CAL Initiative ([LIVE-21487](https://ledgerhq.atlassian.net/browse/LIVE-21487))**: Replace static token lists with dynamic, server-managed asset data. This guide is for the usecases where we still need to list tokens dynamically.
8
+
9
+ ### useTokensData
10
+
11
+ ```typescript
12
+ const { data, isLoading, isSuccess, isError, loadNext, error, refetch } = useTokensData({
13
+ networkFamily?: string[], // Filter by network families (e.g., ["ethereum", "polygon"])
14
+ output?: string[], // Specify output fields (e.g., ["id", "name", "ticker", "units", "delisted"])
15
+ limit?: number, // Maximum number of assets to return
16
+ pageSize?: number, // Number of items per page (default: 100, options: 10, 100, 1000)
17
+ ref?: string, // CAL reference (default: "branch:main", can be tag or commit)
18
+ isStaging?: boolean, // Use staging or production environment
19
+ });
20
+ ```
21
+
22
+ **Data Structure:**
23
+
24
+ ```typescript
25
+ data: {
26
+ tokens: TokenCurrency[], // Array of token currencies
27
+ pagination: {
28
+ nextCursor?: string, // Cursor for next page (if available)
29
+ }
30
+ }
31
+ ```
32
+
33
+ ## Integration on LLD
34
+
35
+ **Pagination:** Use `react-window`'s `InfiniteLoader` + `FixedSizeList` with `loadMoreItems` callback, or implement scroll detection to call `loadNext`
36
+
37
+ ## Integration on LLM
38
+
39
+ **Pagination:** Use `BottomSheetVirtualizedList` or `FlatList` with `onEndReached={loadNext}`
40
+
41
+ ## Migration Examples
42
+
43
+ ### 1. Basic Token Lists
44
+
45
+ **Before:**
46
+
47
+ ```typescript
48
+ import { listTokens } from "@ledgerhq/cryptoassets";
49
+
50
+ function TokenList() {
51
+ const tokens = listTokens();
52
+ return (
53
+ <div>
54
+ {tokens.map(token => <div key={token.id}>{token.name}</div>)}
55
+ </div>
56
+ );
57
+ }
58
+ ```
59
+
60
+ **After:**
61
+
62
+ ```typescript
63
+ import { useTokensData } from "@ledgerhq/cryptoassets/cal-client/hooks/useTokensData";
64
+
65
+ function TokenList() {
66
+ const { data, isLoading, loadNext } = useTokensData({
67
+ pageSize: 100, // Optional: default is 100
68
+ });
69
+
70
+ if (isLoading) return <div>Loading...</div>;
71
+
72
+ const tokens = data?.tokens || [];
73
+
74
+ return (
75
+ <SomethingThatManagesInfiniteScrollList
76
+ data={tokens}
77
+ renderItem={(token) => (
78
+ <div key={token.id}>{token.name}</div>
79
+ )}
80
+ onScrollEnd={loadNext}
81
+ />
82
+ );
83
+ }
84
+ ```
85
+
86
+ ### 2. Currency-Specific Tokens (by Network Family)
87
+
88
+ **Before:**
89
+
90
+ ```typescript
91
+ import { listTokensForCryptoCurrency } from "@ledgerhq/cryptoassets";
92
+
93
+ function EthereumTokens({ currency }) {
94
+ const tokens = listTokensForCryptoCurrency(currency);
95
+ return (
96
+ <select>
97
+ {tokens.map(token => (
98
+ <option key={token.id} value={token.id}>{token.name}</option>
99
+ ))}
100
+ </select>
101
+ );
102
+ }
103
+ ```
104
+
105
+ **After:**
106
+
107
+ ```typescript
108
+ import { useTokensData } from "@ledgerhq/cryptoassets/cal-client/hooks/useTokensData";
109
+
110
+ function EthereumTokens({ networkFamily }) {
111
+ const { data, isLoading, loadNext } = useTokensData({
112
+ networkFamily: [networkFamily], // e.g., ["ethereum"] or ["polygon"]
113
+ });
114
+
115
+ if (isLoading) return <div>Loading...</div>;
116
+
117
+ const tokens = data?.tokens || [];
118
+
119
+ return (
120
+ <select>
121
+ {tokens.map(token => (
122
+ <option key={token.id} value={token.id}>{token.name}</option>
123
+ ))}
124
+ </select>
125
+ );
126
+ }
127
+ ```
128
+
129
+ **Note:** If you need to filter by a specific parent currency within a network family, you'll need to do client-side filtering:
130
+
131
+ ```typescript
132
+ const tokens = (data?.tokens || []).filter(token => token.parentCurrency?.id === currency.id);
133
+ ```
134
+
135
+ ### 3. Search (Client-Side Filtering)
136
+
137
+ **Before:**
138
+
139
+ ```typescript
140
+ import { listTokens } from "@ledgerhq/cryptoassets";
141
+
142
+ function SearchableTokens({ searchQuery }) {
143
+ const allTokens = listTokens();
144
+ const filteredTokens = allTokens.filter(token =>
145
+ token.name.toLowerCase().includes(searchQuery.toLowerCase())
146
+ );
147
+ return (
148
+ <div>
149
+ {filteredTokens.map(token => <div key={token.id}>{token.name}</div>)}
150
+ </div>
151
+ );
152
+ }
153
+ ```
154
+
155
+ **After:**
156
+
157
+ ```typescript
158
+ import { useTokensData } from "@ledgerhq/cryptoassets/cal-client/hooks/useTokensData";
159
+ import { useMemo } from "react";
160
+
161
+ function SearchableTokens({ searchQuery }) {
162
+ const { data, isLoading, loadNext } = useTokensData({
163
+ pageSize: 100,
164
+ });
165
+
166
+ const filteredTokens = useMemo(() => {
167
+ if (!data?.tokens) return [];
168
+ if (!searchQuery) return data.tokens;
169
+
170
+ const query = searchQuery.toLowerCase();
171
+ return data.tokens.filter(token =>
172
+ token.name.toLowerCase().includes(query) ||
173
+ token.ticker.toLowerCase().includes(query)
174
+ );
175
+ }, [data?.tokens, searchQuery]);
176
+
177
+ if (isLoading) return <div>Loading...</div>;
178
+
179
+ return (
180
+ <SomethingThatManagesInfiniteScrollList
181
+ data={filteredTokens}
182
+ renderItem={(token) => <div key={token.id}>{token.name}</div>}
183
+ onScrollEnd={loadNext}
184
+ />
185
+ );
186
+ }
187
+ ```
188
+
189
+ ## Advanced Usage
190
+
191
+ ### 4. Optimizing Performance with Specific Output Fields
192
+
193
+ If you only need certain fields from the token data, you can specify them to reduce payload size:
194
+
195
+ ```typescript
196
+ import { useTokensData } from "@ledgerhq/cryptoassets/cal-client/hooks/useTokensData";
197
+
198
+ function TokenDropdown() {
199
+ const { data, isLoading } = useTokensData({
200
+ output: ["id", "name", "ticker"], // Only fetch what you need
201
+ networkFamily: ["ethereum"],
202
+ pageSize: 100,
203
+ });
204
+
205
+ if (isLoading) return <select disabled>Loading...</select>;
206
+
207
+ return (
208
+ <select>
209
+ {data?.tokens.map(token => (
210
+ <option key={token.id} value={token.id}>
211
+ {token.name} ({token.ticker})
212
+ </option>
213
+ ))}
214
+ </select>
215
+ );
216
+ }
217
+ ```
218
+
219
+ ### 5. Using a Specific CAL Version
220
+
221
+ To ensure consistent data across deployments, you can specify a particular CAL version:
222
+
223
+ ```typescript
224
+ const { data, isLoading } = useTokensData({
225
+ ref: "tag:tokens-1.11.12", // Use a specific tag
226
+ // ref: "branch:main", // Default: latest main branch
227
+ // ref: "commit:48188c6ca2888a9c50a0466d2442ec2f24cc2852", // Specific commit
228
+ });
229
+ ```
230
+
231
+ ### 6. Limiting Total Results
232
+
233
+ For use cases where you only need a subset of tokens (e.g., displaying top tokens):
234
+
235
+ ```typescript
236
+ const { data, isLoading } = useTokensData({
237
+ limit: 50, // Only fetch first 50 tokens
238
+ pageSize: 50, // Match page size to limit for single request
239
+ networkFamily: ["ethereum"],
240
+ });
241
+ ```
242
+
243
+ ### 7. Using Staging Environment
244
+
245
+ For testing with staging data:
246
+
247
+ ```typescript
248
+ const { data, isLoading, isError } = useTokensData({
249
+ isStaging: true, // Use staging API
250
+ networkFamily: ["ethereum"],
251
+ });
252
+ ```
253
+
254
+ ## Future Evolution
255
+
256
+ The CAL Client is actively evolving and will be enhanced based on real-world usage and requirements. As we gather feedback from implementations across LLD and LLM, we plan to add:
257
+
258
+ - **Server-side search capabilities** - To optimize search performance for large datasets
259
+ - **Additional filtering options** - More granular filtering based on specific use cases
260
+ - **Caching strategies** - Improved caching mechanisms for better performance
261
+ - **Additional hooks** - Specialized hooks for common patterns (e.g., `useTokensByNetwork`, `useTokenSearch`)
262
+ - **Enhanced error handling** - More detailed error types and recovery strategies
263
+
264
+ **Have a specific use case or need?** Feel free to reach out to the team or create an issue. We're committed to evolving the API based on your needs while maintaining backward compatibility where possible.
265
+
266
+ ## Migration Checklist
267
+
268
+ - [ ] Replace `listTokens()` calls with `useTokensData({})`
269
+ - [ ] Replace `listTokensForCryptoCurrency()` with `useTokensData({ networkFamily: [...] })`
270
+ - [ ] Update component to handle async loading state
271
+ - [ ] Implement pagination if displaying large lists
272
+ - [ ] Update data access from flat array to `data?.tokens`
273
+ - [ ] Add client-side filtering if search functionality is needed
274
+ - [ ] Consider using `output` parameter to optimize payload size
275
+ - [ ] Test with different network families
276
+ - [ ] Verify error handling is in place
@@ -0,0 +1,125 @@
1
+ # CAL Client
2
+
3
+ **CAL** stands for **Crypto Assets List** - a data management system for fetching and managing token data across different blockchain networks.
4
+
5
+ ## Installation
6
+
7
+ The CAL client is included in `@ledgerhq/cryptoassets`
8
+
9
+ ## Usage Examples
10
+
11
+ ### Basic Usage with React
12
+
13
+ ```typescript
14
+ import { useTokensData } from "@ledgerhq/cryptoassets/cal-client/hooks/useTokensData";
15
+
16
+ // Fetch tokens data
17
+ const TokensList = () => {
18
+ const { data, isLoading, error, loadNext, isSuccess } = useTokensData({
19
+ networkFamily: ["ethereum", "polygon"],
20
+ pageSize: 100,
21
+ isStaging: false,
22
+ });
23
+
24
+ if (isLoading) return <div>Loading tokens...</div>;
25
+ if (error) return <div>Error loading tokens</div>;
26
+
27
+ return (
28
+ <div>
29
+ {data?.tokens.map(token => (
30
+ <div key={token.id}>
31
+ <span>{token.name} ({token.ticker})</span>
32
+ <span>Contract: {token.contractAddress}</span>
33
+ <span>Network: {token.parentCurrency.name}</span>
34
+ </div>
35
+ ))}
36
+ {loadNext && <button onClick={() => loadNext()}>Load More</button>}
37
+ </div>
38
+ );
39
+ };
40
+ ```
41
+
42
+ ### Desktop Integration (Redux)
43
+
44
+ ```typescript
45
+ import { cryptoAssetsApi } from "@ledgerhq/cryptoassets/cal-client/state-manager/api";
46
+
47
+ // Configure Redux store
48
+ const store = configureStore({
49
+ reducer: {
50
+ // ... other reducers
51
+ [cryptoAssetsApi.reducerPath]: cryptoAssetsApi.reducer,
52
+ },
53
+ middleware: getDefaultMiddleware => getDefaultMiddleware().concat(cryptoAssetsApi.middleware),
54
+ });
55
+ ```
56
+
57
+ ### Advanced Usage with Pagination
58
+
59
+ ```typescript
60
+ import { useTokensData } from "@ledgerhq/cryptoassets/cal-client/hooks/useTokensData";
61
+
62
+ const InfiniteTokensList = () => {
63
+ const {
64
+ data,
65
+ isLoading,
66
+ loadNext,
67
+ refetch
68
+ } = useTokensData({
69
+ networkFamily: ["ethereum"],
70
+ pageSize: 100,
71
+ output: ["id", "name", "ticker", "contract_address"],
72
+ });
73
+
74
+ return (
75
+ <div>
76
+ <button onClick={() => refetch()}>Refresh</button>
77
+ {data?.tokens.map(token => (
78
+ <TokenCard key={token.id} token={token} />
79
+ ))}
80
+ {loadNext && <button onClick={() => loadNext()}>Load More</button>}
81
+ </div>
82
+ );
83
+ };
84
+ ```
85
+
86
+ ## API Reference
87
+
88
+ ### Hooks
89
+
90
+ - `useTokensData(params)` - Fetch paginated tokens data with infinite scroll support
91
+ - **Parameters:**
92
+ - `networkFamily?: string[]` - Filter by network families (e.g., ["ethereum", "polygon"])
93
+ - `pageSize?: number` - Number of items per page (default: 100, options: 10, 100, 1000)
94
+ - `isStaging?: boolean` - Use staging environment
95
+ - `output?: string[]` - Specify output fields (default: all fields)
96
+ - **Returns:**
97
+ - `data: TokensDataWithPagination` - Token data with pagination info
98
+ - `isLoading: boolean` - Initial loading state
99
+ - `error: any` - Error object if request failed
100
+ - `loadNext?: () => void` - Function to load next page (undefined if no more pages)
101
+ - `isSuccess: boolean` - Whether the request succeeded
102
+ - `isError: boolean` - Whether the request failed
103
+ - `refetch: () => void` - Function to refetch data
104
+
105
+ ### Types
106
+
107
+ ```typescript
108
+ interface TokensDataWithPagination {
109
+ tokens: TokenCurrency[];
110
+ pagination: {
111
+ nextCursor?: string;
112
+ };
113
+ }
114
+
115
+ interface GetTokensDataParams {
116
+ networkFamily?: string[];
117
+ isStaging?: boolean;
118
+ pageSize?: number;
119
+ output?: string[];
120
+ }
121
+ ```
122
+
123
+ ## Integration
124
+
125
+ For custom implementations, ensure you include the `cryptoAssetsApi` reducer and middleware in your Redux store configuration. The CAL client uses RTK Query for efficient data fetching and caching.
@@ -0,0 +1,59 @@
1
+ import { z } from "zod";
2
+
3
+ /**
4
+ * Zod schema for token unit
5
+ */
6
+ export const TokenUnitSchema = z.object({
7
+ /** Code identifier for the unit */
8
+ code: z.string(),
9
+ /** Display name of the unit */
10
+ name: z.string(),
11
+ /** Magnitude (number of decimals) */
12
+ magnitude: z.number(),
13
+ });
14
+
15
+ export type TokenUnit = z.infer<typeof TokenUnitSchema>;
16
+
17
+ /**
18
+ * Zod schema for API token response
19
+ */
20
+ export const ApiTokenResponseSchema = z.object({
21
+ /** Type of asset (token or currency) */
22
+ type: z.string(),
23
+ /** Unique identifier for the token */
24
+ id: z.string(),
25
+ /** Smart contract address */
26
+ contract_address: z.string(),
27
+ /** Token standard (e.g., erc20, bep20, spl) */
28
+ standard: z.string(),
29
+ /** Number of decimals */
30
+ decimals: z.number(),
31
+ /** Network identifier (e.g., arbitrum, ethereum) */
32
+ network: z.string(),
33
+ /** Network family (e.g., ethereum, solana) */
34
+ network_family: z.string(),
35
+ /** Whether the token is delisted */
36
+ delisted: z.boolean(),
37
+ /** Full name of the token */
38
+ name: z.string(),
39
+ /** Ticker symbol */
40
+ ticker: z.string(),
41
+ /** Array of unit representations */
42
+ units: z.array(TokenUnitSchema).min(1),
43
+ /** Symbol */
44
+ symbol: z.string(),
45
+ /** Chain ID */
46
+ chain_id: z.string(),
47
+ /** Token identifier */
48
+ token_identifier: z.string(),
49
+ /** Network type */
50
+ network_type: z.string(),
51
+ /** Meta currency ID */
52
+ meta_currency_id: z.string(),
53
+ /** Blockchain name */
54
+ blockchain_name: z.string(),
55
+ /** Live signature */
56
+ live_signature: z.string(),
57
+ });
58
+
59
+ export type ApiTokenResponse = z.infer<typeof ApiTokenResponseSchema>;
@@ -0,0 +1,222 @@
1
+ /**
2
+ * @jest-environment jsdom
3
+ */
4
+
5
+ import { renderHook } from "@testing-library/react";
6
+ import { useTokensData } from "../useTokensData";
7
+ import { useGetTokensDataInfiniteQuery } from "../../state-manager/api";
8
+
9
+ jest.mock("../../state-manager/api", () => ({
10
+ useGetTokensDataInfiniteQuery: jest.fn(),
11
+ }));
12
+
13
+ const mockUseGetTokensDataInfiniteQuery = jest.mocked(useGetTokensDataInfiniteQuery);
14
+
15
+ const defaultMockValues = {
16
+ data: undefined,
17
+ isLoading: false,
18
+ error: undefined,
19
+ fetchNextPage: jest.fn(),
20
+ isSuccess: true,
21
+ isFetching: false,
22
+ isError: false,
23
+ fetchPreviousPage: jest.fn(),
24
+ isFetchingPreviousPage: false,
25
+ refetch: jest.fn(),
26
+ status: "success",
27
+ };
28
+
29
+ describe("useAssetsData", () => {
30
+ beforeEach(() => {
31
+ jest.clearAllMocks();
32
+ });
33
+
34
+ it("should return loading state when API is loading", () => {
35
+ mockUseGetTokensDataInfiniteQuery.mockReturnValue({
36
+ ...defaultMockValues,
37
+ isLoading: true,
38
+ status: "pending",
39
+ });
40
+
41
+ const { result } = renderHook(() =>
42
+ useTokensData({
43
+ networkFamily: ["ethereum"],
44
+ pageSize: 100,
45
+ output: ["id", "name", "ticker", "contract_address"],
46
+ }),
47
+ );
48
+
49
+ expect(result.current.isLoading).toBe(true);
50
+ expect(result.current.data).toBe(undefined);
51
+ });
52
+
53
+ it("should return combined data from multiple pages", () => {
54
+ const mockPages = [
55
+ {
56
+ tokens: [
57
+ {
58
+ id: "token-1",
59
+ name: "Token 1",
60
+ ticker: "T1",
61
+ contractAddress: "0x123",
62
+ parentCurrency: { id: "currency-1", name: "Currency 1" },
63
+ },
64
+ {
65
+ id: "token-2",
66
+ name: "Token 2",
67
+ ticker: "T2",
68
+ contractAddress: "0x456",
69
+ parentCurrency: { id: "currency-2", name: "Currency 2" },
70
+ },
71
+ ],
72
+ pagination: { nextCursor: "cursor-2" },
73
+ },
74
+ {
75
+ tokens: [
76
+ {
77
+ id: "token-3",
78
+ name: "Token 3",
79
+ ticker: "T3",
80
+ contractAddress: "0x789",
81
+ parentCurrency: { id: "currency-3", name: "Currency 3" },
82
+ },
83
+ ],
84
+ pagination: { nextCursor: undefined },
85
+ },
86
+ ];
87
+
88
+ mockUseGetTokensDataInfiniteQuery.mockReturnValue({
89
+ ...defaultMockValues,
90
+ data: { pages: mockPages, pageParams: [{ cursor: "" }, { cursor: "cursor-2" }] },
91
+ });
92
+
93
+ const { result } = renderHook(() =>
94
+ useTokensData({
95
+ networkFamily: ["ethereum"],
96
+ pageSize: 100,
97
+ output: ["id", "name", "ticker", "contract_address"],
98
+ }),
99
+ );
100
+
101
+ expect(result.current.data).toEqual({
102
+ tokens: [
103
+ {
104
+ id: "token-1",
105
+ name: "Token 1",
106
+ ticker: "T1",
107
+ contractAddress: "0x123",
108
+ parentCurrency: { id: "currency-1", name: "Currency 1" },
109
+ },
110
+ {
111
+ id: "token-2",
112
+ name: "Token 2",
113
+ ticker: "T2",
114
+ contractAddress: "0x456",
115
+ parentCurrency: { id: "currency-2", name: "Currency 2" },
116
+ },
117
+ {
118
+ id: "token-3",
119
+ name: "Token 3",
120
+ ticker: "T3",
121
+ contractAddress: "0x789",
122
+ parentCurrency: { id: "currency-3", name: "Currency 3" },
123
+ },
124
+ ],
125
+ pagination: { nextCursor: undefined },
126
+ });
127
+ expect(result.current.isLoading).toBe(false);
128
+ });
129
+
130
+ it("should return error when API has error", () => {
131
+ const mockError = new Error("API Error");
132
+ mockUseGetTokensDataInfiniteQuery.mockReturnValue({
133
+ ...defaultMockValues,
134
+ data: undefined,
135
+ error: mockError,
136
+ isSuccess: false,
137
+ isError: true,
138
+ status: "error",
139
+ });
140
+
141
+ const { result } = renderHook(() =>
142
+ useTokensData({
143
+ networkFamily: ["ethereum"],
144
+ pageSize: 100,
145
+ output: ["id", "name", "ticker", "contract_address"],
146
+ }),
147
+ );
148
+
149
+ expect(result.current.error).toBe(mockError);
150
+ expect(result.current.isLoading).toBe(false);
151
+ });
152
+
153
+ it("should provide loadNext function when there's a nextCursor", () => {
154
+ const mockFetchNextPage = jest.fn();
155
+ const mockPages = [
156
+ {
157
+ tokens: [],
158
+ pagination: { nextCursor: "next-cursor-456" },
159
+ },
160
+ ];
161
+
162
+ mockUseGetTokensDataInfiniteQuery.mockReturnValue({
163
+ ...defaultMockValues,
164
+ data: { pages: mockPages, pageParams: [{ cursor: "" }] },
165
+ fetchNextPage: mockFetchNextPage,
166
+ });
167
+
168
+ const { result } = renderHook(() =>
169
+ useTokensData({
170
+ networkFamily: ["ethereum"],
171
+ pageSize: 100,
172
+ output: ["id", "name", "ticker", "contract_address"],
173
+ }),
174
+ );
175
+
176
+ expect(result.current.loadNext).toBeDefined();
177
+ result.current.loadNext?.();
178
+
179
+ expect(mockFetchNextPage).toHaveBeenCalled();
180
+ });
181
+
182
+ it("should not provide loadNext function when there's no nextCursor", () => {
183
+ const mockPages = [
184
+ {
185
+ tokens: [],
186
+ pagination: { nextCursor: undefined },
187
+ },
188
+ ];
189
+
190
+ mockUseGetTokensDataInfiniteQuery.mockReturnValue({
191
+ ...defaultMockValues,
192
+ data: { pages: mockPages, pageParams: [{ cursor: "" }] },
193
+ });
194
+
195
+ const { result } = renderHook(() =>
196
+ useTokensData({
197
+ networkFamily: ["ethereum"],
198
+ pageSize: 100,
199
+ output: ["id", "name", "ticker", "contract_address"],
200
+ }),
201
+ );
202
+
203
+ expect(result.current.loadNext).toBeUndefined();
204
+ });
205
+
206
+ it("should return undefined data when no pages exist", () => {
207
+ mockUseGetTokensDataInfiniteQuery.mockReturnValue({
208
+ ...defaultMockValues,
209
+ });
210
+
211
+ const { result } = renderHook(() =>
212
+ useTokensData({
213
+ networkFamily: ["ethereum"],
214
+ pageSize: 100,
215
+ output: ["id", "name", "ticker", "contract_address"],
216
+ }),
217
+ );
218
+
219
+ expect(result.current.data).toBeUndefined();
220
+ expect(result.current.loadNext).toBeUndefined();
221
+ });
222
+ });
@@ -0,0 +1 @@
1
+ export * from "./useTokensData";