@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,440 @@
1
+ // import type { TransactionResponse } from "@0xsquid/sdk/dist/types/ethers";
2
+ import { useCosmosContext } from "@/core/providers/CosmosProvider";
3
+ import { keys } from "@/core/queries/queries-keys";
4
+ import type { TransactionReplacedError } from "@/core/types/error";
5
+ import { TransactionErrorType } from "@/core/types/error";
6
+ import type { RouteResponse } from "@0xsquid/sdk/dist/types";
7
+ import { ChainType } from "@0xsquid/sdk/dist/types";
8
+ import { SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate";
9
+ import type { TransactionReceipt } from "@ethersproject/abstract-provider";
10
+ import { useMutation, useQueryClient } from "@tanstack/react-query";
11
+ import { TxRaw } from "cosmjs-types/cosmos/tx/v1beta1/tx";
12
+ import { ethers } from "ethers";
13
+ import { Logger } from "ethers/lib/utils.js";
14
+ import { useCallback, useMemo } from "react";
15
+ import { useAccount, useSigner } from "wagmi";
16
+
17
+ import type {
18
+ TransactionHistoryStore,
19
+ TransactionParams,
20
+ } from "@/core/types/transaction";
21
+ import { TransactionStatus } from "@/core/types/transaction";
22
+ import { getWorkingCosmosRpcUrl } from "@/services/external/rpcService";
23
+ import { getTransactionError } from "@/services/internal/errorService";
24
+ import {
25
+ getSourceExplorerTxUrl,
26
+ replaceTransactionAtNonce,
27
+ updateTransactionHistoryStatus,
28
+ } from "@/services/internal/transactionService";
29
+
30
+ import { useSquidChains } from "@/hooks//chains/useSquidChains";
31
+ import { usePersistStore, useSquidStore } from "@/hooks/store/useSquidStore";
32
+ import { useSwap } from "@/hooks/swap/useSwap";
33
+ import { useEstimate } from "@/hooks/transaction/useEstimate";
34
+ import { useGetRoute } from "@/hooks/transaction/useGetRoute";
35
+ import { useGnosisContext } from "@/hooks/wallet/useGnosisContext";
36
+ import { useMultiChainWallet } from "@/hooks/wallet/useMultiChainWallet";
37
+ import { WidgetEvents } from "@/services/internal/eventService";
38
+
39
+ enum ExecutionState {
40
+ NOT_ENOUGH_BALANCE = "NOT_ENOUGH_BALANCE",
41
+ NOT_CONNECTED = "NOT_CONNECTED",
42
+ READY = "READY",
43
+ }
44
+
45
+ export const useExecuteTransaction = (
46
+ route: ReturnType<typeof useGetRoute>
47
+ ) => {
48
+ const signer = useSigner();
49
+ const { cosmosSigner } = useCosmosContext();
50
+
51
+ const { chains } = useSquidChains();
52
+ const queryClient = useQueryClient();
53
+ const { fromPrice, toPrice, squid, currentTransaction, config } =
54
+ useSquidStore();
55
+
56
+ const { enoughBalanceToSwap } = useEstimate({
57
+ squidRoute: route,
58
+ });
59
+
60
+ const { connector: activeConnector } = useAccount();
61
+
62
+ const { fromChain, toChain, fromToken, toToken } = useSwap();
63
+ const { getGnosisTransactionHash } = useGnosisContext();
64
+
65
+ const { connectedAddress: sourceUserAddress } =
66
+ useMultiChainWallet(fromChain);
67
+
68
+ const isSameChainSwap = useMemo(
69
+ () => fromChain?.chainId === toChain?.chainId,
70
+ [fromChain?.chainId, toChain?.chainId]
71
+ );
72
+
73
+ /**
74
+ * States to see if the swap execution is possible or not based on swap & user params
75
+ * And if not possible, gives the reason
76
+ * TODO: The NOT_ENOUGH_BALANCE state could be fetch independently from the route response, only based on user fromAmount
77
+ */
78
+ const executionState = useMemo(() => {
79
+ if (!enoughBalanceToSwap) {
80
+ return ExecutionState.NOT_ENOUGH_BALANCE;
81
+ }
82
+
83
+ // TODO:; Add more states here
84
+
85
+ return ExecutionState.READY;
86
+ }, [enoughBalanceToSwap]);
87
+
88
+ /**
89
+ * This method is used to persist the transaction in local storage
90
+ * This way we can access user's transaction history
91
+ * TODO: This part will be fetched from the backend, using user address in the future
92
+ * @param txToPersist
93
+ * @param route
94
+ */
95
+ const persistTransaction = async (
96
+ txToPersist: TransactionParams,
97
+ route?: RouteResponse["route"]
98
+ ) => {
99
+ // Persisting the transaction in local storage
100
+ if (route) {
101
+ const previousHistoryList =
102
+ usePersistStore.getState().transactionsHistory ?? [];
103
+
104
+ const newHistoryElement: TransactionHistoryStore = {
105
+ ...txToPersist,
106
+ params: route.params,
107
+ estimate: route.estimate,
108
+ };
109
+
110
+ usePersistStore.setState({
111
+ transactionsHistory: [...previousHistoryList, newHistoryElement],
112
+ });
113
+ }
114
+ };
115
+
116
+ /**
117
+ * Set the transaction state in the store
118
+ * This is useful to access the latest transaction from any hook
119
+ */
120
+ const setTransactionState = useCallback(
121
+ ({
122
+ route,
123
+ txHash,
124
+ nonce,
125
+ status,
126
+ sourceStatus,
127
+ userAddress,
128
+ axelarUrl,
129
+ }: {
130
+ route?: RouteResponse["route"];
131
+ txHash: string;
132
+ nonce?: number;
133
+ status: TransactionStatus;
134
+ sourceStatus: TransactionStatus;
135
+ userAddress?: string;
136
+ axelarUrl?: string;
137
+ }) => {
138
+ if (route && route.transactionRequest) {
139
+ const { routeType } = route.transactionRequest;
140
+
141
+ const tx: TransactionParams = {
142
+ fromChain,
143
+ toChain,
144
+ routeType,
145
+ nonce,
146
+ transactionId: txHash,
147
+ status,
148
+ sourceStatus,
149
+ timestamp: Date.now(),
150
+ fromAddress: userAddress,
151
+ sourceTxExplorerUrl: getSourceExplorerTxUrl(fromChain, txHash),
152
+ sourceExplorerImgUrl: fromChain?.chainIconURI,
153
+ axelarUrl,
154
+ };
155
+
156
+ useSquidStore.setState({
157
+ currentTransaction: tx,
158
+ });
159
+
160
+ return tx;
161
+ }
162
+ return undefined;
163
+ },
164
+ [fromChain, toChain]
165
+ );
166
+
167
+ const swapQueryCosmos = useMutation(
168
+ async (route?: RouteResponse["route"]) => {
169
+ const chainData = chains.find(
170
+ (c) => c.chainId === route?.params.fromChain
171
+ );
172
+
173
+ if (cosmosSigner && chainData) {
174
+ try {
175
+ const rpc = await getWorkingCosmosRpcUrl(chainData);
176
+ const signingClient = await SigningCosmWasmClient.connectWithSigner(
177
+ rpc,
178
+ cosmosSigner
179
+ );
180
+ const signerAddress = (await cosmosSigner.getAccounts())[0].address;
181
+
182
+ if (signerAddress && signingClient && route) {
183
+ const tx = (await squid?.executeRoute({
184
+ signer: signingClient as any,
185
+ signerAddress,
186
+ route,
187
+ executionSettings: {
188
+ infiniteApproval: config.infiniteApproval,
189
+ },
190
+ })) as TxRaw;
191
+
192
+ // set the tx state to loading, as soon as user signed the tx
193
+ setTransactionState({
194
+ txHash: "", // We don't have the hash yet
195
+ route,
196
+ status: TransactionStatus.ONGOING,
197
+ sourceStatus: TransactionStatus.ONGOING,
198
+ });
199
+
200
+ // broadcast the signed tx to get hash and listen to events
201
+ const response = await signingClient.broadcastTx(
202
+ TxRaw.encode(tx).finish()
203
+ );
204
+
205
+ const hash = response.transactionHash;
206
+
207
+ // Dispatch event so it can be listened from outside the widget
208
+ WidgetEvents.getInstance().dispatchSwapExecuteCall(route, hash);
209
+
210
+ const txParams: TransactionParams | undefined = setTransactionState(
211
+ {
212
+ route,
213
+ txHash: hash,
214
+ userAddress: sourceUserAddress,
215
+ status: TransactionStatus.ONGOING,
216
+ sourceStatus: TransactionStatus.ONGOING,
217
+ axelarUrl: undefined,
218
+ }
219
+ );
220
+ if (txParams) {
221
+ persistTransaction(txParams);
222
+ }
223
+
224
+ return response.code === 0;
225
+ }
226
+ } catch (error) {
227
+ console.log(error);
228
+
229
+ const castedError = (error as { message: string | undefined }) ?? {};
230
+ if (castedError.message?.includes("Request rejected")) {
231
+ throw new Error(castedError.message);
232
+ }
233
+ }
234
+ }
235
+ throw new Error("Need all parameters");
236
+ }
237
+ );
238
+
239
+ // If the transaction is replaced, we need to update the transaction hash
240
+ // Transaction replaced can mean that the user has speed up the transaction for example
241
+ // Could also be cancelled
242
+ const handleTransactionReplacementError = useCallback(
243
+ async ({
244
+ error,
245
+ route,
246
+ status,
247
+ sourceStatus,
248
+ userAddress,
249
+ axelarUrl,
250
+ }: {
251
+ error: any;
252
+ route?: RouteResponse["route"];
253
+ status: TransactionStatus;
254
+ sourceStatus: TransactionStatus;
255
+ userAddress?: string;
256
+ axelarUrl?: string;
257
+ }): Promise<TransactionReceipt> => {
258
+ if (route && error.code === Logger.errors.TRANSACTION_REPLACED) {
259
+ const txReplacementError = error as TransactionReplacedError;
260
+
261
+ const { hash: newHash, nonce: newNonce } =
262
+ txReplacementError.replacement;
263
+
264
+ if (route.transactionRequest) {
265
+ const txParams = setTransactionState({
266
+ route,
267
+ txHash: newHash,
268
+ nonce: newNonce,
269
+ userAddress: sourceUserAddress,
270
+ status,
271
+ sourceStatus,
272
+ axelarUrl: undefined,
273
+ });
274
+
275
+ if (txParams) {
276
+ const newHistoryElement: TransactionHistoryStore = {
277
+ ...txParams,
278
+ params: route.params,
279
+ estimate: route.estimate,
280
+ };
281
+
282
+ // Need to store the new transaction hash on the previous transaction
283
+ usePersistStore.setState({
284
+ transactionsHistory: replaceTransactionAtNonce(
285
+ newNonce,
286
+ sourceUserAddress!,
287
+ usePersistStore.getState().transactionsHistory,
288
+ newHistoryElement
289
+ ),
290
+ });
291
+ }
292
+ }
293
+
294
+ try {
295
+ const response = await txReplacementError.replacement.wait();
296
+ return response;
297
+ } catch (replacementError) {
298
+ // Maybe the transaction was replaced again
299
+ // recursive call
300
+ return handleTransactionReplacementError({
301
+ error,
302
+ route,
303
+ status,
304
+ sourceStatus,
305
+ userAddress,
306
+ axelarUrl,
307
+ });
308
+ }
309
+ } else {
310
+ throw error;
311
+ }
312
+ },
313
+ [setTransactionState, sourceUserAddress, squid?.axelarscanURL]
314
+ );
315
+
316
+ const swapQueryEvm = useMutation(async (route?: RouteResponse["route"]) => {
317
+ if (route && !!squid && signer.isSuccess && signer.data) {
318
+ const txResponse = (await squid.executeRoute({
319
+ signer: signer.data,
320
+ route,
321
+ executionSettings: {
322
+ infiniteApproval: config.infiniteApproval,
323
+ },
324
+ })) as unknown as ethers.providers.TransactionResponse;
325
+
326
+ let hash = txResponse.hash;
327
+ if (activeConnector?.id === "safe") {
328
+ hash = await getGnosisTransactionHash(txResponse.hash);
329
+ }
330
+
331
+ // Dispatch event so it can be listened from outside the widget
332
+ WidgetEvents.getInstance().dispatchSwapExecuteCall(route, hash);
333
+
334
+ if (route.transactionRequest) {
335
+ const txParams: TransactionParams | undefined = setTransactionState({
336
+ route,
337
+ txHash: hash,
338
+ nonce: txResponse.nonce,
339
+ userAddress: sourceUserAddress,
340
+ status: TransactionStatus.INITIAL_LOADING,
341
+ sourceStatus: TransactionStatus.ONGOING,
342
+ axelarUrl: undefined,
343
+ });
344
+ if (txParams) {
345
+ persistTransaction(txParams);
346
+ }
347
+ }
348
+
349
+ try {
350
+ const response = await txResponse.wait();
351
+ return response;
352
+ } catch (error: any) {
353
+ return handleTransactionReplacementError({
354
+ error,
355
+ route,
356
+ status: TransactionStatus.INITIAL_LOADING,
357
+ sourceStatus: TransactionStatus.ONGOING,
358
+ userAddress: sourceUserAddress,
359
+ axelarUrl: undefined,
360
+ });
361
+ }
362
+ }
363
+ throw new Error("Need all parameters");
364
+ });
365
+
366
+ /**
367
+ * Execute cross chain swap with selected tokens
368
+ * getRoute should be called before this mutation
369
+ */
370
+ const swapQuery = useMutation(
371
+ async () => {
372
+ const sourceChain = chains?.find(
373
+ (chain) => chain.chainId == route.data?.params?.fromChain
374
+ );
375
+
376
+ if (sourceChain?.chainType === ChainType.COSMOS) {
377
+ return swapQueryCosmos.mutateAsync(route.data);
378
+ }
379
+ if (sourceChain?.chainType === ChainType.EVM) {
380
+ return swapQueryEvm.mutateAsync(route.data);
381
+ }
382
+
383
+ throw new Error("Invalid parameters or chain not found");
384
+ },
385
+ {
386
+ onError: (error: any) => {
387
+ const { currentTransaction: currentTx } = useSquidStore.getState();
388
+ const errorObject = getTransactionError(error);
389
+ useSquidStore.setState({
390
+ currentTransaction: {
391
+ ...currentTx!,
392
+ status: TransactionStatus.ERROR,
393
+ sourceStatus: TransactionStatus.ERROR,
394
+ error: errorObject,
395
+ },
396
+ });
397
+ if (
398
+ currentTx?.transactionId &&
399
+ errorObject?.type === TransactionErrorType.CALL_EXCEPTION
400
+ ) {
401
+ usePersistStore.setState({
402
+ transactionsHistory: updateTransactionHistoryStatus(
403
+ currentTransaction?.transactionId,
404
+ TransactionStatus.ERROR,
405
+ usePersistStore.getState().transactionsHistory,
406
+ undefined
407
+ ),
408
+ });
409
+ }
410
+ },
411
+ onSuccess: () => {
412
+ const { currentTransaction: currentTx } = useSquidStore.getState();
413
+
414
+ queryClient.invalidateQueries(keys({}).balances());
415
+
416
+ useSquidStore.setState({
417
+ currentTransaction: {
418
+ ...currentTx!,
419
+ sourceStatus: TransactionStatus.SUCCESS,
420
+ status: isSameChainSwap
421
+ ? TransactionStatus.SUCCESS
422
+ : TransactionStatus.ONGOING,
423
+ },
424
+ });
425
+ },
426
+ }
427
+ );
428
+
429
+ return {
430
+ swapQuery,
431
+ currentTransaction,
432
+ fromToken,
433
+ toToken,
434
+ fromPrice,
435
+ toPrice,
436
+ toChain,
437
+ fromChain,
438
+ executionState,
439
+ };
440
+ };
@@ -0,0 +1,46 @@
1
+ import { useSquidStore } from "@/hooks/store/useSquidStore";
2
+ import { useSwap } from "@/hooks/swap/useSwap";
3
+ import { useUserParams } from "@/hooks/user/useUserParams";
4
+ import { formatSeconds } from "@/services/internal/transactionService";
5
+ import { useMemo } from "react";
6
+
7
+ export const useEstimateExpress = () => {
8
+ const { expressSupportedForThisRoute } = useUserParams();
9
+ const { fromChain } = useSwap();
10
+ const { config } = useSquidStore();
11
+
12
+ const backendSupportingExpress = useMemo(
13
+ () => fromChain?.enableBoostByDefault,
14
+ [fromChain]
15
+ );
16
+
17
+ const expressActivatedUI = useMemo(
18
+ () =>
19
+ expressSupportedForThisRoute &&
20
+ config.enableExpress &&
21
+ backendSupportingExpress,
22
+ [
23
+ config.enableExpress,
24
+ backendSupportingExpress,
25
+ expressSupportedForThisRoute,
26
+ ]
27
+ );
28
+
29
+ const transactionTimeEstimate = useMemo(
30
+ () =>
31
+ expressSupportedForThisRoute && config.enableExpress
32
+ ? formatSeconds(20, "s", "min")
33
+ : formatSeconds(fromChain?.estimatedRouteDuration || 0, "s", "min"),
34
+ [
35
+ config.enableExpress,
36
+ fromChain?.estimatedRouteDuration,
37
+ expressSupportedForThisRoute,
38
+ ]
39
+ );
40
+
41
+ return {
42
+ expressSupportedForThisRoute,
43
+ transactionTimeEstimate,
44
+ expressActivatedUI,
45
+ };
46
+ };
@@ -0,0 +1,145 @@
1
+ import type {
2
+ DexName,
3
+ FallbackAddress,
4
+ RouteRequest,
5
+ Token,
6
+ } from "@0xsquid/sdk/dist/types";
7
+
8
+ import { osmosisChainId } from "@/core/constants";
9
+ import { useCosmosContext } from "@/core/providers/CosmosProvider";
10
+ import { useMutation } from "@tanstack/react-query";
11
+ import { constants, utils } from "ethers";
12
+ import { useCallback } from "react";
13
+
14
+ import {
15
+ useSquidStore,
16
+ useSwapRoutePersistStore,
17
+ } from "@/hooks/store/useSquidStore";
18
+ import { useUserParams } from "@/hooks/user/useUserParams";
19
+
20
+ export const useGetRoute = () => {
21
+ const { getCosmosAddressForChain, isConnected: isCosmosConnected } =
22
+ useCosmosContext();
23
+ const { squid, config } = useSquidStore();
24
+ const { swapRoute } = useSwapRoutePersistStore();
25
+ const { expressEnabled, gasEnabled } = useUserParams();
26
+
27
+ /**
28
+ * Get fallback addresses for cosmos chains
29
+ * This is needed by backend when a cosmos swap occurs between non coin118 chains
30
+ * The backend might need a coin118 address to send the funds to in case of failure for a swap happening between the two chains
31
+ */
32
+ const getCosmosFallbackAddresses = useCallback(async () => {
33
+ // If not connected, user might have filled it manually
34
+ if (!isCosmosConnected) {
35
+ return swapRoute?.fallbackAddress
36
+ ? ([
37
+ {
38
+ address: swapRoute?.fallbackAddress,
39
+ coinType: 118,
40
+ },
41
+ // TODO: Temp parsing here, should be included in v2 sdk
42
+ ] as FallbackAddress[])
43
+ : undefined;
44
+ }
45
+
46
+ // the user is connected to cosmos, we can get the fallback address from the wallet
47
+ // We only need coin118, so taking osmosis hub address by default
48
+ const osmosisAddress = await getCosmosAddressForChain(
49
+ osmosisChainId.mainnet
50
+ );
51
+ if (!osmosisAddress) {
52
+ return undefined;
53
+ }
54
+
55
+ // Always used the one set by user (if any), otherwise use the derived osmo address
56
+ // only return something if one of the fallback address is defined
57
+ if (!!swapRoute?.fallbackAddress || !!osmosisAddress) {
58
+ return [
59
+ {
60
+ address: swapRoute?.fallbackAddress ?? osmosisAddress,
61
+ coinType: 118,
62
+ },
63
+ ] as FallbackAddress[];
64
+ }
65
+
66
+ return undefined;
67
+ }, [getCosmosAddressForChain, isCosmosConnected, swapRoute?.fallbackAddress]);
68
+
69
+ /**
70
+ * Fetching route data from the API
71
+ * These data will be used to trigger the transaction
72
+ * @returns {Route} Route data
73
+ */
74
+ return useMutation(
75
+ async ({
76
+ fromChain,
77
+ toChain,
78
+ fromToken,
79
+ toToken,
80
+ sourceUserAddress,
81
+ destinationAddress,
82
+ fromPrice,
83
+ }: {
84
+ fromChain: string;
85
+ toChain: string;
86
+ fromToken: Token;
87
+ toToken: Token;
88
+ sourceUserAddress: string;
89
+ destinationAddress: string;
90
+ fromPrice: string;
91
+ }) => {
92
+ const quoteOnly =
93
+ sourceUserAddress === undefined || destinationAddress === undefined;
94
+
95
+ const isEvmSwap = Number(fromChain) > 0 && Number(toChain) > 0;
96
+
97
+ const cosmosFallbackAddresses =
98
+ quoteOnly || isEvmSwap ? undefined : await getCosmosFallbackAddresses();
99
+
100
+ // TODO: Put RouteRequest back when it supports fallbackAddress
101
+ const params: RouteRequest = {
102
+ fromChain,
103
+ fromToken: fromToken.address,
104
+ fromAddress: sourceUserAddress ?? constants.AddressZero,
105
+ fromAmount: utils
106
+ .parseUnits(fromPrice?.toString() ?? "0", fromToken?.decimals)
107
+ .toString(),
108
+ toChain,
109
+ toToken: toToken.address,
110
+ toAddress: destinationAddress ?? "",
111
+ quoteOnly:
112
+ sourceUserAddress === undefined || destinationAddress === undefined,
113
+ slippageConfig: {
114
+ // We set slippage to undefined because automode will be picked if slippage is undefined
115
+ // Slippage to 1 = auto mode
116
+ slippage: config.slippage === 1 ? undefined : config.slippage,
117
+ autoMode: 1,
118
+ },
119
+ enableBoost: expressEnabled,
120
+ prefer: config.preferDex as DexName[] | undefined,
121
+ receiveGasOnDestination: gasEnabled,
122
+ onChainQuoting: !!config.onChainQuoting,
123
+ };
124
+
125
+ // If the swap is involving cosmos chains, we need to add the fallback addresses (if any)
126
+ if (
127
+ cosmosFallbackAddresses &&
128
+ cosmosFallbackAddresses.length > 0 &&
129
+ cosmosFallbackAddresses[0].address
130
+ ) {
131
+ params.fallbackAddresses = cosmosFallbackAddresses;
132
+ }
133
+
134
+ const { route, requestId } = await squid!.getRoute({
135
+ ...params,
136
+ });
137
+
138
+ useSquidStore.setState({
139
+ currentRequestId: requestId,
140
+ });
141
+
142
+ return route;
143
+ }
144
+ );
145
+ };