@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.
- package/.turbo/turbo-build.log +1 -1
- package/.unimportedrc.json +3 -6
- package/CHANGELOG.md +16 -0
- package/lib/abandonseed.d.ts.map +1 -1
- package/lib/abandonseed.js +1 -0
- package/lib/abandonseed.js.map +1 -1
- package/lib/cal-client/entities/index.d.ts +125 -0
- package/lib/cal-client/entities/index.d.ts.map +1 -0
- package/lib/cal-client/entities/index.js +57 -0
- package/lib/cal-client/entities/index.js.map +1 -0
- package/lib/cal-client/hooks/index.d.ts +2 -0
- package/lib/cal-client/hooks/index.d.ts.map +1 -0
- package/lib/cal-client/hooks/index.js +18 -0
- package/lib/cal-client/hooks/index.js.map +1 -0
- package/lib/cal-client/hooks/useTokensData.d.ts +11 -0
- package/lib/cal-client/hooks/useTokensData.d.ts.map +1 -0
- package/lib/cal-client/hooks/useTokensData.js +35 -0
- package/lib/cal-client/hooks/useTokensData.js.map +1 -0
- package/lib/cal-client/state-manager/api.d.ts +842 -0
- package/lib/cal-client/state-manager/api.d.ts.map +1 -0
- package/lib/cal-client/state-manager/api.js +87 -0
- package/lib/cal-client/state-manager/api.js.map +1 -0
- package/lib/cal-client/state-manager/fields.d.ts +2 -0
- package/lib/cal-client/state-manager/fields.d.ts.map +1 -0
- package/lib/cal-client/state-manager/fields.js +24 -0
- package/lib/cal-client/state-manager/fields.js.map +1 -0
- package/lib/cal-client/state-manager/types.d.ts +36 -0
- package/lib/cal-client/state-manager/types.d.ts.map +1 -0
- package/lib/cal-client/state-manager/types.js +8 -0
- package/lib/cal-client/state-manager/types.js.map +1 -0
- package/lib/currencies.d.ts.map +1 -1
- package/lib/currencies.js +52 -0
- package/lib/currencies.js.map +1 -1
- package/lib-es/abandonseed.d.ts.map +1 -1
- package/lib-es/abandonseed.js +1 -0
- package/lib-es/abandonseed.js.map +1 -1
- package/lib-es/cal-client/entities/index.d.ts +125 -0
- package/lib-es/cal-client/entities/index.d.ts.map +1 -0
- package/lib-es/cal-client/entities/index.js +54 -0
- package/lib-es/cal-client/entities/index.js.map +1 -0
- package/lib-es/cal-client/hooks/index.d.ts +2 -0
- package/lib-es/cal-client/hooks/index.d.ts.map +1 -0
- package/lib-es/cal-client/hooks/index.js +2 -0
- package/lib-es/cal-client/hooks/index.js.map +1 -0
- package/lib-es/cal-client/hooks/useTokensData.d.ts +11 -0
- package/lib-es/cal-client/hooks/useTokensData.d.ts.map +1 -0
- package/lib-es/cal-client/hooks/useTokensData.js +31 -0
- package/lib-es/cal-client/hooks/useTokensData.js.map +1 -0
- package/lib-es/cal-client/state-manager/api.d.ts +842 -0
- package/lib-es/cal-client/state-manager/api.d.ts.map +1 -0
- package/lib-es/cal-client/state-manager/api.js +84 -0
- package/lib-es/cal-client/state-manager/api.js.map +1 -0
- package/lib-es/cal-client/state-manager/fields.d.ts +2 -0
- package/lib-es/cal-client/state-manager/fields.d.ts.map +1 -0
- package/lib-es/cal-client/state-manager/fields.js +21 -0
- package/lib-es/cal-client/state-manager/fields.js.map +1 -0
- package/lib-es/cal-client/state-manager/types.d.ts +36 -0
- package/lib-es/cal-client/state-manager/types.d.ts.map +1 -0
- package/lib-es/cal-client/state-manager/types.js +5 -0
- package/lib-es/cal-client/state-manager/types.js.map +1 -0
- package/lib-es/currencies.d.ts.map +1 -1
- package/lib-es/currencies.js +52 -0
- package/lib-es/currencies.js.map +1 -1
- package/package.json +6 -3
- package/src/abandonseed.ts +1 -0
- package/src/cal-client/MIGRATION_GUIDE.md +276 -0
- package/src/cal-client/README.md +125 -0
- package/src/cal-client/entities/index.ts +59 -0
- package/src/cal-client/hooks/__tests__/useTokensData.test.ts +222 -0
- package/src/cal-client/hooks/index.ts +1 -0
- package/src/cal-client/hooks/useTokensData.ts +47 -0
- package/src/cal-client/state-manager/api.ts +97 -0
- package/src/cal-client/state-manager/fields.ts +20 -0
- package/src/cal-client/state-manager/types.ts +39 -0
- 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";
|