@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,63 @@
1
+ import { useSquidStore } from "@/hooks/store/useSquidStore";
2
+ import { useSwap } from "@/hooks/swap/useSwap";
3
+ import { ChainType } from "@0xsquid/sdk/dist/types";
4
+ import { useMemo } from "react";
5
+
6
+ export const useUserParams = () => {
7
+ const { config } = useSquidStore();
8
+ const { fromToken, toToken, toChain, fromChain } = useSwap();
9
+
10
+ // =============
11
+ // GAS
12
+ // =============
13
+ const getGasOnDestSupportedForThisRoute = useMemo(
14
+ () =>
15
+ // Not supporting get gas on dest for same chains
16
+ fromChain?.chainId !== toChain?.chainId &&
17
+ // If the destination chain is cosmos, we don't support getting gas there
18
+ toChain?.chainType !== ChainType.COSMOS &&
19
+ // Not supporting get gas on dest for same tokens (bridge)
20
+
21
+ ((fromToken?.subGraphIds?.some(
22
+ (sgi) => !!toToken?.subGraphIds?.includes(sgi)
23
+ ) &&
24
+ toToken?.subGraphIds?.some(
25
+ (sgi) => !!fromToken?.subGraphIds?.includes(sgi)
26
+ )) ||
27
+ // Except for uusdc -> uusdc
28
+ (fromToken?.subGraphIds?.includes("uusdc") &&
29
+ toToken?.subGraphIds?.includes("uusdc"))),
30
+ [
31
+ fromChain?.chainId,
32
+ fromToken?.subGraphIds,
33
+ toChain?.chainId,
34
+ toToken?.subGraphIds,
35
+ toChain?.chainType,
36
+ ]
37
+ );
38
+
39
+ const gasEnabled = useMemo(
40
+ () => config.enableGetGasOnDestination && getGasOnDestSupportedForThisRoute,
41
+ [config.enableGetGasOnDestination, getGasOnDestSupportedForThisRoute]
42
+ );
43
+
44
+ // =============
45
+ // BOOST
46
+ // =============
47
+ const expressSupportedForThisRoute = useMemo(
48
+ () => toChain?.chainType !== ChainType.COSMOS,
49
+ [toChain?.chainType]
50
+ );
51
+
52
+ const expressEnabled = useMemo(
53
+ () => expressSupportedForThisRoute && config.enableExpress,
54
+ [config.enableExpress, expressSupportedForThisRoute]
55
+ );
56
+
57
+ return {
58
+ gasEnabled,
59
+ expressEnabled,
60
+ expressSupportedForThisRoute,
61
+ getGasOnDestSupportedForThisRoute,
62
+ };
63
+ };
@@ -0,0 +1,53 @@
1
+ import type { ChainData, Token } from "@0xsquid/sdk/dist/types";
2
+ import { ChainType } from "@0xsquid/sdk/dist/types";
3
+ import { useMutation } from "@tanstack/react-query";
4
+ import { useAccount, useNetwork, useSwitchNetwork } from "wagmi";
5
+
6
+ export const useAddToken = (
7
+ chainToCompare: ChainData | undefined,
8
+ tokenToCompare: Token | undefined
9
+ ) => {
10
+ const { chain: currentEvmChain } = useNetwork();
11
+ const { connector } = useAccount();
12
+
13
+ const { switchNetworkAsync } = useSwitchNetwork({
14
+ throwForSwitchChainNotSupported: true,
15
+ });
16
+
17
+ /**
18
+ * Add token to wallet
19
+ */
20
+ const addToken = useMutation(async (tokenToAdd: Token | void) => {
21
+ const token = tokenToAdd ?? tokenToCompare;
22
+ if (token && chainToCompare?.chainType === ChainType.EVM) {
23
+ const provider = await connector?.getProvider();
24
+
25
+ // Switch network if needed
26
+ if (currentEvmChain?.id.toString() !== token?.chainId) {
27
+ await switchNetworkAsync?.(+token.chainId);
28
+ // Metamask is not popping the second modal if we don't wait a bit
29
+ // eslint-disable-next-line no-promise-executor-return
30
+ await new Promise((resolve) => setTimeout(resolve, 100));
31
+ }
32
+ // Add token to wallet
33
+ provider.request({
34
+ method: "wallet_watchAsset",
35
+ params: {
36
+ type: "ERC20",
37
+ options: {
38
+ address: token?.address,
39
+ symbol: token?.symbol,
40
+ decimals: token?.decimals,
41
+ image: token?.logoURI,
42
+ },
43
+ },
44
+ });
45
+ }
46
+ // TODO: Implement keplr add token
47
+ return false;
48
+ });
49
+
50
+ return {
51
+ addToken,
52
+ };
53
+ };
@@ -0,0 +1,56 @@
1
+ import { PriorityConnectors } from "@/core/constants";
2
+ import { useEffect } from "react";
3
+ import { useConnect } from "wagmi";
4
+
5
+ const PRIORITY_CONNECTOR_ID = [PriorityConnectors.Safe];
6
+
7
+ const WAGMI_LOCAL_STORAGE_WALLET_ID = "wagmi.wallet";
8
+
9
+ /**
10
+ * Auto connect to Safe provider if it's present
11
+ * Safe will always be the priority connector
12
+ * Because if it's present, this means the user is on a Safe app iframe
13
+ */
14
+ export const useAutoConnect = () => {
15
+ const { connect, connectors } = useConnect();
16
+
17
+ const wagmiLocalStorageWalletID = localStorage.getItem(
18
+ WAGMI_LOCAL_STORAGE_WALLET_ID
19
+ );
20
+
21
+ useEffect(() => {
22
+ const queryParameters = new URLSearchParams(window.location.search);
23
+ // TODO: At the moment gnosis was published without the embed parameter in the manifest,
24
+ // but we should ask gnosis to change our app url to https://app.squidrouter.com/?embed="safe"
25
+ // So we could remove the priority connector array and just use the embed parameter
26
+ const embedType = queryParameters.get("embed") as PriorityConnectors;
27
+ let priorityConnector: any;
28
+
29
+ if (embedType) {
30
+ priorityConnector = connectors.find((c) => c.id === embedType);
31
+ } else {
32
+ priorityConnector = connectors.find(
33
+ (c) =>
34
+ PRIORITY_CONNECTOR_ID.includes(c.id as PriorityConnectors) && c.ready
35
+ );
36
+ }
37
+
38
+ // Connect priority provider if it's present
39
+ // Otherwise, connect to the last wallet used if authorized
40
+ if (priorityConnector) {
41
+ connect({ connector: priorityConnector });
42
+ } else if (wagmiLocalStorageWalletID) {
43
+ const wagmiLocalStorageWalletConnector = connectors.find(
44
+ (c) => c.id === JSON.parse(wagmiLocalStorageWalletID) && c.ready
45
+ );
46
+
47
+ // If we do not check this, the wallet will try to connect even if the user disconnected on purpose
48
+ // Checking this will prevent the wallet to popup at each page refresh if the user is disconnected
49
+ wagmiLocalStorageWalletConnector?.isAuthorized().then((isAuthorized) => {
50
+ if (isAuthorized) {
51
+ connect({ connector: wagmiLocalStorageWalletConnector });
52
+ }
53
+ });
54
+ }
55
+ }, [connect, connectors]);
56
+ };
@@ -0,0 +1,84 @@
1
+ import { useSwapRoutePersistStore } from "@/hooks/store/useSquidStore";
2
+ import { useSwap } from "@/hooks/swap/useSwap";
3
+ import { useMultiChainWallet } from "@/hooks/wallet/useMultiChainWallet";
4
+ import SafeAppsSDK from "@safe-global/safe-apps-sdk";
5
+ import { TransactionStatus as GnosisTransactionStatus } from "@safe-global/safe-apps-sdk/dist/src/types";
6
+ import { useCallback, useEffect, useMemo, useState } from "react";
7
+ import { useAccount } from "wagmi";
8
+
9
+ export const useGnosisContext = () => {
10
+ const { connector } = useAccount();
11
+ const { fromChain } = useSwap();
12
+ const { swapRoute } = useSwapRoutePersistStore();
13
+ const { connectedAddress } = useMultiChainWallet(fromChain);
14
+ const [isGnosisContext, setisGnosisContext] = useState(false);
15
+
16
+ /**
17
+ * Method that will be used to send transaction
18
+ * TODO: could have loaded the sdk when app load and stored globally
19
+ */
20
+ const getGnosisSafeContext = useCallback(async () => {
21
+ const appsSdk = new SafeAppsSDK();
22
+ const safe = await appsSdk.safe.getInfo();
23
+ const isSafeContext =
24
+ safe.chainId !== undefined &&
25
+ safe.safeAddress !== undefined &&
26
+ connector?.id === "safe";
27
+
28
+ setisGnosisContext(isSafeContext);
29
+
30
+ if (isSafeContext) return appsSdk;
31
+ return undefined;
32
+ }, [connector]);
33
+
34
+ useEffect(() => {
35
+ getGnosisSafeContext();
36
+ }, [connector]);
37
+
38
+ /**
39
+ * Check if we are in a Gnosis Safe Context
40
+ * And if source wallet address = destination address
41
+ * If swapRoute.destinationAddress is not defined, it means that it's the same from the source
42
+ */
43
+ const isSameAddressAndGnosisContext = useMemo(() => {
44
+ const destAddressSameAsSource =
45
+ connectedAddress === swapRoute?.destinationAddress ||
46
+ swapRoute?.destinationAddress === undefined;
47
+
48
+ return isGnosisContext && destAddressSameAsSource;
49
+ }, [connectedAddress, swapRoute, isGnosisContext]);
50
+
51
+ /**
52
+ * There's a specific way to get the transaction hash for the safe connector
53
+ * Using then initial hash received by the transaction, we can get the real hash
54
+ * @param hashReceived
55
+ * @returns
56
+ */
57
+ const getGnosisTransactionHash = async (
58
+ initialHash: string
59
+ ): Promise<string> => {
60
+ const safeSdk = await getGnosisSafeContext();
61
+
62
+ const tx = await safeSdk?.txs.getBySafeTxHash(initialHash);
63
+ const status: GnosisTransactionStatus | undefined = tx?.txStatus;
64
+ if (
65
+ status !== GnosisTransactionStatus.FAILED &&
66
+ status !== GnosisTransactionStatus.SUCCESS &&
67
+ status !== GnosisTransactionStatus.CANCELLED
68
+ ) {
69
+ // Workaround, Wait 2 seconds before checking the gnosis status again
70
+ // TODO: There might be a better way to do this
71
+ // eslint-disable-next-line no-promise-executor-return
72
+ await new Promise((res) => setTimeout(res, 2000));
73
+ return getGnosisTransactionHash(initialHash);
74
+ }
75
+ return tx?.txHash ?? initialHash;
76
+ };
77
+
78
+ return {
79
+ getGnosisSafeContext,
80
+ isSameAddressAndGnosisContext,
81
+ isGnosisContext,
82
+ getGnosisTransactionHash,
83
+ };
84
+ };
@@ -0,0 +1,44 @@
1
+ import { PriorityConnectors } from "@/core/constants";
2
+ import { useGnosisContext } from "@/hooks/wallet/useGnosisContext";
3
+ import { useMemo } from "react";
4
+
5
+ export const useIntegratorContext = () => {
6
+ const { isGnosisContext } = useGnosisContext();
7
+ /**
8
+ * Check if the wallet is handled externally
9
+ * Example: Ledger or Gnosis Safe
10
+ * Either by the embed parameter or by Gnosis context
11
+ */
12
+ const walletHandledExternally: boolean = useMemo(() => {
13
+ const embedTypesHavingExternalWallet: PriorityConnectors[] = [
14
+ PriorityConnectors.LedgerLive,
15
+ PriorityConnectors.Safe,
16
+ ];
17
+ const queryParameters = new URLSearchParams(window.location.search);
18
+ const embedType = queryParameters.get("embed") as PriorityConnectors;
19
+
20
+ return (
21
+ isGnosisContext || embedTypesHavingExternalWallet.includes(embedType)
22
+ );
23
+ }, [isGnosisContext]);
24
+
25
+ const isEmbed = useMemo(() => {
26
+ const queryParameters = new URLSearchParams(window.location.search);
27
+ const embedType = queryParameters.get("embed") as PriorityConnectors;
28
+ return !!embedType;
29
+ }, []);
30
+
31
+ /**
32
+ * It's important to know if we can use certain features such as
33
+ * the clipboard reading
34
+ */
35
+ const widgetInIframe = useMemo(() => {
36
+ try {
37
+ return window.self !== window.top;
38
+ } catch (e) {
39
+ return false;
40
+ }
41
+ }, []);
42
+
43
+ return { walletHandledExternally, isEmbed, widgetInIframe };
44
+ };
@@ -0,0 +1,146 @@
1
+ import { useCosmosContext } from "@/core/providers/CosmosProvider";
2
+ import type { AddEthereumChainParameter } from "@/core/types/wallet";
3
+ import { useCosmosForChain } from "@/hooks/cosmos/useCosmosForChain";
4
+ import { formatWalletAddress } from "@/services/internal/walletService";
5
+ import type { ChainData } from "@0xsquid/sdk/dist/types";
6
+ import { ChainType } from "@0xsquid/sdk/dist/types";
7
+ import { useMutation } from "@tanstack/react-query";
8
+ import { AddChainError, UserRejectedRequestError } from "@wagmi/core";
9
+ import { ethers } from "ethers";
10
+ import { useMemo } from "react";
11
+ import {
12
+ ChainNotConfiguredError,
13
+ SwitchChainError,
14
+ useAccount,
15
+ useNetwork,
16
+ useSwitchNetwork,
17
+ } from "wagmi";
18
+
19
+ export const useMultiChainWallet = (chainToCompare: ChainData | undefined) => {
20
+ const { chain: currentEvmChain } = useNetwork();
21
+ const { isConnected: isEvmConnected, connector, address } = useAccount();
22
+ const { isConnected: cosmosIsConnected } = useCosmosContext();
23
+ const { cosmosAddress } = useCosmosForChain(chainToCompare);
24
+
25
+ const { switchNetworkAsync } = useSwitchNetwork({
26
+ throwForSwitchChainNotSupported: true,
27
+ });
28
+
29
+ /**
30
+ * Get connected address, depends on chainType
31
+ */
32
+ const connectedAddress = useMemo(() => {
33
+ switch (chainToCompare?.chainType) {
34
+ case ChainType.EVM:
35
+ return address;
36
+ case ChainType.COSMOS:
37
+ return cosmosAddress;
38
+ default:
39
+ return address;
40
+ }
41
+ }, [address, chainToCompare?.chainType, cosmosAddress]);
42
+
43
+ const parsedAddress = useMemo(
44
+ () => formatWalletAddress(connectedAddress),
45
+ [connectedAddress]
46
+ );
47
+
48
+ /**
49
+ * Change current network for desired chain
50
+ */
51
+ const changeNetwork = useMutation(
52
+ async () => {
53
+ // throw for tenderly fork chain addition
54
+ if (chainToCompare?.rpc.includes("tenderly")) {
55
+ throw new SwitchChainError("Add tenderly chain");
56
+ }
57
+
58
+ if (chainToCompare?.chainType === ChainType.EVM) {
59
+ await switchNetworkAsync?.(+chainToCompare.chainId);
60
+ }
61
+ // Implement keplr change network
62
+ // Looks like there are no method to do that at the moment
63
+ return false;
64
+ },
65
+ {
66
+ onError: async (error: any) => {
67
+ if (error instanceof UserRejectedRequestError) {
68
+ return;
69
+ }
70
+ if (
71
+ (error instanceof ChainNotConfiguredError ||
72
+ error instanceof SwitchChainError ||
73
+ error instanceof AddChainError) &&
74
+ chainToCompare
75
+ ) {
76
+ const provider = await connector?.getProvider();
77
+ const chainName = chainToCompare.rpc.includes("tenderly")
78
+ ? `${chainToCompare.networkName} Tenderly fork`
79
+ : chainToCompare.networkName;
80
+ const chainParameters: AddEthereumChainParameter = {
81
+ chainId: ethers.utils.hexValue(
82
+ parseInt(chainToCompare.chainId, 10)
83
+ ),
84
+ chainName,
85
+ nativeCurrency: chainToCompare.nativeCurrency,
86
+ rpcUrls: [chainToCompare.rpc],
87
+ blockExplorerUrls: chainToCompare.blockExplorerUrls,
88
+ iconUrls: [chainToCompare.chainIconURI],
89
+ };
90
+ provider.request({
91
+ method: "wallet_addEthereumChain",
92
+ params: [chainParameters],
93
+ });
94
+ }
95
+ },
96
+ }
97
+ );
98
+
99
+ /**
100
+ * Handle multiple chains
101
+ */
102
+ const networkConnected = useMemo(() => {
103
+ switch (chainToCompare?.chainType) {
104
+ case ChainType.EVM:
105
+ return isEvmConnected;
106
+ case ChainType.COSMOS:
107
+ return cosmosIsConnected;
108
+ default:
109
+ return isEvmConnected;
110
+ }
111
+ }, [isEvmConnected, chainToCompare, cosmosIsConnected]);
112
+
113
+ /**
114
+ * Checks if Network is connected and with the right chain
115
+ */
116
+ const networkConnectedOnRightChain = useMemo(() => {
117
+ if (chainToCompare?.chainType === ChainType.EVM) {
118
+ // tenderly validation based on rpc
119
+ if (
120
+ currentEvmChain?.rpcUrls?.default?.http?.length &&
121
+ currentEvmChain?.rpcUrls?.default?.http[0].includes("tenderly")
122
+ ) {
123
+ return (
124
+ isEvmConnected &&
125
+ currentEvmChain?.rpcUrls?.default?.http[0].toLowerCase() ===
126
+ chainToCompare.rpc.toLowerCase()
127
+ );
128
+ }
129
+
130
+ return (
131
+ isEvmConnected &&
132
+ currentEvmChain?.id.toString() === chainToCompare.chainId
133
+ );
134
+ }
135
+ // TODO: Implement keplr check
136
+ return true;
137
+ }, [isEvmConnected, currentEvmChain, chainToCompare]);
138
+
139
+ return {
140
+ changeNetwork,
141
+ networkConnected,
142
+ networkConnectedOnRightChain,
143
+ connectedAddress,
144
+ parsedAddress,
145
+ };
146
+ };
@@ -0,0 +1,86 @@
1
+ import { wallets } from "@/core/constants";
2
+ import { useCosmosContext } from "@/core/providers/CosmosProvider";
3
+ import type { Wallet } from "@/core/types/wallet";
4
+ import { useSquidChains } from "@/hooks/chains/useSquidChains";
5
+ import { useSquidRouter } from "@/hooks/webapp-router/useSquidRouter";
6
+ import {
7
+ formatChainsForWagmi,
8
+ redirectExtensionStoreIfNotInstalled,
9
+ } from "@/services/internal/walletService";
10
+ import type { ChainData } from "@0xsquid/sdk/dist/types";
11
+ import { ChainType } from "@0xsquid/sdk/dist/types";
12
+ import { useMemo } from "react";
13
+ import { useAccount, useConnect, useDisconnect } from "wagmi";
14
+
15
+ export const useWallet = (chain: ChainData | undefined) => {
16
+ const { connector: activeConnector, isConnected: isEvmConnected } =
17
+ useAccount();
18
+
19
+ const { disconnectAsync: disconnectEvm } = useDisconnect();
20
+ const {
21
+ isConnected: cosmosIsConnected,
22
+ connectCosmos,
23
+ cosmosConnectedWallet,
24
+ } = useCosmosContext();
25
+ const { connectAsync } = useConnect();
26
+ const { previousRoute } = useSquidRouter();
27
+ const { chains } = useSquidChains();
28
+
29
+ const connectWallet = async (
30
+ wallet: Wallet,
31
+ redirect?: boolean,
32
+ direction?: "from" | "to"
33
+ ) => {
34
+ redirectExtensionStoreIfNotInstalled(wallet);
35
+
36
+ try {
37
+ if (wallet && wallet.connector && wallet.type === ChainType.EVM) {
38
+ await disconnectEvm();
39
+ await connectAsync({
40
+ connector: wallet.connector(formatChainsForWagmi(chains)),
41
+ });
42
+ } else if (wallet.type === ChainType.COSMOS && chain) {
43
+ await connectCosmos?.mutateAsync({ chain, wallet, direction });
44
+ }
45
+ if (redirect) {
46
+ previousRoute();
47
+ }
48
+ } catch (error) {
49
+ console.error(error);
50
+ }
51
+ };
52
+
53
+ /**
54
+ * Get the connected wallet object
55
+ * @returns {Wallet | undefined}
56
+ */
57
+ const currentWallet = useMemo(() => {
58
+ if (chain?.chainType === ChainType.EVM) {
59
+ if (activeConnector?.id && isEvmConnected) {
60
+ const activeWallet = wallets.find(
61
+ (w) => w.connectorId === activeConnector.id
62
+ );
63
+ return activeWallet;
64
+ }
65
+ }
66
+ if (chain?.chainType === ChainType.COSMOS) {
67
+ if (cosmosIsConnected) {
68
+ return wallets.find(
69
+ (w) => w.connectorId === cosmosConnectedWallet?.connectorId
70
+ );
71
+ }
72
+ }
73
+ return undefined;
74
+ }, [
75
+ activeConnector?.id,
76
+ chain?.chainType,
77
+ cosmosConnectedWallet?.connectorId,
78
+ cosmosIsConnected,
79
+ isEvmConnected,
80
+ ]);
81
+
82
+ return {
83
+ currentWallet,
84
+ connectWallet,
85
+ };
86
+ };
@@ -0,0 +1,42 @@
1
+ import { WidgetRoute } from "@/core/types/route";
2
+ import { useSquidRouterStore } from "@/hooks/store/useSquidStore";
3
+
4
+ export const useSquidRouter = () => {
5
+ const { history } = useSquidRouterStore();
6
+
7
+ const switchRoute = (
8
+ route: WidgetRoute,
9
+ params?: { [key: string]: any | undefined } | undefined,
10
+ addRouteToHistory = true
11
+ ) => {
12
+ const currentHistory = useSquidRouterStore.getState().history;
13
+ if (addRouteToHistory) {
14
+ useSquidRouterStore.setState({
15
+ history: [...currentHistory, { route, params }],
16
+ });
17
+ } else {
18
+ useSquidRouterStore.setState({
19
+ history: [...currentHistory.slice(0, -1), { route, params }],
20
+ });
21
+ }
22
+ };
23
+
24
+ const previousRoute = () => {
25
+ const currentHistory = useSquidRouterStore.getState().history;
26
+ currentHistory.pop();
27
+
28
+ useSquidRouterStore.setState({
29
+ history: currentHistory,
30
+ });
31
+ };
32
+
33
+ const currentRoute = history[history.length - 1].route;
34
+ const currentRouteParams = history[history.length - 1].params;
35
+
36
+ return {
37
+ currentRoute,
38
+ switchRoute,
39
+ previousRoute,
40
+ currentRouteParams,
41
+ };
42
+ };
package/src/index.ts ADDED
@@ -0,0 +1,5 @@
1
+ // src/index.tsx
2
+ export * from "./hooks";
3
+ export * from "./provider";
4
+ export * from "./core";
5
+ export * from "./services";
@@ -0,0 +1,105 @@
1
+ import { wallets } from "@/core/constants";
2
+ import { CosmosProvider } from "@/core/providers/CosmosProvider";
3
+ import { defaultOptions } from "@/core/queries/react-query-config";
4
+ import { useSquidStore } from "@/hooks/store/useSquidStore";
5
+ import { getConfigWithDefaults } from "@/services/internal/configService";
6
+ import { formatChainsForWagmi } from "@/services/internal/walletService";
7
+ import { Squid } from "@0xsquid/sdk";
8
+ import { ChainData, ChainType } from "@0xsquid/squid-types";
9
+ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
10
+ import { createContext, useEffect, useState } from "react";
11
+ import React from "react";
12
+ import { WagmiConfig, configureChains, createClient } from "wagmi";
13
+ import { jsonRpcProvider } from "wagmi/providers/jsonRpc";
14
+
15
+ interface Config {
16
+ integratorId: string;
17
+ }
18
+
19
+ interface ISdkProviderProps {
20
+ children: React.ReactNode;
21
+ config: Config;
22
+ }
23
+
24
+ const SquidContext = createContext(undefined);
25
+
26
+ const verifyIntegratorIdValidity = (integratorId: string) => {
27
+ if (!integratorId) {
28
+ throw new Error("Integrator ID is required");
29
+ }
30
+ };
31
+
32
+ export const SquidProvider: React.FC<ISdkProviderProps> = ({
33
+ children,
34
+ config,
35
+ }: ISdkProviderProps) => {
36
+ console.log("SquidProvider");
37
+ verifyIntegratorIdValidity(config.integratorId);
38
+
39
+ const queryClient = new QueryClient({ defaultOptions });
40
+ const [wagmiClient, setWagmiClient] = useState<any>(null);
41
+
42
+ /**
43
+ * Initialize Wagmi client
44
+ * @param chains
45
+ */
46
+ const initWagmiClient = (chains: ChainData[]) => {
47
+ const { provider, chains: wagmiChains } = configureChains(
48
+ formatChainsForWagmi(chains),
49
+ [
50
+ jsonRpcProvider({
51
+ rpc: (chain) => ({
52
+ http: chain.rpcUrls.default.http[0] ?? "",
53
+ webSocket: undefined,
54
+ }),
55
+ }),
56
+ ]
57
+ );
58
+
59
+ // Get formatted connectors from Squid supported wallets
60
+ const evmWalletConnectors = wallets
61
+ .filter((w) => w.type === ChainType.EVM)
62
+ .map((w) => w.connector?.(wagmiChains));
63
+
64
+ const wClient = createClient({
65
+ persister: null,
66
+ provider,
67
+ connectors: [...evmWalletConnectors],
68
+ });
69
+
70
+ setWagmiClient(wClient);
71
+ };
72
+
73
+ // Initialize Squid SDK & wagmi client
74
+ useEffect(() => {
75
+ const initializeSdk = async () => {
76
+ const squid = new Squid({
77
+ integratorId: config.integratorId,
78
+ baseUrl: "https://v2.api.squidrouter.com",
79
+ });
80
+ await squid.init();
81
+
82
+ useSquidStore.setState((_) => ({
83
+ config: getConfigWithDefaults({
84
+ integratorId: config.integratorId,
85
+ }),
86
+ squid,
87
+ }));
88
+ initWagmiClient(squid.chains);
89
+ };
90
+
91
+ initializeSdk();
92
+ }, []);
93
+
94
+ return (
95
+ <SquidContext.Provider value={undefined}>
96
+ <QueryClientProvider client={queryClient}>
97
+ {wagmiClient && (
98
+ <WagmiConfig client={wagmiClient}>
99
+ <CosmosProvider>{children}</CosmosProvider>
100
+ </WagmiConfig>
101
+ )}
102
+ </QueryClientProvider>
103
+ </SquidContext.Provider>
104
+ );
105
+ };
@@ -0,0 +1,2 @@
1
+ declare module "*.png";
2
+ declare module "*.svg";