@0xsquid/react-hooks 1.0.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 (108) hide show
  1. package/.babelrc +7 -0
  2. package/.eslintignore +1 -0
  3. package/.eslintrc.json +44 -0
  4. package/dist/index.js +6 -0
  5. package/jest.config.ts +28 -0
  6. package/package.json +124 -0
  7. package/scripts/copyAssets.js +27 -0
  8. package/scripts/removeBuildTestFolder.js +28 -0
  9. package/scripts/replacePaths.js +37 -0
  10. package/src/assets/images/icons/wallets/binance.svg +4 -0
  11. package/src/assets/images/icons/wallets/bitget.svg +43 -0
  12. package/src/assets/images/icons/wallets/bitkeep.svg +9 -0
  13. package/src/assets/images/icons/wallets/blockchaindotcom.svg +11 -0
  14. package/src/assets/images/icons/wallets/brave.svg +18 -0
  15. package/src/assets/images/icons/wallets/c98.svg +3 -0
  16. package/src/assets/images/icons/wallets/coinbase.svg +5 -0
  17. package/src/assets/images/icons/wallets/cosmostation.svg +9 -0
  18. package/src/assets/images/icons/wallets/defiConnect.svg +25 -0
  19. package/src/assets/images/icons/wallets/exodus.svg +33 -0
  20. package/src/assets/images/icons/wallets/fetchai.svg +1 -0
  21. package/src/assets/images/icons/wallets/index.ts +43 -0
  22. package/src/assets/images/icons/wallets/keplr.svg +9 -0
  23. package/src/assets/images/icons/wallets/leap.svg +35 -0
  24. package/src/assets/images/icons/wallets/metamask.svg +48 -0
  25. package/src/assets/images/icons/wallets/okx.svg +1 -0
  26. package/src/assets/images/icons/wallets/rabby.svg +1 -0
  27. package/src/assets/images/icons/wallets/trustwallet.svg +11 -0
  28. package/src/assets/images/icons/wallets/walletConnect.svg +11 -0
  29. package/src/assets/images/icons/wallets/wallet_icon.svg +1 -0
  30. package/src/assets/images/icons/wallets/xdefi.svg +9 -0
  31. package/src/assets/images/icons/wallets/zerion.svg +11 -0
  32. package/src/connectors/ExodusConnector.ts +6 -0
  33. package/src/connectors/LedgerLiveConnector.ts +226 -0
  34. package/src/contracts/typechain/ERC20.d.ts +441 -0
  35. package/src/contracts/typechain/factories/ERC20__factory.d.ts +56 -0
  36. package/src/contracts/typechain/factories/ERC20__factory.ts +340 -0
  37. package/src/core/constants.ts +549 -0
  38. package/src/core/externalLinks.ts +26 -0
  39. package/src/core/index.ts +1 -0
  40. package/src/core/multicall3.ts +22 -0
  41. package/src/core/numbers.ts +44 -0
  42. package/src/core/providers/CosmosProvider.tsx +55 -0
  43. package/src/core/queries/queries-keys.ts +156 -0
  44. package/src/core/queries/react-query-config.ts +13 -0
  45. package/src/core/types/components.ts +16 -0
  46. package/src/core/types/config.ts +52 -0
  47. package/src/core/types/dex.ts +31 -0
  48. package/src/core/types/error.ts +52 -0
  49. package/src/core/types/event.ts +16 -0
  50. package/src/core/types/route.ts +21 -0
  51. package/src/core/types/swap.ts +8 -0
  52. package/src/core/types/tokens.ts +30 -0
  53. package/src/core/types/transaction.ts +84 -0
  54. package/src/core/types/types.ts +37 -0
  55. package/src/core/types/wallet.ts +83 -0
  56. package/src/hooks/chains/useSquidChains.ts +66 -0
  57. package/src/hooks/cosmos/useCosmos.ts +279 -0
  58. package/src/hooks/cosmos/useCosmosForChain.ts +33 -0
  59. package/src/hooks/index.ts +32 -0
  60. package/src/hooks/navigation/useKeyboardNavigation.ts +88 -0
  61. package/src/hooks/store/useSquidStore.ts +80 -0
  62. package/src/hooks/swap/useSwap.ts +359 -0
  63. package/src/hooks/tokens/useAllTokensWithBalance.ts +157 -0
  64. package/src/hooks/tokens/useBalance.ts +180 -0
  65. package/src/hooks/tokens/useMultiChainBalance.ts +59 -0
  66. package/src/hooks/tokens/usePrices.ts +52 -0
  67. package/src/hooks/tokens/useSingleTokenPrice.ts +45 -0
  68. package/src/hooks/tokens/useSquidTokens.ts +48 -0
  69. package/src/hooks/tokens/useTokensWithBalance.ts +169 -0
  70. package/src/hooks/transaction/useEstimate.ts +397 -0
  71. package/src/hooks/transaction/useEstimatePriceImpact.ts +39 -0
  72. package/src/hooks/transaction/useExecuteTransaction.ts +440 -0
  73. package/src/hooks/transaction/useExpress.ts +46 -0
  74. package/src/hooks/transaction/useGetRoute.ts +145 -0
  75. package/src/hooks/transaction/useSingleTransaction.ts +155 -0
  76. package/src/hooks/transaction/useTransaction.ts +734 -0
  77. package/src/hooks/user/useUserParams.ts +63 -0
  78. package/src/hooks/wallet/useAddToken.ts +53 -0
  79. package/src/hooks/wallet/useAutoConnect.ts +56 -0
  80. package/src/hooks/wallet/useGnosisContext.ts +84 -0
  81. package/src/hooks/wallet/useIntegratorContext.ts +44 -0
  82. package/src/hooks/wallet/useMultiChainWallet.ts +146 -0
  83. package/src/hooks/wallet/useWallet.ts +86 -0
  84. package/src/hooks/webapp-router/useSquidRouter.ts +42 -0
  85. package/src/index.ts +5 -0
  86. package/src/provider/index.tsx +105 -0
  87. package/src/react-app-env.d.ts +2 -0
  88. package/src/services/external/analyticsService.ts +46 -0
  89. package/src/services/external/coingeckoService.ts +35 -0
  90. package/src/services/external/rpcService.ts +196 -0
  91. package/src/services/external/secretService.ts +178 -0
  92. package/src/services/index.ts +1 -0
  93. package/src/services/internal/apiService.ts +6 -0
  94. package/src/services/internal/assetsService.ts +84 -0
  95. package/src/services/internal/configService.ts +261 -0
  96. package/src/services/internal/errorService.ts +124 -0
  97. package/src/services/internal/eventService.ts +93 -0
  98. package/src/services/internal/priceService.ts +10 -0
  99. package/src/services/internal/transactionService.ts +255 -0
  100. package/src/services/internal/transactionStatusService.ts +293 -0
  101. package/src/services/internal/walletService.ts +158 -0
  102. package/src/tests/assetsService.test.ts +176 -0
  103. package/src/tests/configService.test.ts +486 -0
  104. package/src/tests/fetchSquidData.ts +43 -0
  105. package/src/tests/jest-svg-transform.ts +19 -0
  106. package/src/tests/sample.json +0 -0
  107. package/src/tests/walletService.test.ts +178 -0
  108. package/tsconfig.json +30 -0
@@ -0,0 +1,176 @@
1
+ import {
2
+ shareSubgraphId,
3
+ sortTokensBySharedSubgraphIds,
4
+ filterChains,
5
+ } from "@/services/internal/assetsService";
6
+ import { OnlineSquidData, fetchSquidData } from "./fetchSquidData";
7
+
8
+ let squid: OnlineSquidData | undefined = undefined;
9
+ let CHAINS: OnlineSquidData["chains"] = {};
10
+ let TOKENS: OnlineSquidData["tokens"] = {};
11
+
12
+ describe("assetsService", () => {
13
+ beforeAll(async () => {
14
+ squid = await fetchSquidData();
15
+ CHAINS = squid?.chains;
16
+ TOKENS = squid?.tokens;
17
+ });
18
+
19
+ describe("shareSubgraphId", () => {
20
+ test("should return false when no token has subgraph ids", () => {
21
+ expect(shareSubgraphId(TOKENS.BASE.USDBC, TOKENS.AVALANCHE.MAI)).toBe(
22
+ false
23
+ );
24
+ });
25
+
26
+ test("should return true when tokens share subgraph ids", () => {
27
+ expect(shareSubgraphId(TOKENS.AVALANCHE.USDC, TOKENS.ETHEREUM.USDC)).toBe(
28
+ true
29
+ );
30
+ });
31
+
32
+ test("should return false when tokens do not share subgraph ids", () => {
33
+ expect(shareSubgraphId(TOKENS.AVALANCHE.AVAX, TOKENS.ETHEREUM.ETH)).toBe(
34
+ false
35
+ );
36
+ });
37
+ });
38
+
39
+ describe("sortTokensBySharedSubgraphIds", () => {
40
+ test("should sort 'a' token first when it shares subgraph ids with the token to check", () => {
41
+ expect(
42
+ sortTokensBySharedSubgraphIds({
43
+ a: TOKENS.AVALANCHE.USDC,
44
+ b: TOKENS.ETHEREUM.WBTC,
45
+ tokenToCheck: TOKENS.ETHEREUM.USDC,
46
+ })
47
+ ).toBe(-1);
48
+ });
49
+
50
+ test("should sort 'b' token first when it shares subgraph ids with the token to check", () => {
51
+ expect(
52
+ sortTokensBySharedSubgraphIds({
53
+ a: TOKENS.AVALANCHE.USDC,
54
+ b: TOKENS.ETHEREUM.ETH,
55
+ tokenToCheck: TOKENS.ETHEREUM.WETH,
56
+ })
57
+ ).toBe(1);
58
+ });
59
+
60
+ test("should not change order when tokens do not share subgraph ids with the token to check", () => {
61
+ expect(
62
+ sortTokensBySharedSubgraphIds({
63
+ a: TOKENS.AVALANCHE.AVAX,
64
+ b: TOKENS.ETHEREUM.ETH,
65
+ tokenToCheck: TOKENS.ETHEREUM.USDC,
66
+ })
67
+ ).toBe(0);
68
+ });
69
+
70
+ test("should not change the order when both tokens share subgraph ids with the token to check", () => {
71
+ expect(
72
+ sortTokensBySharedSubgraphIds({
73
+ a: TOKENS.ETHEREUM.USDC,
74
+ b: TOKENS.AVALANCHE.axlUSDC,
75
+ tokenToCheck: TOKENS.AVALANCHE.USDC,
76
+ })
77
+ ).toBe(0);
78
+ });
79
+ });
80
+
81
+ describe("filterChains", () => {
82
+ test("return chains without changes when no chain is disabled or coming soon", () => {
83
+ const chains = [CHAINS.AVALANCHE, CHAINS.ETHEREUM, CHAINS.NOBLE];
84
+
85
+ expect(
86
+ filterChains({
87
+ chains,
88
+ config: {
89
+ comingSoonChainIds: undefined,
90
+ disabledChains: undefined,
91
+ },
92
+ direction: "from",
93
+ })
94
+ ).toEqual(chains);
95
+ });
96
+
97
+ test("sorts disabled chains to the end", () => {
98
+ expect(
99
+ filterChains({
100
+ chains: [CHAINS.MOONBEAM, CHAINS.POLYGON, CHAINS.BINANCE],
101
+ config: {
102
+ comingSoonChainIds: undefined,
103
+ disabledChains: { source: [CHAINS.POLYGON.chainId] },
104
+ },
105
+ direction: "from",
106
+ })
107
+ ).toEqual([CHAINS.MOONBEAM, CHAINS.BINANCE, CHAINS.POLYGON]);
108
+
109
+ expect(
110
+ filterChains({
111
+ chains: [CHAINS.POLYGON, CHAINS.CELO, CHAINS.BINANCE],
112
+ config: {
113
+ comingSoonChainIds: undefined,
114
+ disabledChains: { destination: [CHAINS.CELO.chainId] },
115
+ },
116
+ direction: "to",
117
+ })
118
+ ).toEqual([CHAINS.POLYGON, CHAINS.BINANCE, CHAINS.CELO]);
119
+ });
120
+
121
+ test("sorts coming soon chains to the end", () => {
122
+ expect(
123
+ filterChains({
124
+ chains: [CHAINS.LINEA, CHAINS.FANTOM, CHAINS.CELO],
125
+ config: {
126
+ disabledChains: undefined,
127
+ comingSoonChainIds: [CHAINS.LINEA.chainId],
128
+ },
129
+ direction: "from",
130
+ })
131
+ ).toEqual([CHAINS.FANTOM, CHAINS.CELO, CHAINS.LINEA]);
132
+
133
+ expect(
134
+ filterChains({
135
+ chains: [CHAINS.NOBLE, CHAINS.OPTIMISM, CHAINS.MANTLE],
136
+ config: {
137
+ disabledChains: undefined,
138
+ comingSoonChainIds: [CHAINS.NOBLE.chainId],
139
+ },
140
+ direction: "to",
141
+ })
142
+ ).toEqual([CHAINS.OPTIMISM, CHAINS.MANTLE, CHAINS.NOBLE]);
143
+ });
144
+
145
+ test("sorts available chains first, then disabled chains, and finally coming soon chains", () => {
146
+ const chains = [
147
+ CHAINS.MOONBEAM,
148
+ CHAINS.POLYGON,
149
+ CHAINS.BINANCE,
150
+ CHAINS.LINEA,
151
+ CHAINS.FANTOM,
152
+ CHAINS.CELO,
153
+ ];
154
+
155
+ expect(
156
+ filterChains({
157
+ chains,
158
+ config: {
159
+ disabledChains: {
160
+ source: [CHAINS.MOONBEAM.chainId, CHAINS.LINEA.chainId],
161
+ },
162
+ comingSoonChainIds: [CHAINS.POLYGON.chainId],
163
+ },
164
+ direction: "from",
165
+ })
166
+ ).toEqual([
167
+ CHAINS.BINANCE,
168
+ CHAINS.FANTOM,
169
+ CHAINS.CELO,
170
+ CHAINS.MOONBEAM,
171
+ CHAINS.LINEA,
172
+ CHAINS.POLYGON,
173
+ ]);
174
+ });
175
+ });
176
+ });
@@ -0,0 +1,486 @@
1
+ import { nativeEvmTokenAddress } from "@/core/constants";
2
+ import {
3
+ filterTokensForDestination,
4
+ getDefaultTokenAddressForChain,
5
+ getFirstAvailableChainId,
6
+ getInitialOrDefaultTokenAddressForChain,
7
+ } from "../services/internal/configService";
8
+ import { OnlineSquidData, fetchSquidData } from "./fetchSquidData";
9
+
10
+ let squid: OnlineSquidData | undefined = undefined;
11
+ let CHAINS: OnlineSquidData["chains"] = {};
12
+ let TOKENS: OnlineSquidData["tokens"] = {};
13
+
14
+ describe("config service", () => {
15
+ beforeAll(async () => {
16
+ squid = await fetchSquidData();
17
+ CHAINS = squid?.chains;
18
+ TOKENS = squid?.tokens;
19
+ });
20
+ describe("getFirstAvailableChainId", () => {
21
+ test("returns chain id of first chain when no initialAssets or comingSoonChainIds are specified", () => {
22
+ expect(
23
+ getFirstAvailableChainId({ integratorId: "" }, "from", [
24
+ CHAINS.ETHEREUM,
25
+ CHAINS.ARBITRUM,
26
+ CHAINS.POLYGON,
27
+ ])
28
+ ).toBe(CHAINS.ETHEREUM.chainId);
29
+
30
+ expect(
31
+ getFirstAvailableChainId(
32
+ {
33
+ integratorId: "",
34
+ comingSoonChainIds: [],
35
+ initialAssets: {},
36
+ },
37
+ "from",
38
+ [CHAINS.ETHEREUM, CHAINS.ARBITRUM, CHAINS.POLYGON]
39
+ )
40
+ ).toBe(CHAINS.ETHEREUM.chainId);
41
+ });
42
+
43
+ test("returns undefined when no chains are provided", () => {
44
+ expect(getFirstAvailableChainId({ integratorId: "" }, "from", [])).toBe(
45
+ undefined
46
+ );
47
+
48
+ expect(
49
+ getFirstAvailableChainId({ integratorId: "" }, "from", undefined)
50
+ ).toBe(undefined);
51
+ });
52
+
53
+ test("filters by a single comingSoonChainId", () => {
54
+ expect(
55
+ getFirstAvailableChainId(
56
+ {
57
+ integratorId: "",
58
+ comingSoonChainIds: [CHAINS.CELO.chainId],
59
+ },
60
+ "from",
61
+ [CHAINS.CELO, CHAINS.BASE, CHAINS.AVALANCHE]
62
+ )
63
+ ).toBe(CHAINS.BASE.chainId);
64
+ });
65
+
66
+ test("filters by multiple comingSoonChainIds", () => {
67
+ expect(
68
+ getFirstAvailableChainId(
69
+ {
70
+ integratorId: "",
71
+ comingSoonChainIds: [
72
+ CHAINS.AVALANCHE.chainId,
73
+ CHAINS.POLYGON.chainId,
74
+ ],
75
+ },
76
+ "from",
77
+ [
78
+ CHAINS.POLYGON,
79
+ CHAINS.ARBITRUM,
80
+ CHAINS.AVALANCHE,
81
+ CHAINS.OPTIMISM,
82
+ CHAINS.ETHEREUM,
83
+ ]
84
+ )
85
+ ).toBe(CHAINS.ARBITRUM.chainId);
86
+ });
87
+
88
+ test("returns undefined if all chain ids are also comingSoonChainIds", () => {
89
+ const allChains = Object.values(CHAINS);
90
+ const comingSoonChainIds = allChains.map((chain) => chain.chainId);
91
+
92
+ expect(
93
+ getFirstAvailableChainId(
94
+ {
95
+ integratorId: "",
96
+ comingSoonChainIds,
97
+ },
98
+ "from",
99
+ allChains
100
+ )
101
+ ).toBe(undefined);
102
+ });
103
+
104
+ test("returns chain id of initialAssets when its ids are present in chains provided", () => {
105
+ expect(
106
+ getFirstAvailableChainId(
107
+ {
108
+ integratorId: "",
109
+ initialAssets: {
110
+ from: {
111
+ address: nativeEvmTokenAddress,
112
+ chainId: CHAINS.POLYGON.chainId,
113
+ },
114
+ },
115
+ },
116
+ "from",
117
+ [CHAINS.OPTIMISM, CHAINS.BINANCE, CHAINS.POLYGON, CHAINS.ETHEREUM]
118
+ )
119
+ ).toBe(CHAINS.POLYGON.chainId);
120
+
121
+ expect(
122
+ getFirstAvailableChainId(
123
+ {
124
+ integratorId: "",
125
+ initialAssets: {
126
+ to: {
127
+ address: nativeEvmTokenAddress,
128
+ chainId: CHAINS.AVALANCHE.chainId,
129
+ },
130
+ },
131
+ },
132
+ "to",
133
+ [CHAINS.MANTLE, CHAINS.BASE, CHAINS.AVALANCHE, CHAINS.MOONBEAM]
134
+ )
135
+ ).toBe(CHAINS.AVALANCHE.chainId);
136
+ });
137
+
138
+ test("returns chain id of first chain when initialAssets chain ids are not present in chains provided", () => {
139
+ expect(
140
+ getFirstAvailableChainId(
141
+ {
142
+ integratorId: "",
143
+ initialAssets: {
144
+ from: {
145
+ address: nativeEvmTokenAddress,
146
+ chainId: CHAINS.ARBITRUM.chainId,
147
+ },
148
+ },
149
+ },
150
+ "from",
151
+ [CHAINS.BINANCE, CHAINS.ETHEREUM, CHAINS.POLYGON]
152
+ )
153
+ ).toBe(CHAINS.BINANCE.chainId);
154
+
155
+ expect(
156
+ getFirstAvailableChainId(
157
+ {
158
+ integratorId: "",
159
+ initialAssets: {
160
+ to: {
161
+ address: nativeEvmTokenAddress,
162
+ chainId: CHAINS.MOONBEAM.chainId,
163
+ },
164
+ },
165
+ },
166
+ "to",
167
+ [CHAINS.ARBITRUM, CHAINS.AVALANCHE, CHAINS.LINEA]
168
+ )
169
+ ).toBe(CHAINS.ARBITRUM.chainId);
170
+ });
171
+
172
+ test("returns chain id of second chain when initial asset chain is a coming soon chain", () => {
173
+ expect(
174
+ getFirstAvailableChainId(
175
+ {
176
+ integratorId: "",
177
+ comingSoonChainIds: [CHAINS.ARBITRUM.chainId],
178
+ initialAssets: {
179
+ from: {
180
+ address: nativeEvmTokenAddress,
181
+ chainId: CHAINS.ARBITRUM.chainId,
182
+ },
183
+ },
184
+ },
185
+ "from",
186
+ [CHAINS.ARBITRUM, CHAINS.FANTOM, CHAINS.MANTLE]
187
+ )
188
+ ).toBe(CHAINS.FANTOM.chainId);
189
+
190
+ expect(
191
+ getFirstAvailableChainId(
192
+ {
193
+ integratorId: "",
194
+ comingSoonChainIds: [CHAINS.POLYGON.chainId],
195
+ initialAssets: {
196
+ to: {
197
+ address: nativeEvmTokenAddress,
198
+ chainId: CHAINS.POLYGON.chainId,
199
+ },
200
+ },
201
+ },
202
+ "to",
203
+ [CHAINS.POLYGON, CHAINS.BASE, CHAINS.AVALANCHE]
204
+ )
205
+ ).toBe(CHAINS.BASE.chainId);
206
+ });
207
+
208
+ test("returns chain id of second chain when first chain is disabled", () => {
209
+ expect(
210
+ getFirstAvailableChainId(
211
+ {
212
+ integratorId: "",
213
+ disabledChains: {
214
+ source: [CHAINS.ARBITRUM.chainId],
215
+ },
216
+ },
217
+ "from",
218
+ [CHAINS.ARBITRUM, CHAINS.FANTOM, CHAINS.MANTLE]
219
+ )
220
+ ).toBe(CHAINS.FANTOM.chainId);
221
+
222
+ expect(
223
+ getFirstAvailableChainId(
224
+ {
225
+ integratorId: "",
226
+ disabledChains: {
227
+ destination: [CHAINS.NOBLE.chainId],
228
+ },
229
+ },
230
+ "to",
231
+ [CHAINS.NOBLE, CHAINS.AVALANCHE, CHAINS.SCROLL]
232
+ )
233
+ ).toBe(CHAINS.AVALANCHE.chainId);
234
+ });
235
+
236
+ test("returns chain id of second chain when initial asset chain is disabled", () => {
237
+ expect(
238
+ getFirstAvailableChainId(
239
+ {
240
+ integratorId: "",
241
+ disabledChains: {
242
+ source: [CHAINS.OPTIMISM.chainId],
243
+ },
244
+ initialAssets: {
245
+ from: {
246
+ address: nativeEvmTokenAddress,
247
+ chainId: CHAINS.OPTIMISM.chainId,
248
+ },
249
+ },
250
+ },
251
+ "from",
252
+ [CHAINS.OPTIMISM, CHAINS.MOONBEAM, CHAINS.NOBLE]
253
+ )
254
+ ).toBe(CHAINS.MOONBEAM.chainId);
255
+
256
+ expect(
257
+ getFirstAvailableChainId(
258
+ {
259
+ integratorId: "",
260
+ disabledChains: {
261
+ destination: [CHAINS.LINEA.chainId],
262
+ },
263
+ initialAssets: {
264
+ to: {
265
+ address: nativeEvmTokenAddress,
266
+ chainId: CHAINS.LINEA.chainId,
267
+ },
268
+ },
269
+ },
270
+ "to",
271
+ [CHAINS.LINEA, CHAINS.BASE, CHAINS.MANTLE]
272
+ )
273
+ ).toBe(CHAINS.BASE.chainId);
274
+ });
275
+ });
276
+
277
+ describe("getDefaultTokenAddressForChain", () => {
278
+ test("returns first token address if no special config is specified", () => {
279
+ expect(
280
+ getDefaultTokenAddressForChain(
281
+ [TOKENS.ETHEREUM.USDT, TOKENS.ETHEREUM.ETH, TOKENS.ETHEREUM.WETH],
282
+ {
283
+ integratorId: "",
284
+ },
285
+ CHAINS.ETHEREUM.chainId
286
+ )
287
+ ).toBe(TOKENS.ETHEREUM.USDT.address);
288
+
289
+ expect(
290
+ getDefaultTokenAddressForChain(
291
+ [TOKENS.ETHEREUM.WBTC, TOKENS.ETHEREUM.USDC, TOKENS.ETHEREUM.ETH],
292
+ {
293
+ defaultTokensPerChain: [],
294
+ integratorId: "",
295
+ },
296
+ CHAINS.ETHEREUM.chainId
297
+ )
298
+ ).toBe(TOKENS.ETHEREUM.WBTC.address);
299
+ });
300
+
301
+ test("returns undefined if no tokens are found for specified chain id", () => {
302
+ expect(
303
+ getDefaultTokenAddressForChain(
304
+ [TOKENS.BASE.USDbC, TOKENS.BASE.axlUSDC, TOKENS.BASE.WETH],
305
+ {
306
+ integratorId: "",
307
+ },
308
+ CHAINS.ETHEREUM.chainId
309
+ )
310
+ ).toBe(undefined);
311
+ });
312
+
313
+ test("filters by defaultTokensPerChain config", () => {
314
+ expect(
315
+ getDefaultTokenAddressForChain(
316
+ [TOKENS.BASE.USDC, TOKENS.BASE.WETH, TOKENS.ETHEREUM.USDT],
317
+ {
318
+ defaultTokensPerChain: [
319
+ {
320
+ address: TOKENS.ETHEREUM.USDT.address,
321
+ chainId: TOKENS.ETHEREUM.USDT.chainId,
322
+ },
323
+ ],
324
+ integratorId: "",
325
+ },
326
+ CHAINS.ETHEREUM.chainId
327
+ )
328
+ ).toBe(TOKENS.ETHEREUM.USDT.address);
329
+
330
+ expect(
331
+ getDefaultTokenAddressForChain(
332
+ [TOKENS.ETHEREUM.WBTC, TOKENS.BASE.axlUSDC, TOKENS.BASE.ETH],
333
+ {
334
+ defaultTokensPerChain: [
335
+ {
336
+ address: TOKENS.BASE.axlUSDC.address,
337
+ chainId: TOKENS.BASE.axlUSDC.chainId,
338
+ },
339
+ ],
340
+ integratorId: "",
341
+ },
342
+ CHAINS.BASE.chainId
343
+ )
344
+ ).toBe(TOKENS.BASE.axlUSDC.address);
345
+ });
346
+
347
+ test("filters by unavailableTokenAddress", () => {
348
+ expect(
349
+ getDefaultTokenAddressForChain(
350
+ [TOKENS.ETHEREUM.WBTC, TOKENS.BASE.axlUSDC, TOKENS.BASE.USDbC],
351
+ {
352
+ integratorId: "",
353
+ },
354
+ CHAINS.BASE.chainId,
355
+ TOKENS.BASE.axlUSDC.address // unavailableTokenAddress
356
+ )
357
+ ).toBe(TOKENS.BASE.USDbC.address);
358
+ });
359
+
360
+ test("filters when unavailableTokenAddress and default token are the same", () => {
361
+ expect(
362
+ getDefaultTokenAddressForChain(
363
+ [
364
+ TOKENS.BASE.WETH,
365
+ TOKENS.ETHEREUM.ETH,
366
+ TOKENS.BASE.USDC,
367
+ TOKENS.ETHEREUM.USDT,
368
+ ],
369
+ {
370
+ integratorId: "",
371
+ defaultTokensPerChain: [
372
+ {
373
+ address: TOKENS.ETHEREUM.ETH.address,
374
+ chainId: TOKENS.ETHEREUM.ETH.chainId,
375
+ },
376
+ ],
377
+ },
378
+ CHAINS.ETHEREUM.chainId,
379
+ TOKENS.ETHEREUM.ETH.address // unavailableTokenAddress
380
+ )
381
+ ).toBe(TOKENS.ETHEREUM.USDT.address);
382
+ });
383
+ });
384
+
385
+ describe("filterTokensForDestination", () => {
386
+ test("returns different tokens than source token when from and to chains are the same", () => {
387
+ expect(
388
+ filterTokensForDestination(
389
+ [TOKENS.ETHEREUM.ETH, TOKENS.ETHEREUM.USDC, TOKENS.ETHEREUM.WETH],
390
+ CHAINS.ETHEREUM, // destination chain
391
+ TOKENS.ETHEREUM.ETH // source token
392
+ )
393
+ ).toStrictEqual([TOKENS.ETHEREUM.USDC, TOKENS.ETHEREUM.WETH]);
394
+
395
+ expect(
396
+ filterTokensForDestination(
397
+ [TOKENS.BASE.USDbC, TOKENS.BASE.WETH, TOKENS.BASE.axlUSDC],
398
+ CHAINS.BASE, // destination chain
399
+ TOKENS.BASE.axlUSDC // source token
400
+ )
401
+ ).toStrictEqual([TOKENS.BASE.USDbC, TOKENS.BASE.WETH]);
402
+ });
403
+
404
+ test("returns only tokens of same chain as destination chain", () => {
405
+ expect(
406
+ filterTokensForDestination(
407
+ [
408
+ TOKENS.ETHEREUM.USDC,
409
+ TOKENS.BASE.USDbC,
410
+ TOKENS.ETHEREUM.WETH,
411
+ TOKENS.BASE.ETH,
412
+ ],
413
+ CHAINS.ETHEREUM, // destination chain
414
+ TOKENS.BASE.USDC // source token
415
+ )
416
+ ).toStrictEqual([TOKENS.ETHEREUM.USDC, TOKENS.ETHEREUM.WETH]);
417
+ });
418
+ });
419
+
420
+ // we only test initialAssets here, as the rest of the logic is tested in getDefaultTokenAddressForChain
421
+ describe("getInitialOrDefaultTokenAddressForChain", () => {
422
+ test("returns initial assets when chain id matches", () => {
423
+ const fromInitialAsset = TOKENS.ETHEREUM.WETH;
424
+
425
+ expect(
426
+ getInitialOrDefaultTokenAddressForChain({
427
+ chainId: CHAINS.ETHEREUM.chainId,
428
+ config: {
429
+ integratorId: "",
430
+ initialAssets: {
431
+ from: fromInitialAsset,
432
+ },
433
+ },
434
+ direction: "from",
435
+ tokens: [TOKENS.ETHEREUM.USDT, fromInitialAsset],
436
+ })
437
+ ).toBe(fromInitialAsset.address);
438
+
439
+ const toInitialAsset = TOKENS.AVALANCHE.MAI;
440
+
441
+ expect(
442
+ getInitialOrDefaultTokenAddressForChain({
443
+ chainId: CHAINS.AVALANCHE.chainId,
444
+ config: {
445
+ integratorId: "",
446
+ initialAssets: {
447
+ to: toInitialAsset,
448
+ },
449
+ },
450
+ direction: "to",
451
+ tokens: [TOKENS.AVALANCHE.axlUSDC, toInitialAsset],
452
+ })
453
+ ).toBe(toInitialAsset.address);
454
+ });
455
+
456
+ test("returns first token when initial asset does not match chain id", () => {
457
+ expect(
458
+ getInitialOrDefaultTokenAddressForChain({
459
+ chainId: CHAINS.ETHEREUM.chainId, // we want to get Ethereum tokens
460
+ config: {
461
+ integratorId: "",
462
+ initialAssets: {
463
+ from: TOKENS.BASE.USDbC, // initial asset is a Base token, but we want to get Ethereum tokens
464
+ },
465
+ },
466
+ direction: "from",
467
+ tokens: [TOKENS.ETHEREUM.USDT, TOKENS.ETHEREUM.WETH],
468
+ })
469
+ ).toBe(TOKENS.ETHEREUM.USDT.address);
470
+
471
+ expect(
472
+ getInitialOrDefaultTokenAddressForChain({
473
+ chainId: CHAINS.BASE.chainId, // we want to get Base tokens
474
+ config: {
475
+ integratorId: "",
476
+ initialAssets: {
477
+ to: TOKENS.AVALANCHE.AVAX, // initial asset is an Avalanche token, but we want to get Base tokens
478
+ },
479
+ },
480
+ direction: "from",
481
+ tokens: [TOKENS.BASE.ETH, TOKENS.BASE.axlUSDC],
482
+ })
483
+ ).toBe(TOKENS.BASE.ETH.address);
484
+ });
485
+ });
486
+ });
@@ -0,0 +1,43 @@
1
+ import { Squid } from "@0xsquid/sdk";
2
+ import { ChainData, Token } from "@0xsquid/squid-types";
3
+
4
+ const INTEGRATOR_ID = "squid-react-hooks";
5
+
6
+ export interface OnlineSquidData {
7
+ tokens: Record<string, Record<string, Token>>;
8
+ chains: Record<string, ChainData>;
9
+ }
10
+
11
+ const remapSquidData = (squidData: Squid) => {
12
+ const tokens: Record<string, Record<string, Token>> = {};
13
+ const chains: Record<string, ChainData> = {};
14
+
15
+ squidData.chains.forEach((chain) => {
16
+ const chainName = chain.networkIdentifier.toUpperCase();
17
+
18
+ tokens[chainName] = squidData.tokens
19
+ .filter((token) => token.chainId === chain.chainId)
20
+ .reduce(
21
+ (acc, token) => {
22
+ const tokenSymbol = token.symbol;
23
+ acc[tokenSymbol] = token;
24
+ return acc;
25
+ },
26
+ {} as Record<string, Token>
27
+ );
28
+
29
+ chains[chainName] = chain;
30
+ });
31
+
32
+ return { tokens, chains };
33
+ };
34
+
35
+ export const fetchSquidData = async (): Promise<OnlineSquidData> => {
36
+ const squid = new Squid({
37
+ integratorId: INTEGRATOR_ID,
38
+ baseUrl: "https://v2.api.squidrouter.com",
39
+ });
40
+ await squid.init();
41
+
42
+ return remapSquidData(squid);
43
+ };