@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,261 @@
1
+ import { defaultValues } from "@/core/constants";
2
+ import type { AppConfig } from "@/core/types/config";
3
+ import type { ChainData, Token } from "@0xsquid/sdk/dist/types";
4
+ import { ChainType } from "@0xsquid/sdk/dist/types";
5
+ import get from "lodash/get";
6
+ import { shareSubgraphId } from "./assetsService";
7
+
8
+ export const getConfigWithDefaults = (
9
+ config: AppConfig | undefined
10
+ ): AppConfig => {
11
+ return {
12
+ integratorId: get(
13
+ config,
14
+ "integratorId",
15
+ defaultValues.config.integratorId
16
+ ),
17
+ companyName: get(config, "companyName", defaultValues.config.companyName),
18
+ slippage: get(config, "slippage", defaultValues.config.slippage),
19
+ advanced: get(config, "advanced", defaultValues.config.advanced),
20
+ internalSameChainSwapAllowed: get(
21
+ config,
22
+ "internalSameChainSwapAllowed",
23
+ defaultValues.config.internalSameChainSwapAllowed
24
+ ),
25
+ hideAnimations: get(
26
+ config,
27
+ "hideAnimations",
28
+ defaultValues.config.hideAnimations
29
+ ),
30
+ infiniteApproval: get(
31
+ config,
32
+ "infiniteApproval",
33
+ defaultValues.config.infiniteApproval
34
+ ),
35
+ enableExpress: get(
36
+ config,
37
+ "enableExpress",
38
+ defaultValues.config.enableExpress
39
+ ),
40
+ collectFees: get(config, "collectFees", defaultValues.config.collectFees),
41
+ enableGetGasOnDestination: get(
42
+ config,
43
+ "enableGetGasOnDestination",
44
+ defaultValues.config.enableGetGasOnDestination
45
+ ),
46
+ apiUrl: get(config, "apiUrl", defaultValues.config.apiUrl),
47
+ showOnRampLink: get(
48
+ config,
49
+ "showOnRampLink",
50
+ defaultValues.config.showOnRampLink
51
+ ),
52
+ priceImpactWarnings: get(
53
+ config,
54
+ "priceImpactWarnings",
55
+ defaultValues.config.priceImpactWarnings
56
+ ),
57
+ initialAssets: get(
58
+ config,
59
+ "initialAssets",
60
+ defaultValues.config.initialAssets
61
+ ),
62
+ defaultTokensPerChain: get(
63
+ config,
64
+ "defaultTokensPerChain",
65
+ defaultValues.config.defaultTokensPerChain
66
+ ),
67
+ preferDex: get(config, "preferDex", defaultValues.config.preferDex),
68
+ favTokens: get(config, "favTokens", defaultValues.config.favTokens),
69
+ comingSoonChainIds: get(
70
+ config,
71
+ "comingSoonChainIds",
72
+ defaultValues.config.comingSoonChainIds
73
+ ),
74
+ availableChains: get(
75
+ config,
76
+ "availableChains",
77
+ defaultValues.config.availableChains
78
+ ),
79
+ onChainQuoting: get(
80
+ config,
81
+ "onChainQuoting",
82
+ defaultValues.config.onChainQuoting
83
+ ),
84
+ disabledChains: get(
85
+ config,
86
+ "disabledChains",
87
+ defaultValues.config.disabledChains
88
+ ),
89
+ };
90
+ };
91
+
92
+ export const randomIntFromInterval = (min: number, max: number) => {
93
+ return Math.floor(Math.random() * (max - min + 1) + min);
94
+ };
95
+
96
+ export const getTokensForChain = (
97
+ tokens: Token[],
98
+ chainId: number | string | undefined
99
+ ) => {
100
+ return chainId ? tokens.filter((t) => t.chainId === chainId) : tokens;
101
+ };
102
+
103
+ export const getFirstAvailableChainId = (
104
+ config: AppConfig,
105
+ direction: "from" | "to",
106
+ chains: ChainData[] | undefined = []
107
+ ) => {
108
+ // available chains are those that are not coming soon or disabled chains
109
+ const availableChains = chains.filter(
110
+ (c) =>
111
+ !config.comingSoonChainIds?.find((id) => id === c.chainId) &&
112
+ !config.disabledChains?.[
113
+ direction === "from" ? "source" : "destination"
114
+ ]?.some((id) => id === c.chainId)
115
+ );
116
+
117
+ if (
118
+ direction === "from" &&
119
+ config.initialAssets?.from &&
120
+ availableChains.some(
121
+ (c) => c.chainId === String(config.initialAssets?.from?.chainId)
122
+ )
123
+ ) {
124
+ return config.initialAssets.from.chainId;
125
+ }
126
+
127
+ if (
128
+ direction === "to" &&
129
+ config.initialAssets?.to &&
130
+ availableChains.some(
131
+ (c) => c.chainId === String(config.initialAssets?.to?.chainId)
132
+ )
133
+ ) {
134
+ return config.initialAssets.to.chainId;
135
+ }
136
+
137
+ return availableChains.length > 0 ? availableChains[0].chainId : undefined;
138
+ };
139
+
140
+ export const getDefaultTokenAddressForChain = (
141
+ tokens: Token[],
142
+ config: AppConfig,
143
+ chainId: number | string | undefined,
144
+ unavailableTokenAddress?: string
145
+ ): string | undefined => {
146
+ const chainIdDefaultToken = config.defaultTokensPerChain?.find(
147
+ (token) => token.chainId == chainId
148
+ );
149
+
150
+ if (
151
+ chainIdDefaultToken &&
152
+ chainIdDefaultToken.address !== unavailableTokenAddress
153
+ ) {
154
+ return chainIdDefaultToken.address;
155
+ }
156
+
157
+ const filteredTokens = chainId
158
+ ? tokens.filter(
159
+ (t) => t.chainId === chainId && t.address !== unavailableTokenAddress
160
+ )
161
+ : tokens;
162
+
163
+ return filteredTokens.length > 0 ? filteredTokens[0].address : undefined;
164
+ };
165
+
166
+ /**
167
+ * Retrieves the initial token address for a specific chain and direction.
168
+ * If no initial assets are set in config, returns the default token address for the chain.
169
+ *
170
+ * @param tokens - The list of tokens.
171
+ * @param config - The application configuration.
172
+ * @param chainId - The ID of the chain.
173
+ * @param direction - The direction ("from" or "to").
174
+ * @returns The initial token address if found; otherwise, the default token address.
175
+ */
176
+ export const getInitialOrDefaultTokenAddressForChain = ({
177
+ chainId,
178
+ config,
179
+ direction,
180
+ tokens,
181
+ unavailableTokenAddress,
182
+ }: {
183
+ tokens: Token[];
184
+ config: AppConfig;
185
+ chainId: number | string | undefined;
186
+ direction: "from" | "to";
187
+ unavailableTokenAddress?: string;
188
+ }): string | undefined => {
189
+ if (
190
+ direction === "from" &&
191
+ config.initialAssets?.from &&
192
+ config.initialAssets.from.chainId === chainId
193
+ ) {
194
+ return config.initialAssets.from.address;
195
+ }
196
+
197
+ if (
198
+ direction === "to" &&
199
+ config.initialAssets?.to &&
200
+ config.initialAssets.to.chainId === chainId
201
+ ) {
202
+ return config.initialAssets.to.address;
203
+ }
204
+
205
+ return getDefaultTokenAddressForChain(
206
+ tokens,
207
+ config,
208
+ chainId,
209
+ unavailableTokenAddress
210
+ );
211
+ };
212
+
213
+ /**
214
+ * Filter tokens for destination chain
215
+ *
216
+ * Case 1: fromToken.bridgeOnly = true
217
+ * Destination token list shows only tokens with the same commonKey as fromToken
218
+ *
219
+ * Case 2: fromToken.bridgeOnly = false
220
+ * Destination token list shows all tokens with bridgeOnly = false
221
+ * OR Destination token list shows all tokens with the same commonKey as fromToken
222
+ * @param tokens
223
+ * @param selectedDestinationChain
224
+ * @param selectedSourceChainID
225
+ * @param selectedSourceToken
226
+ * @returns
227
+ */
228
+ export const filterTokensForDestination = <T extends Token>(
229
+ tokens: T[],
230
+ selectedDestinationChain: ChainData | undefined,
231
+ selectedSourceToken: Token | undefined
232
+ ): T[] => {
233
+ let filteredTokens = [];
234
+
235
+ if (selectedSourceToken?.subGraphOnly) {
236
+ filteredTokens = tokens.filter(
237
+ (t) =>
238
+ shareSubgraphId(t, selectedSourceToken) &&
239
+ t.chainId === selectedDestinationChain?.chainId
240
+ );
241
+ }
242
+
243
+ filteredTokens = tokens.filter(
244
+ (t) => !t.subGraphOnly || shareSubgraphId(t, selectedSourceToken as Token)
245
+ );
246
+
247
+ // We don't want to show the same source token & chain in the destination list
248
+ // except for Cosmos swaps
249
+ return filteredTokens.filter((t) => {
250
+ const areSameToken =
251
+ t.address === selectedSourceToken?.address &&
252
+ t.chainId === selectedSourceToken.chainId;
253
+
254
+ // only return tokens that are on the same chain as the destination chain
255
+ const isValidChain = t.chainId === selectedDestinationChain?.chainId;
256
+ const isCosmosSwap =
257
+ selectedDestinationChain?.chainType === ChainType.COSMOS;
258
+
259
+ return isValidChain && (isCosmosSwap || !areSameToken);
260
+ });
261
+ };
@@ -0,0 +1,124 @@
1
+ import type {
2
+ SquidRouteError,
3
+ SquidStatusError,
4
+ TransactionError,
5
+ TransactionErrorWithMessage,
6
+ } from "@/core/types/error";
7
+ import { TransactionErrorType } from "@/core/types/error";
8
+
9
+ export const transactionErrorCode: Record<string, TransactionErrorWithMessage> =
10
+ {
11
+ // Metamask
12
+ ACTION_REJECTED: {
13
+ type: TransactionErrorType.REJECTED_BY_USER,
14
+ message: "Return to the swap page and try again.",
15
+ },
16
+ // Keplr
17
+ "Request rejected": {
18
+ type: TransactionErrorType.REJECTED_BY_USER,
19
+ message: "Return to the swap page and try again.",
20
+ },
21
+ // Metamask
22
+ 4001: {
23
+ type: TransactionErrorType.REJECTED_BY_USER,
24
+ message: "Return to the swap page and try again.",
25
+ },
26
+ CALL_EXCEPTION: {
27
+ type: TransactionErrorType.CALL_EXCEPTION,
28
+ message:
29
+ "Swap failed on the source chain. Please let us know by submitting a ticket here.",
30
+ },
31
+ UNKNOWN: {
32
+ type: TransactionErrorType.UNKNOWN,
33
+ message: "The transaction could not be initiated.",
34
+ },
35
+ };
36
+
37
+ /**
38
+ * Check if the error is an EVM transaction error
39
+ * Because Typescript supports casting but that's only for compilation time,
40
+ * We need it at runtime since we're getting the error object from the wallet
41
+ * @param error
42
+ * @returns boolean
43
+ */
44
+ export const isTransactionError = (error: any): error is TransactionError => {
45
+ return (
46
+ error &&
47
+ (typeof error.code === "string" ||
48
+ typeof error.code === "number" ||
49
+ typeof error.message === "string" ||
50
+ typeof error.reason === "string" ||
51
+ typeof error.action === "string")
52
+ );
53
+ };
54
+
55
+ /**
56
+ * Return the error from enum entry
57
+ * @param error
58
+ * @returns boolean
59
+ */
60
+ export const getTransactionError = (
61
+ error: any
62
+ ): TransactionErrorWithMessage => {
63
+ if (isTransactionError(error)) {
64
+ const castError = error as TransactionError;
65
+ if (castError.code) return transactionErrorCode[castError.code];
66
+ if (castError.message) return transactionErrorCode[castError.message];
67
+ return transactionErrorCode.UNKNOWN;
68
+ }
69
+ return transactionErrorCode.UNKNOWN;
70
+ };
71
+
72
+ // --------------------
73
+ // SQUID ROUTE ERRORS
74
+ // --------------------
75
+ // TODO: Components will be handled outside hooks lib
76
+ // const squidRouteErrorMapping: Record<
77
+ // SquidRouteErrorType,
78
+ // string | JSX.Element
79
+ // > = {
80
+ // Unknown: RouteDefaultErrorMsg(),
81
+ // UnknownError: RouteDefaultErrorMsg(),
82
+ // SquidServiceError: RouteDefaultErrorMsg(),
83
+ // };
84
+
85
+ /**
86
+ * Check if the error is an Squid route error
87
+ * Because Typescript supports casting but that's only for compilation time,
88
+ * We need it at runtime since we're getting the error object from the wallet
89
+ * @param error
90
+ * @returns boolean
91
+ */
92
+ export const isSwapRouteError = (error: any): error is SquidRouteError => {
93
+ return (
94
+ error &&
95
+ (typeof error.code === "string" || typeof error.errorType === "string")
96
+ );
97
+ };
98
+
99
+ export const isStatusError = (error: any): error is SquidStatusError => {
100
+ return error && typeof error.errorType === "string";
101
+ };
102
+
103
+ /**
104
+ * Tries to parse as SquidRouteError Type & Return the error from Record entries
105
+ * @param error
106
+ * @returns string error message
107
+ */
108
+ // TODO: move this outside hooks
109
+ // export const getSquidRouteErrorMessage = (error: any): string | JSX.Element => {
110
+ // if (isSwapRouteError(error)) {
111
+ // // Try to get the error message from the error code
112
+ // const codeMsg =
113
+ // error.code || error.errorType
114
+ // ? squidRouteErrorMapping[
115
+ // (error.code ?? error.errorType) as SquidRouteErrorType
116
+ // ]
117
+ // : undefined;
118
+
119
+ // if (codeMsg) return codeMsg;
120
+ // // If there is no error code, try to get the error message from backend
121
+ // if (error.message) return error.message;
122
+ // }
123
+ // return squidRouteErrorMapping.Unknown;
124
+ // };
@@ -0,0 +1,93 @@
1
+ import type { EventListenerFunction, WidgetEventMap } from "@/core/types/event";
2
+ import type { RouteResponse } from "@0xsquid/sdk/dist/types";
3
+
4
+ export class WidgetEvents extends EventTarget {
5
+ private static instance: WidgetEvents;
6
+
7
+ private constructor() {
8
+ super();
9
+ }
10
+
11
+ // Singleton, because we need to access this service outside the widget
12
+ public static getInstance(): WidgetEvents {
13
+ if (!WidgetEvents.instance) {
14
+ WidgetEvents.instance = new WidgetEvents();
15
+ }
16
+ return WidgetEvents.instance;
17
+ }
18
+
19
+ /**
20
+ * Overriding the addEventListener method to make it easier to use with typescript
21
+ * @param type
22
+ * @param listener
23
+ * @param options
24
+ */
25
+ listenToWidget<T extends keyof WidgetEventMap>(
26
+ type: T,
27
+ listener: EventListenerFunction<T>,
28
+ options?: boolean | AddEventListenerOptions
29
+ ) {
30
+ super.addEventListener(type, listener as unknown as EventListener, options);
31
+ }
32
+
33
+ /**
34
+ * Overriding the removeEventListener method to make it easier to use with typescript
35
+ * @param type
36
+ * @param listener
37
+ * @param options
38
+ */
39
+ removeWidgetListener<T extends keyof WidgetEventMap>(
40
+ type: T,
41
+ listener: EventListenerFunction<T>,
42
+ options?: boolean | EventListenerOptions
43
+ ) {
44
+ super.removeEventListener(
45
+ type,
46
+ listener as unknown as EventListener,
47
+ options
48
+ );
49
+ }
50
+
51
+ /**
52
+ * Dispatch an event of the widget
53
+ * To be listened from an integrator website
54
+ * For example to display a success message when transaction is done
55
+ * @param name
56
+ * @param data
57
+ */
58
+ dispatch<T extends keyof WidgetEventMap>(name: T, data: WidgetEventMap[T]) {
59
+ this.dispatchEvent(new CustomEvent(name, { detail: data }));
60
+ }
61
+
62
+ /**
63
+ * Will dispatch the main axelar status of transaction
64
+ * This will be called every time the status is received by backend
65
+ * (Using interval to check status)
66
+ * @param status
67
+ */
68
+ dispatchSwapStatus(status: string) {
69
+ this.dispatch("swapStatus", { status });
70
+ }
71
+
72
+ /**
73
+ * Dispatch event when user executes a swap
74
+ * Only when we have the tx hash received
75
+ * @param route
76
+ */
77
+ dispatchSwapExecuteCall(route: RouteResponse["route"], txHash: string) {
78
+ this.dispatch("swap", { route, txHash });
79
+ }
80
+
81
+ /**
82
+ * Dispatch event when user changes chain/token
83
+ * @param swapParams
84
+ */
85
+ dispatchSwapParamsChanged(swapParams: {
86
+ fromChainId?: string;
87
+ toChainId?: string;
88
+ fromTokenAddress?: string;
89
+ toTokenAddress?: string;
90
+ }) {
91
+ this.dispatch("swapParamsChanged", { ...swapParams });
92
+ }
93
+ }
@@ -0,0 +1,10 @@
1
+ import { BigNumber as BN } from "bignumber.js";
2
+
3
+ export const convertTokenAmountToUSD = (
4
+ tokenAmount: string,
5
+ priceUSD?: string
6
+ ) => {
7
+ return BN(tokenAmount)
8
+ .multipliedBy(priceUSD ?? "0")
9
+ .toFixed();
10
+ };