@sodax/dapp-kit 1.5.7-beta → 2.0.0-rc.1

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 (202) hide show
  1. package/README.md +300 -422
  2. package/ai-exported/AGENTS.md +134 -0
  3. package/ai-exported/integration/README.md +49 -0
  4. package/ai-exported/integration/ai-rules.md +79 -0
  5. package/ai-exported/integration/architecture.md +274 -0
  6. package/ai-exported/integration/features/README.md +29 -0
  7. package/ai-exported/integration/features/auxiliary-services.md +169 -0
  8. package/ai-exported/integration/features/bitcoin.md +87 -0
  9. package/ai-exported/integration/features/bridge.md +91 -0
  10. package/ai-exported/integration/features/dex.md +152 -0
  11. package/ai-exported/integration/features/migration.md +118 -0
  12. package/ai-exported/integration/features/money-market.md +116 -0
  13. package/ai-exported/integration/features/staking.md +123 -0
  14. package/ai-exported/integration/features/swap.md +101 -0
  15. package/ai-exported/integration/quickstart.md +187 -0
  16. package/ai-exported/integration/recipes/README.md +136 -0
  17. package/ai-exported/integration/recipes/backend-queries.md +157 -0
  18. package/ai-exported/integration/recipes/bitcoin.md +193 -0
  19. package/ai-exported/integration/recipes/bridge.md +174 -0
  20. package/ai-exported/integration/recipes/dex.md +204 -0
  21. package/ai-exported/integration/recipes/invalidations.md +115 -0
  22. package/ai-exported/integration/recipes/migration.md +212 -0
  23. package/ai-exported/integration/recipes/money-market.md +206 -0
  24. package/ai-exported/integration/recipes/mutation-error-handling.md +118 -0
  25. package/ai-exported/integration/recipes/observability.md +93 -0
  26. package/ai-exported/integration/recipes/setup.md +144 -0
  27. package/ai-exported/integration/recipes/staking.md +202 -0
  28. package/ai-exported/integration/recipes/swap.md +272 -0
  29. package/ai-exported/integration/recipes/wallet-connectivity.md +101 -0
  30. package/ai-exported/integration/reference/README.md +12 -0
  31. package/ai-exported/integration/reference/glossary.md +188 -0
  32. package/ai-exported/integration/reference/hooks-index.md +194 -0
  33. package/ai-exported/integration/reference/public-api.md +110 -0
  34. package/ai-exported/integration/reference/querykey-conventions.md +179 -0
  35. package/ai-exported/migration/README.md +60 -0
  36. package/ai-exported/migration/ai-rules.md +81 -0
  37. package/ai-exported/migration/breaking-changes/hook-signatures.md +233 -0
  38. package/ai-exported/migration/breaking-changes/querykey-conventions.md +108 -0
  39. package/ai-exported/migration/breaking-changes/result-handling.md +211 -0
  40. package/ai-exported/migration/breaking-changes/sdk-leakage.md +165 -0
  41. package/ai-exported/migration/checklist.md +89 -0
  42. package/ai-exported/migration/features/README.md +34 -0
  43. package/ai-exported/migration/features/auxiliary-services.md +114 -0
  44. package/ai-exported/migration/features/bitcoin.md +88 -0
  45. package/ai-exported/migration/features/bridge.md +123 -0
  46. package/ai-exported/migration/features/dex.md +101 -0
  47. package/ai-exported/migration/features/migration.md +120 -0
  48. package/ai-exported/migration/features/money-market.md +97 -0
  49. package/ai-exported/migration/features/staking.md +109 -0
  50. package/ai-exported/migration/features/swap.md +118 -0
  51. package/ai-exported/migration/recipes.md +188 -0
  52. package/ai-exported/migration/reference/README.md +15 -0
  53. package/ai-exported/migration/reference/deleted-hooks.md +110 -0
  54. package/ai-exported/migration/reference/error-shape-crosswalk.md +144 -0
  55. package/ai-exported/migration/reference/renamed-hooks.md +66 -0
  56. package/dist/index.cjs +2642 -0
  57. package/dist/index.cjs.map +1 -0
  58. package/dist/index.d.cts +1550 -0
  59. package/dist/index.d.ts +1020 -2051
  60. package/dist/index.mjs +1581 -1531
  61. package/dist/index.mjs.map +1 -1
  62. package/package.json +20 -10
  63. package/src/contexts/index.ts +0 -3
  64. package/src/hooks/_mutationContract.test.ts +99 -0
  65. package/src/hooks/backend/README.md +2 -2
  66. package/src/hooks/backend/index.ts +13 -13
  67. package/src/hooks/backend/unwrapResult.ts +1 -0
  68. package/src/hooks/backend/useBackendAllMoneyMarketAssets.ts +13 -45
  69. package/src/hooks/backend/useBackendAllMoneyMarketBorrowers.ts +29 -59
  70. package/src/hooks/backend/useBackendIntentByHash.ts +21 -47
  71. package/src/hooks/backend/useBackendIntentByTxHash.ts +23 -50
  72. package/src/hooks/backend/useBackendMoneyMarketAsset.ts +21 -54
  73. package/src/hooks/backend/useBackendMoneyMarketAssetBorrowers.ts +30 -57
  74. package/src/hooks/backend/useBackendMoneyMarketAssetSuppliers.ts +31 -58
  75. package/src/hooks/backend/useBackendMoneyMarketPosition.ts +22 -38
  76. package/src/hooks/backend/useBackendOrderbook.ts +27 -49
  77. package/src/hooks/backend/useBackendSubmitSwapTx.ts +30 -36
  78. package/src/hooks/backend/useBackendSubmitSwapTxStatus.ts +38 -58
  79. package/src/hooks/backend/useBackendUserIntents.ts +25 -63
  80. package/src/hooks/bitcoin/index.ts +9 -8
  81. package/src/hooks/bitcoin/useBitcoinBalance.ts +20 -5
  82. package/src/hooks/bitcoin/useExpiredUtxos.ts +26 -16
  83. package/src/hooks/bitcoin/useFundTradingWallet.ts +33 -30
  84. package/src/hooks/bitcoin/useRadfiAuth.ts +43 -40
  85. package/src/hooks/bitcoin/useRadfiSession.ts +53 -59
  86. package/src/hooks/bitcoin/useRadfiWithdraw.ts +35 -53
  87. package/src/hooks/bitcoin/useRenewUtxos.ts +30 -50
  88. package/src/hooks/bitcoin/useTradingWallet.ts +1 -1
  89. package/src/hooks/bitcoin/useTradingWalletBalance.ts +25 -14
  90. package/src/hooks/bridge/index.ts +5 -5
  91. package/src/hooks/bridge/useBridge.ts +29 -55
  92. package/src/hooks/bridge/useBridgeAllowance.ts +38 -38
  93. package/src/hooks/bridge/useBridgeApprove.ts +32 -57
  94. package/src/hooks/bridge/useGetBridgeableAmount.ts +23 -37
  95. package/src/hooks/bridge/useGetBridgeableTokens.ts +27 -50
  96. package/src/hooks/dex/index.ts +16 -16
  97. package/src/hooks/dex/useClaimRewards.ts +35 -54
  98. package/src/hooks/dex/useCreateDecreaseLiquidityParams.ts +7 -20
  99. package/src/hooks/dex/useCreateDepositParams.ts +7 -21
  100. package/src/hooks/dex/useCreateSupplyLiquidityParams.ts +13 -28
  101. package/src/hooks/dex/useCreateWithdrawParams.ts +7 -20
  102. package/src/hooks/dex/useDecreaseLiquidity.ts +40 -66
  103. package/src/hooks/dex/useDexAllowance.ts +29 -75
  104. package/src/hooks/dex/useDexApprove.ts +32 -43
  105. package/src/hooks/dex/useDexDeposit.ts +42 -49
  106. package/src/hooks/dex/useDexWithdraw.ts +32 -43
  107. package/src/hooks/dex/useLiquidityAmounts.ts +13 -82
  108. package/src/hooks/dex/usePoolBalances.ts +50 -72
  109. package/src/hooks/dex/usePoolData.ts +17 -43
  110. package/src/hooks/dex/usePools.ts +11 -38
  111. package/src/hooks/dex/usePositionInfo.ts +27 -62
  112. package/src/hooks/dex/useSupplyLiquidity.ts +80 -75
  113. package/src/hooks/index.ts +12 -10
  114. package/src/hooks/migrate/index.ts +13 -4
  115. package/src/hooks/migrate/useMigrateBaln.ts +42 -0
  116. package/src/hooks/migrate/useMigrateIcxToSoda.ts +44 -0
  117. package/src/hooks/migrate/useMigratebnUSD.ts +47 -0
  118. package/src/hooks/migrate/useMigrationAllowance.ts +76 -0
  119. package/src/hooks/migrate/useMigrationApprove.ts +66 -0
  120. package/src/hooks/migrate/useRevertMigrateSodaToIcx.ts +39 -0
  121. package/src/hooks/mm/index.ts +14 -12
  122. package/src/hooks/mm/useAToken.ts +25 -41
  123. package/src/hooks/mm/useATokensBalances.ts +29 -60
  124. package/src/hooks/mm/useBorrow.ts +38 -56
  125. package/src/hooks/mm/useMMAllowance.ts +37 -73
  126. package/src/hooks/mm/useMMApprove.ts +36 -43
  127. package/src/hooks/mm/useRepay.ts +33 -53
  128. package/src/hooks/mm/useReservesData.ts +12 -38
  129. package/src/hooks/mm/useReservesHumanized.ts +12 -31
  130. package/src/hooks/mm/useReservesList.ts +11 -31
  131. package/src/hooks/mm/useReservesUsdFormat.ts +15 -35
  132. package/src/hooks/mm/useSupply.ts +45 -51
  133. package/src/hooks/mm/useUserFormattedSummary.ts +32 -84
  134. package/src/hooks/mm/useUserReservesData.ts +27 -77
  135. package/src/hooks/mm/useWithdraw.ts +38 -54
  136. package/src/hooks/partner/index.ts +6 -0
  137. package/src/hooks/partner/useApproveToken.ts +42 -0
  138. package/src/hooks/partner/useFeeClaimSwap.ts +38 -0
  139. package/src/hooks/partner/useFetchAssetsBalances.ts +37 -0
  140. package/src/hooks/partner/useGetAutoSwapPreferences.ts +37 -0
  141. package/src/hooks/partner/useIsTokenApproved.ts +39 -0
  142. package/src/hooks/partner/useSetSwapPreference.ts +50 -0
  143. package/src/hooks/provider/index.ts +1 -2
  144. package/src/hooks/provider/useHubProvider.ts +1 -1
  145. package/src/hooks/recovery/index.ts +2 -0
  146. package/src/hooks/recovery/useHubAssetBalances.ts +43 -0
  147. package/src/hooks/recovery/useWithdrawHubAsset.ts +48 -0
  148. package/src/hooks/shared/index.ts +10 -6
  149. package/src/hooks/shared/types.ts +77 -0
  150. package/src/hooks/shared/unwrapResult.ts +19 -0
  151. package/src/hooks/shared/useDeriveUserWalletAddress.ts +22 -40
  152. package/src/hooks/shared/useEstimateGas.ts +18 -15
  153. package/src/hooks/shared/useGetUserHubWalletAddress.ts +25 -26
  154. package/src/hooks/shared/useRequestTrustline.ts +28 -61
  155. package/src/hooks/shared/useSafeMutation.test.ts +43 -0
  156. package/src/hooks/shared/useSafeMutation.ts +68 -0
  157. package/src/hooks/shared/useSodaxContext.ts +1 -1
  158. package/src/hooks/shared/useStellarTrustlineCheck.ts +30 -64
  159. package/src/hooks/shared/useXBalances.test.ts +113 -0
  160. package/src/hooks/shared/useXBalances.ts +61 -0
  161. package/src/hooks/staking/index.ts +18 -18
  162. package/src/hooks/staking/useCancelUnstake.ts +30 -41
  163. package/src/hooks/staking/useClaim.ts +27 -36
  164. package/src/hooks/staking/useConvertedAssets.ts +24 -34
  165. package/src/hooks/staking/useInstantUnstake.ts +33 -40
  166. package/src/hooks/staking/useInstantUnstakeAllowance.ts +37 -45
  167. package/src/hooks/staking/useInstantUnstakeApprove.ts +42 -42
  168. package/src/hooks/staking/useInstantUnstakeRatio.ts +24 -41
  169. package/src/hooks/staking/useStake.ts +32 -37
  170. package/src/hooks/staking/useStakeAllowance.ts +30 -43
  171. package/src/hooks/staking/useStakeApprove.ts +40 -40
  172. package/src/hooks/staking/useStakeRatio.ts +24 -40
  173. package/src/hooks/staking/useStakingConfig.ts +14 -27
  174. package/src/hooks/staking/useStakingInfo.ts +30 -38
  175. package/src/hooks/staking/useUnstake.ts +29 -43
  176. package/src/hooks/staking/useUnstakeAllowance.ts +37 -44
  177. package/src/hooks/staking/useUnstakeApprove.ts +40 -43
  178. package/src/hooks/staking/useUnstakingInfo.ts +29 -41
  179. package/src/hooks/staking/useUnstakingInfoWithPenalty.ts +31 -47
  180. package/src/hooks/swap/index.ts +8 -8
  181. package/src/hooks/swap/useCancelLimitOrder.ts +24 -41
  182. package/src/hooks/swap/useCancelSwap.ts +24 -33
  183. package/src/hooks/swap/useCreateLimitOrder.ts +29 -62
  184. package/src/hooks/swap/useQuote.ts +17 -43
  185. package/src/hooks/swap/useStatus.ts +22 -29
  186. package/src/hooks/swap/useSwap.ts +31 -49
  187. package/src/hooks/swap/useSwapAllowance.ts +38 -35
  188. package/src/hooks/swap/useSwapApprove.ts +48 -57
  189. package/src/index.ts +5 -3
  190. package/src/providers/SodaxProvider.tsx +17 -11
  191. package/src/providers/createSodaxQueryClient.ts +96 -0
  192. package/src/providers/index.ts +2 -1
  193. package/src/utils/dex-utils.ts +27 -5
  194. package/src/utils/index.ts +1 -1
  195. package/dist/index.d.mts +0 -2581
  196. package/dist/index.js +0 -2574
  197. package/dist/index.js.map +0 -1
  198. package/src/hooks/migrate/types.ts +0 -15
  199. package/src/hooks/migrate/useMigrate.tsx +0 -110
  200. package/src/hooks/migrate/useMigrationAllowance.tsx +0 -79
  201. package/src/hooks/migrate/useMigrationApprove.tsx +0 -129
  202. package/src/hooks/provider/useSpokeProvider.ts +0 -172
package/dist/index.mjs CHANGED
@@ -1,10 +1,37 @@
1
- import React, { createContext, useContext, useState, useCallback, useMemo, useRef, useEffect } from 'react';
2
- import { SpokeService, deriveUserWalletAddress, STELLAR_MAINNET_CHAIN_ID, StellarSpokeProvider, StellarSpokeService, HubService, spokeChainConfig, BitcoinSpokeProvider, SONIC_MAINNET_CHAIN_ID, SonicSpokeProvider, EvmSpokeProvider, SuiSpokeProvider, IconSpokeProvider, InjectiveSpokeProvider, SolanaSpokeProvider, NearSpokeProvider, StacksSpokeProvider, BitcoinSpokeService, normalizePsbtToBase64, isLegacybnUSDToken, ClService, Sodax } from '@sodax/sdk';
3
- import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
4
- import { isAddress, parseUnits } from 'viem';
5
- import { ICON_MAINNET_CHAIN_ID } from '@sodax/types';
1
+ import { useMutation, useQuery, useQueryClient, QueryClient, MutationCache } from '@tanstack/react-query';
2
+ import { createContext, useCallback, useContext, useState, useRef, useEffect, useMemo } from 'react';
3
+ import { ChainKeys, RadfiApiError, normalizePsbtToBase64, ClService, Sodax } from '@sodax/sdk';
4
+ export * from '@sodax/sdk';
5
+ import { isAddress, erc20Abi, parseUnits } from 'viem';
6
+ import { jsx } from 'react/jsx-runtime';
6
7
 
7
- // src/contexts/index.ts
8
+ // src/hooks/shared/unwrapResult.ts
9
+ function unwrapResult(result) {
10
+ if (!result.ok) {
11
+ if (result.error instanceof Error) throw result.error;
12
+ const e = result.error;
13
+ const msg = e?.detail?.code ?? e?.detail?.message ?? e?.message ?? "SDK call failed";
14
+ throw new Error(msg, { cause: result.error });
15
+ }
16
+ return result.value;
17
+ }
18
+ async function toResult(promise) {
19
+ try {
20
+ const value = await promise;
21
+ return { ok: true, value };
22
+ } catch (error) {
23
+ return { ok: false, error };
24
+ }
25
+ }
26
+ function useSafeMutation(options) {
27
+ const mutation = useMutation(options);
28
+ const { mutateAsync } = mutation;
29
+ const mutateAsyncSafe = useCallback(
30
+ (vars, opts) => toResult(mutateAsync(vars, opts)),
31
+ [mutateAsync]
32
+ );
33
+ return { ...mutation, mutateAsyncSafe };
34
+ }
8
35
  var SodaxContext = createContext(null);
9
36
  var useSodaxContext = () => {
10
37
  const context = useContext(SodaxContext);
@@ -13,58 +40,61 @@ var useSodaxContext = () => {
13
40
  }
14
41
  return context;
15
42
  };
16
- function useEstimateGas(spokeProvider) {
17
- return useMutation({
18
- mutationFn: async (rawTx) => {
19
- if (!spokeProvider) {
20
- throw new Error("spokeProvider is not found");
21
- }
22
- const response = await SpokeService.estimateGas(rawTx, spokeProvider);
23
- return response;
24
- }
43
+
44
+ // src/hooks/shared/useEstimateGas.ts
45
+ function useEstimateGas({
46
+ mutationOptions
47
+ } = {}) {
48
+ const { sodax } = useSodaxContext();
49
+ return useSafeMutation({
50
+ mutationKey: ["shared", "estimateGas"],
51
+ ...mutationOptions,
52
+ mutationFn: async (params) => unwrapResult(await sodax.spoke.estimateGas(params))
25
53
  });
26
54
  }
27
- function useDeriveUserWalletAddress(spokeChainId, spokeAddress) {
55
+ function useDeriveUserWalletAddress({
56
+ params,
57
+ queryOptions
58
+ } = {}) {
28
59
  const { sodax } = useSodaxContext();
60
+ const spokeChainId = params?.spokeChainId;
61
+ const spokeAddress = params?.spokeAddress;
29
62
  return useQuery({
30
- queryKey: ["deriveUserWalletAddress", spokeChainId, spokeAddress],
63
+ queryKey: ["shared", "deriveUserWalletAddress", spokeChainId, spokeAddress],
31
64
  queryFn: async () => {
32
65
  if (!spokeChainId || !spokeAddress) {
33
66
  throw new Error("Spoke chain id and address are required");
34
67
  }
35
- spokeChainId = typeof spokeChainId === "object" ? spokeChainId.chainConfig.chain.id : spokeChainId;
36
- return await deriveUserWalletAddress(sodax.hubProvider, spokeChainId, spokeAddress);
68
+ return await sodax.hubProvider.getUserHubWalletAddress(spokeAddress, spokeChainId);
37
69
  },
38
70
  enabled: !!spokeChainId && !!spokeAddress,
39
- refetchInterval: false
40
- // This is a deterministic operation, no need to refetch
71
+ refetchInterval: false,
72
+ ...queryOptions
41
73
  });
42
74
  }
43
- function useStellarTrustlineCheck(token, amount, spokeProvider, chainId) {
75
+ function useStellarTrustlineCheck({
76
+ params,
77
+ queryOptions
78
+ } = {}) {
79
+ const { sodax } = useSodaxContext();
80
+ const token = params?.token;
81
+ const amount = params?.amount;
82
+ const chainId = params?.chainId;
83
+ const walletProvider = params?.walletProvider;
44
84
  return useQuery({
45
- queryKey: ["stellar-trustline-check", token],
85
+ queryKey: ["shared", "stellarTrustlineCheck", token],
46
86
  queryFn: async () => {
47
- if (chainId !== STELLAR_MAINNET_CHAIN_ID) {
48
- return true;
49
- }
50
- if (!spokeProvider || !token || !amount || !(spokeProvider instanceof StellarSpokeProvider)) {
51
- console.error(
52
- "Spoke provider, token or amount not found. Details: spokeProvider:",
53
- spokeProvider,
54
- "token:",
55
- token,
56
- "amount:",
57
- amount
58
- );
59
- return false;
60
- }
61
- const response = await StellarSpokeService.hasSufficientTrustline(token, amount, spokeProvider);
62
- return response;
87
+ if (chainId !== ChainKeys.STELLAR_MAINNET) return true;
88
+ if (!walletProvider || !token || !amount) return false;
89
+ const walletAddress = await walletProvider.getWalletAddress();
90
+ return sodax.spoke.stellar.hasSufficientTrustline(token, amount, walletAddress);
63
91
  },
64
- enabled: !!spokeProvider && !!token && !!amount
92
+ enabled: !!walletProvider && !!token && !!amount,
93
+ ...queryOptions
65
94
  });
66
95
  }
67
96
  function useRequestTrustline(token) {
97
+ const { sodax } = useSodaxContext();
68
98
  const queryClient = useQueryClient();
69
99
  const [isLoading, setIsLoading] = useState(false);
70
100
  const [isRequested, setIsRequested] = useState(false);
@@ -74,20 +104,29 @@ function useRequestTrustline(token) {
74
104
  async ({
75
105
  token: token2,
76
106
  amount,
77
- spokeProvider
107
+ srcChainKey,
108
+ walletProvider
78
109
  }) => {
79
- if (!spokeProvider || !token2 || !amount || !(spokeProvider instanceof StellarSpokeProvider)) {
80
- const error2 = new Error("Spoke provider, token or amount not found");
110
+ if (!token2 || !amount) {
111
+ const error2 = new Error("Token and amount are required");
81
112
  setError(error2);
82
113
  throw error2;
83
114
  }
84
115
  setIsLoading(true);
85
116
  setError(null);
86
117
  try {
87
- const result = await StellarSpokeService.requestTrustline(token2, amount, spokeProvider);
118
+ const srcAddress = await walletProvider.getWalletAddress();
119
+ const result = await sodax.spoke.stellar.requestTrustline({
120
+ raw: false,
121
+ srcChainKey,
122
+ srcAddress,
123
+ token: token2,
124
+ amount,
125
+ walletProvider
126
+ });
88
127
  setData(result);
89
128
  setIsRequested(true);
90
- queryClient.invalidateQueries({ queryKey: ["stellar-trustline-check", token2] });
129
+ queryClient.invalidateQueries({ queryKey: ["shared", "stellarTrustlineCheck", token2] });
91
130
  return result;
92
131
  } catch (err) {
93
132
  const error2 = err instanceof Error ? err : new Error("Unknown error occurred");
@@ -97,30 +136,56 @@ function useRequestTrustline(token) {
97
136
  setIsLoading(false);
98
137
  }
99
138
  },
100
- [queryClient]
139
+ [queryClient, sodax]
101
140
  );
102
- return {
103
- requestTrustline,
104
- isLoading,
105
- isRequested,
106
- error,
107
- data
108
- };
141
+ return { requestTrustline, isLoading, isRequested, error, data };
109
142
  }
110
- function useGetUserHubWalletAddress(spokeChainId, spokeAddress) {
143
+ function useGetUserHubWalletAddress({
144
+ params,
145
+ queryOptions
146
+ } = {}) {
111
147
  const { sodax } = useSodaxContext();
148
+ const spokeChainId = params?.spokeChainId;
149
+ const spokeAddress = params?.spokeAddress;
112
150
  return useQuery({
113
- queryKey: ["getUserHubWalletAddress", spokeChainId, spokeAddress],
151
+ queryKey: ["shared", "userHubWalletAddress", spokeChainId, spokeAddress],
114
152
  queryFn: async () => {
115
153
  if (!spokeChainId || !spokeAddress) {
116
154
  throw new Error("Spoke chain id and address are required");
117
155
  }
118
- spokeChainId = typeof spokeChainId === "object" ? spokeChainId.chainConfig.chain.id : spokeChainId;
119
- return await HubService.getUserHubWalletAddress(spokeAddress, spokeChainId, sodax.hubProvider);
156
+ return await sodax.hubProvider.getUserHubWalletAddress(spokeAddress, spokeChainId);
120
157
  },
121
158
  enabled: !!spokeChainId && !!spokeAddress,
122
- refetchInterval: false
123
- // This is a deterministic operation, no need to refetch
159
+ refetchInterval: false,
160
+ ...queryOptions
161
+ });
162
+ }
163
+ var REFETCH_INTERVAL_MS = 5e3;
164
+ function getXBalancesQueryOptions({ xService, xChainId, xTokens, address }) {
165
+ return {
166
+ // Pair symbol + address: readable in devtools, unique on-chain (symbol alone
167
+ // can collide — e.g. scam tokens copying legitimate ticker).
168
+ queryKey: ["shared", "xBalances", xChainId, xTokens.map((x) => [x.symbol, x.address]), address],
169
+ queryFn: async () => {
170
+ if (!xService) return {};
171
+ return xService.getBalances(address, xTokens);
172
+ },
173
+ enabled: !!xService && !!address && xTokens.length > 0,
174
+ refetchInterval: REFETCH_INTERVAL_MS
175
+ };
176
+ }
177
+ function useXBalances({
178
+ params,
179
+ queryOptions
180
+ } = {}) {
181
+ return useQuery({
182
+ ...getXBalancesQueryOptions({
183
+ xService: params?.xService,
184
+ xChainId: params?.xChainId,
185
+ xTokens: params?.xTokens ?? [],
186
+ address: params?.address
187
+ }),
188
+ ...queryOptions
124
189
  });
125
190
  }
126
191
 
@@ -129,100 +194,6 @@ function useHubProvider() {
129
194
  const { sodax } = useSodaxContext();
130
195
  return sodax.hubProvider;
131
196
  }
132
- function useSpokeProvider(spokeChainId, walletProvider) {
133
- const { rpcConfig } = useSodaxContext();
134
- const xChainType = spokeChainId ? spokeChainConfig[spokeChainId]?.chain.type : void 0;
135
- const spokeProvider = useMemo(() => {
136
- if (!walletProvider) return void 0;
137
- if (!spokeChainId) return void 0;
138
- if (!xChainType) return void 0;
139
- if (!rpcConfig) return void 0;
140
- if (xChainType === "BITCOIN") {
141
- const bitcoinConfig = spokeChainConfig[spokeChainId];
142
- const btcRpcOverride = rpcConfig[spokeChainId];
143
- return new BitcoinSpokeProvider(
144
- walletProvider,
145
- bitcoinConfig,
146
- {
147
- url: btcRpcOverride?.radfiApiUrl || bitcoinConfig.radfiApiUrl,
148
- apiKey: bitcoinConfig.radfiApiKey,
149
- umsUrl: btcRpcOverride?.radfiUmsUrl || bitcoinConfig.radfiUmsUrl
150
- },
151
- "TRADING",
152
- btcRpcOverride?.rpcUrl || bitcoinConfig.rpcUrl
153
- );
154
- }
155
- if (xChainType === "EVM") {
156
- if (spokeChainId === SONIC_MAINNET_CHAIN_ID) {
157
- return new SonicSpokeProvider(
158
- walletProvider,
159
- spokeChainConfig[spokeChainId]
160
- );
161
- }
162
- const evmRpcUrl = rpcConfig[spokeChainId];
163
- return new EvmSpokeProvider(
164
- walletProvider,
165
- spokeChainConfig[spokeChainId],
166
- typeof evmRpcUrl === "string" ? evmRpcUrl : void 0
167
- );
168
- }
169
- if (xChainType === "SUI") {
170
- return new SuiSpokeProvider(
171
- spokeChainConfig[spokeChainId],
172
- walletProvider
173
- );
174
- }
175
- if (xChainType === "ICON") {
176
- return new IconSpokeProvider(
177
- walletProvider,
178
- spokeChainConfig[spokeChainId]
179
- );
180
- }
181
- if (xChainType === "INJECTIVE") {
182
- return new InjectiveSpokeProvider(
183
- spokeChainConfig[spokeChainId],
184
- walletProvider
185
- );
186
- }
187
- if (xChainType === "STELLAR") {
188
- const stellarConfig = spokeChainConfig[spokeChainId];
189
- return new StellarSpokeProvider(
190
- walletProvider,
191
- stellarConfig,
192
- rpcConfig.stellar ? rpcConfig.stellar : {
193
- horizonRpcUrl: stellarConfig.horizonRpcUrl,
194
- sorobanRpcUrl: stellarConfig.sorobanRpcUrl
195
- }
196
- );
197
- }
198
- if (xChainType === "SOLANA") {
199
- return new SolanaSpokeProvider(
200
- walletProvider,
201
- rpcConfig.solana ? {
202
- ...spokeChainConfig[spokeChainId],
203
- rpcUrl: rpcConfig.solana
204
- } : spokeChainConfig[spokeChainId]
205
- );
206
- }
207
- if (xChainType === "NEAR") {
208
- return new NearSpokeProvider(
209
- walletProvider,
210
- spokeChainConfig[spokeChainId]
211
- );
212
- }
213
- if (xChainType === "STACKS") {
214
- return new StacksSpokeProvider(
215
- rpcConfig[spokeChainId] ? {
216
- ...spokeChainConfig[spokeChainId],
217
- rpcUrl: rpcConfig[spokeChainId]
218
- } : spokeChainConfig[spokeChainId],
219
- walletProvider
220
- );
221
- }
222
- return void 0;
223
- }, [spokeChainId, xChainType, walletProvider, rpcConfig]);
224
- return spokeProvider;
225
- }
226
197
  var SESSION_KEY = (address) => `radfi_session_${address}`;
227
198
  function saveRadfiSession(address, session) {
228
199
  try {
@@ -244,38 +215,41 @@ function clearRadfiSession(address) {
244
215
  } catch {
245
216
  }
246
217
  }
247
- function useRadfiAuth(spokeProvider) {
248
- return useMutation({
249
- mutationFn: async () => {
250
- if (!spokeProvider) {
251
- throw new Error("Bitcoin spoke provider not found");
252
- }
253
- const walletAddress = await spokeProvider.walletProvider.getWalletAddress();
218
+ function useRadfiAuth({
219
+ mutationOptions
220
+ } = {}) {
221
+ const { sodax } = useSodaxContext();
222
+ return useSafeMutation({
223
+ mutationKey: ["bitcoin", "radfiAuth"],
224
+ ...mutationOptions,
225
+ mutationFn: async ({ walletProvider }) => {
226
+ const radfi = sodax.spoke.bitcoin.radfi;
227
+ const walletAddress = await walletProvider.getWalletAddress();
254
228
  const existingSession = loadRadfiSession(walletAddress);
255
229
  const cachedPublicKey = existingSession?.publicKey;
256
230
  try {
257
- const { accessToken, refreshToken, tradingAddress, publicKey } = await spokeProvider.authenticateWithWallet(cachedPublicKey);
258
- const session = {
259
- accessToken,
260
- refreshToken,
261
- tradingAddress,
262
- publicKey
263
- };
264
- saveRadfiSession(walletAddress, session);
231
+ const { accessToken, refreshToken, tradingAddress, publicKey } = await radfi.authenticateWithWallet(
232
+ walletProvider,
233
+ cachedPublicKey
234
+ );
235
+ saveRadfiSession(walletAddress, { accessToken, refreshToken, tradingAddress, publicKey });
265
236
  return { accessToken, refreshToken, tradingAddress };
266
237
  } catch (err) {
267
- const isAlreadyRegistered = err instanceof Error && (err.message.includes("duplicatedPubKey") || err.message.includes("4008"));
238
+ const isAlreadyRegistered = err instanceof RadfiApiError && err.code === "4008";
268
239
  if (isAlreadyRegistered && existingSession?.refreshToken) {
269
240
  try {
270
- const refreshed = await spokeProvider.radfi.refreshAccessToken(existingSession.refreshToken);
271
- const session = {
241
+ const refreshed = await radfi.refreshAccessToken(existingSession.refreshToken);
242
+ radfi.setRadfiAccessToken(refreshed.accessToken, refreshed.refreshToken);
243
+ saveRadfiSession(walletAddress, {
272
244
  ...existingSession,
273
245
  accessToken: refreshed.accessToken,
274
246
  refreshToken: refreshed.refreshToken
247
+ });
248
+ return {
249
+ accessToken: refreshed.accessToken,
250
+ refreshToken: refreshed.refreshToken,
251
+ tradingAddress: existingSession.tradingAddress
275
252
  };
276
- spokeProvider.setRadfiAccessToken(refreshed.accessToken, refreshed.refreshToken);
277
- saveRadfiSession(walletAddress, session);
278
- return { accessToken: refreshed.accessToken, refreshToken: refreshed.refreshToken, tradingAddress: existingSession.tradingAddress };
279
253
  } catch {
280
254
  clearRadfiSession(walletAddress);
281
255
  }
@@ -289,85 +263,103 @@ function useRadfiAuth(spokeProvider) {
289
263
  });
290
264
  }
291
265
  var REFRESH_INTERVAL = 5 * 60 * 1e3;
292
- function useRadfiSession(spokeProvider) {
266
+ function useRadfiSession(walletProvider) {
267
+ const { sodax } = useSodaxContext();
293
268
  const [walletAddress, setWalletAddress] = useState();
294
269
  const [isAuthed, setIsAuthed] = useState(false);
295
270
  const [tradingAddress, setTradingAddress] = useState();
296
271
  const isRefreshingRef = useRef(false);
297
- const silentRefresh = useCallback(async (address) => {
298
- if (!spokeProvider || isRefreshingRef.current) return;
299
- isRefreshingRef.current = true;
300
- try {
301
- const session = loadRadfiSession(address);
302
- if (!session?.refreshToken) {
272
+ const silentRefresh = useCallback(
273
+ async (address) => {
274
+ if (!walletProvider || isRefreshingRef.current) return;
275
+ isRefreshingRef.current = true;
276
+ try {
277
+ const session = loadRadfiSession(address);
278
+ if (!session?.refreshToken) {
279
+ setIsAuthed(false);
280
+ return;
281
+ }
282
+ const radfi = sodax.spoke.bitcoin.radfi;
283
+ const { accessToken, refreshToken } = await radfi.refreshAccessToken(session.refreshToken);
284
+ const updated = { ...session, accessToken, refreshToken };
285
+ saveRadfiSession(address, updated);
286
+ radfi.setRadfiAccessToken(accessToken, refreshToken);
287
+ setIsAuthed(true);
288
+ setTradingAddress(updated.tradingAddress || void 0);
289
+ } catch {
290
+ clearRadfiSession(address);
291
+ sodax.spoke.bitcoin.radfi.setRadfiAccessToken("", "");
303
292
  setIsAuthed(false);
304
- return;
293
+ setTradingAddress(void 0);
294
+ } finally {
295
+ isRefreshingRef.current = false;
305
296
  }
306
- const { accessToken, refreshToken } = await spokeProvider.radfi.refreshAccessToken(session.refreshToken);
307
- const updated = {
308
- ...session,
309
- accessToken,
310
- refreshToken
311
- };
312
- saveRadfiSession(address, updated);
313
- spokeProvider.setRadfiAccessToken(accessToken, refreshToken);
314
- setIsAuthed(true);
315
- setTradingAddress(updated.tradingAddress || void 0);
316
- } catch {
317
- clearRadfiSession(address);
318
- spokeProvider.setRadfiAccessToken("", "");
319
- setIsAuthed(false);
320
- setTradingAddress(void 0);
321
- } finally {
322
- isRefreshingRef.current = false;
323
- }
324
- }, [spokeProvider]);
297
+ },
298
+ [walletProvider, sodax]
299
+ );
325
300
  useEffect(() => {
326
- if (!spokeProvider) return;
301
+ if (!walletProvider) return;
327
302
  setIsAuthed(false);
328
303
  setTradingAddress(void 0);
329
304
  setWalletAddress(void 0);
330
- spokeProvider.walletProvider.getWalletAddress().then((addr) => {
305
+ walletProvider.getWalletAddress().then((addr) => {
331
306
  setWalletAddress(addr);
332
307
  const session = loadRadfiSession(addr);
333
308
  if (!session?.refreshToken) return;
334
309
  silentRefresh(addr);
335
310
  }).catch(() => {
336
311
  });
337
- }, [spokeProvider, silentRefresh]);
312
+ }, [walletProvider, silentRefresh]);
338
313
  useEffect(() => {
339
- if (!walletAddress || !spokeProvider) return;
314
+ if (!walletAddress || !walletProvider) return;
340
315
  const id = setInterval(() => {
341
316
  silentRefresh(walletAddress);
342
317
  }, REFRESH_INTERVAL);
343
318
  return () => clearInterval(id);
344
- }, [walletAddress, spokeProvider, silentRefresh]);
345
- const { mutateAsync: loginMutate, isPending: isLoginPending } = useRadfiAuth(spokeProvider);
319
+ }, [walletAddress, walletProvider, silentRefresh]);
320
+ const { mutateAsyncSafe: loginMutateSafe, isPending: isLoginPending } = useRadfiAuth();
346
321
  const login = useCallback(async () => {
347
- const result = await loginMutate();
322
+ if (!walletProvider) {
323
+ return;
324
+ }
325
+ const result = await loginMutateSafe({ walletProvider });
326
+ if (!result.ok) {
327
+ return;
328
+ }
348
329
  setIsAuthed(true);
349
- setTradingAddress(result.tradingAddress || void 0);
350
- }, [loginMutate]);
330
+ setTradingAddress(result.value.tradingAddress || void 0);
331
+ }, [loginMutateSafe, walletProvider]);
351
332
  return { walletAddress, isAuthed, tradingAddress, login, isLoginPending };
352
333
  }
353
- function useFundTradingWallet(spokeProvider) {
334
+ function useFundTradingWallet({
335
+ mutationOptions
336
+ } = {}) {
337
+ const { sodax } = useSodaxContext();
354
338
  const queryClient = useQueryClient();
355
- return useMutation({
356
- mutationFn: async (amount) => {
357
- if (!spokeProvider) {
358
- throw new Error("Bitcoin spoke provider not found");
359
- }
360
- return BitcoinSpokeService.fundTradingWallet(amount, spokeProvider);
339
+ return useSafeMutation({
340
+ mutationKey: ["bitcoin", "fundTradingWallet"],
341
+ ...mutationOptions,
342
+ mutationFn: async ({ amount, walletProvider }) => {
343
+ const walletAddress = await walletProvider.getWalletAddress();
344
+ return sodax.spoke.bitcoin.fundTradingWallet(amount, walletAddress, walletProvider);
361
345
  },
362
- onSuccess: () => {
363
- queryClient.invalidateQueries({ queryKey: ["btc-balance"] });
364
- queryClient.invalidateQueries({ queryKey: ["xBalances"] });
346
+ onSuccess: async (data, vars, ctx) => {
347
+ queryClient.invalidateQueries({ queryKey: ["bitcoin", "balance"] });
348
+ queryClient.invalidateQueries({ queryKey: ["bitcoin", "tradingWalletBalance"] });
349
+ queryClient.invalidateQueries({ queryKey: ["shared", "xBalances", ChainKeys.BITCOIN_MAINNET] });
350
+ await mutationOptions?.onSuccess?.(data, vars, ctx);
365
351
  }
366
352
  });
367
353
  }
368
- function useBitcoinBalance(address, rpcUrl = "https://mempool.space/api") {
354
+ var DEFAULT_RPC_URL = "https://mempool.space/api";
355
+ function useBitcoinBalance({
356
+ params,
357
+ queryOptions
358
+ } = {}) {
359
+ const address = params?.address;
360
+ const rpcUrl = params?.rpcUrl ?? DEFAULT_RPC_URL;
369
361
  return useQuery({
370
- queryKey: ["btc-balance", address],
362
+ queryKey: ["bitcoin", "balance", address],
371
363
  queryFn: async () => {
372
364
  if (!address) return 0n;
373
365
  const response = await fetch(`${rpcUrl}/address/${address}/utxo`);
@@ -375,300 +367,310 @@ function useBitcoinBalance(address, rpcUrl = "https://mempool.space/api") {
375
367
  const utxos = await response.json();
376
368
  return BigInt(utxos.reduce((sum, utxo) => sum + utxo.value, 0));
377
369
  },
378
- enabled: !!address
370
+ enabled: !!address,
371
+ ...queryOptions
379
372
  });
380
373
  }
381
- function useTradingWalletBalance(spokeProvider, tradingAddress) {
374
+
375
+ // src/hooks/bitcoin/useTradingWallet.ts
376
+ function useTradingWallet(walletAddress) {
377
+ if (!walletAddress) return { tradingAddress: void 0 };
378
+ const session = loadRadfiSession(walletAddress);
379
+ return { tradingAddress: session?.tradingAddress || void 0 };
380
+ }
381
+ function useTradingWalletBalance({
382
+ params,
383
+ queryOptions
384
+ } = {}) {
385
+ const { sodax } = useSodaxContext();
386
+ const walletProvider = params?.walletProvider;
387
+ const tradingAddress = params?.tradingAddress;
382
388
  return useQuery({
383
- queryKey: ["trading-wallet-balance", tradingAddress],
389
+ queryKey: ["bitcoin", "tradingWalletBalance", tradingAddress],
384
390
  queryFn: () => {
385
- if (!spokeProvider || !tradingAddress) {
386
- throw new Error("spokeProvider and tradingAddress are required");
391
+ if (!walletProvider || !tradingAddress) {
392
+ throw new Error("walletProvider and tradingAddress are required");
387
393
  }
388
- return spokeProvider.radfi.getBalance(tradingAddress);
394
+ return sodax.spoke.bitcoin.radfi.getBalance(tradingAddress);
389
395
  },
390
- enabled: !!spokeProvider && !!tradingAddress
396
+ enabled: !!walletProvider && !!tradingAddress,
397
+ ...queryOptions
391
398
  });
392
399
  }
393
- function useExpiredUtxos(spokeProvider, tradingAddress) {
400
+ function useExpiredUtxos({
401
+ params,
402
+ queryOptions
403
+ } = {}) {
404
+ const { sodax } = useSodaxContext();
405
+ const walletProvider = params?.walletProvider;
406
+ const tradingAddress = params?.tradingAddress;
394
407
  return useQuery({
395
- queryKey: ["expired-utxos", tradingAddress],
408
+ queryKey: ["bitcoin", "expiredUtxos", tradingAddress],
396
409
  queryFn: async () => {
397
- if (!spokeProvider || !tradingAddress) {
398
- throw new Error("spokeProvider and tradingAddress are required");
410
+ if (!walletProvider || !tradingAddress) {
411
+ throw new Error("walletProvider and tradingAddress are required");
399
412
  }
400
- const result = await spokeProvider.radfi.getExpiredUtxos(tradingAddress);
413
+ const result = await sodax.spoke.bitcoin.radfi.getExpiredUtxos(tradingAddress);
401
414
  return result.data;
402
415
  },
403
- enabled: !!spokeProvider && !!tradingAddress,
404
- refetchInterval: 6e4
405
- // refetch every minute
416
+ enabled: !!walletProvider && !!tradingAddress,
417
+ refetchInterval: 6e4,
418
+ ...queryOptions
406
419
  });
407
420
  }
408
- function useRenewUtxos(spokeProvider) {
421
+ function useRenewUtxos({
422
+ mutationOptions
423
+ } = {}) {
424
+ const { sodax } = useSodaxContext();
409
425
  const queryClient = useQueryClient();
410
- return useMutation({
411
- mutationFn: async ({ txIdVouts }) => {
412
- if (!spokeProvider) {
413
- throw new Error("Bitcoin spoke provider not found");
414
- }
415
- const userAddress = await spokeProvider.walletProvider.getWalletAddress();
426
+ return useSafeMutation({
427
+ mutationKey: ["bitcoin", "renewUtxos"],
428
+ ...mutationOptions,
429
+ mutationFn: async ({ txIdVouts, walletProvider }) => {
430
+ const radfi = sodax.spoke.bitcoin.radfi;
431
+ const userAddress = await walletProvider.getWalletAddress();
416
432
  const session = loadRadfiSession(userAddress);
417
- const accessToken = session?.accessToken || spokeProvider.radfiAccessToken;
433
+ const accessToken = session?.accessToken || radfi.accessToken;
418
434
  if (!accessToken) {
419
435
  throw new Error("Radfi authentication required. Please login first.");
420
436
  }
421
- const buildResult = await spokeProvider.radfi.buildRenewUtxoTransaction(
422
- { userAddress, txIdVouts },
423
- accessToken
424
- );
425
- const signedTx = await spokeProvider.walletProvider.signTransaction(
426
- buildResult.base64Psbt,
427
- false
428
- );
437
+ const buildResult = await radfi.buildRenewUtxoTransaction({ userAddress, txIdVouts }, accessToken);
438
+ const signedTx = await walletProvider.signTransaction(buildResult.base64Psbt, false);
429
439
  const signedBase64Tx = normalizePsbtToBase64(signedTx);
430
- return spokeProvider.radfi.signAndBroadcastRenewUtxo(
431
- { userAddress, signedBase64Tx },
432
- accessToken
433
- );
440
+ return radfi.signAndBroadcastRenewUtxo({ userAddress, signedBase64Tx }, accessToken);
434
441
  },
435
- onSuccess: () => {
436
- queryClient.invalidateQueries({ queryKey: ["expired-utxos"] });
437
- queryClient.invalidateQueries({ queryKey: ["trading-wallet-balance"] });
442
+ onSuccess: async (data, vars, ctx) => {
443
+ queryClient.invalidateQueries({ queryKey: ["bitcoin", "expiredUtxos"] });
444
+ queryClient.invalidateQueries({ queryKey: ["bitcoin", "tradingWalletBalance"] });
445
+ await mutationOptions?.onSuccess?.(data, vars, ctx);
438
446
  }
439
447
  });
440
448
  }
441
- function useRadfiWithdraw(spokeProvider) {
449
+ function useRadfiWithdraw({
450
+ mutationOptions
451
+ } = {}) {
452
+ const { sodax } = useSodaxContext();
442
453
  const queryClient = useQueryClient();
443
- return useMutation({
444
- mutationFn: async ({ amount, tokenId, withdrawTo }) => {
445
- if (!spokeProvider) {
446
- throw new Error("Bitcoin spoke provider not found");
447
- }
448
- const userAddress = await spokeProvider.walletProvider.getWalletAddress();
454
+ return useSafeMutation({
455
+ mutationKey: ["bitcoin", "radfiWithdraw"],
456
+ ...mutationOptions,
457
+ mutationFn: async ({ amount, tokenId, withdrawTo, walletProvider }) => {
458
+ const radfi = sodax.spoke.bitcoin.radfi;
459
+ const userAddress = await walletProvider.getWalletAddress();
449
460
  const session = loadRadfiSession(userAddress);
450
- const accessToken = session?.accessToken || spokeProvider.radfiAccessToken;
461
+ const accessToken = session?.accessToken || radfi.accessToken;
451
462
  if (!accessToken) {
452
463
  throw new Error("Radfi authentication required. Please login first.");
453
464
  }
454
- const buildResult = await spokeProvider.radfi.withdrawToUser(
455
- { userAddress, amount, tokenId, withdrawTo },
456
- accessToken
457
- );
458
- const signedTx = await spokeProvider.walletProvider.signTransaction(
459
- buildResult.base64Psbt,
460
- false
461
- );
465
+ const buildResult = await radfi.withdrawToUser({ userAddress, amount, tokenId, withdrawTo }, accessToken);
466
+ const signedTx = await walletProvider.signTransaction(buildResult.base64Psbt, false);
462
467
  const signedBase64Tx = normalizePsbtToBase64(signedTx);
463
- const txId = await spokeProvider.radfi.signAndBroadcastWithdraw(
464
- { userAddress, signedBase64Tx },
465
- accessToken
466
- );
468
+ const txId = await radfi.signAndBroadcastWithdraw({ userAddress, signedBase64Tx }, accessToken);
467
469
  return { txId, fee: buildResult.fee.totalFee };
468
470
  },
469
- onSuccess: () => {
470
- queryClient.invalidateQueries({ queryKey: ["trading-wallet-balance"] });
471
- queryClient.invalidateQueries({ queryKey: ["btc-balance"] });
472
- queryClient.invalidateQueries({ queryKey: ["xBalances"] });
471
+ onSuccess: async (data, vars, ctx) => {
472
+ queryClient.invalidateQueries({ queryKey: ["bitcoin", "tradingWalletBalance"] });
473
+ queryClient.invalidateQueries({ queryKey: ["bitcoin", "balance"] });
474
+ queryClient.invalidateQueries({ queryKey: ["shared", "xBalances", ChainKeys.BITCOIN_MAINNET] });
475
+ await mutationOptions?.onSuccess?.(data, vars, ctx);
473
476
  }
474
477
  });
475
478
  }
476
- function useBorrow() {
479
+ function useBorrow({
480
+ mutationOptions
481
+ } = {}) {
477
482
  const { sodax } = useSodaxContext();
478
- return useMutation({
479
- mutationFn: async ({ params, spokeProvider }) => {
480
- if (!spokeProvider) {
481
- throw new Error("spokeProvider is not found");
482
- }
483
- const response = await sodax.moneyMarket.borrow(params, spokeProvider);
484
- if (!response.ok) {
485
- throw response.error;
483
+ const queryClient = useQueryClient();
484
+ return useSafeMutation({
485
+ mutationKey: ["mm", "borrow"],
486
+ ...mutationOptions,
487
+ mutationFn: async (vars) => unwrapResult(await sodax.moneyMarket.borrow({ ...vars, raw: false })),
488
+ onSuccess: async (data, vars, ctx) => {
489
+ const { params } = vars;
490
+ queryClient.invalidateQueries({ queryKey: ["mm", "userReservesData", params.srcChainKey, params.srcAddress] });
491
+ queryClient.invalidateQueries({
492
+ queryKey: ["mm", "userFormattedSummary", params.srcChainKey, params.srcAddress]
493
+ });
494
+ queryClient.invalidateQueries({ queryKey: ["mm", "aTokensBalances"] });
495
+ const balanceChains = /* @__PURE__ */ new Set([params.srcChainKey, params.dstChainKey ?? params.srcChainKey]);
496
+ for (const chainKey of balanceChains) {
497
+ queryClient.invalidateQueries({ queryKey: ["shared", "xBalances", chainKey] });
486
498
  }
487
- return response;
499
+ await mutationOptions?.onSuccess?.(data, vars, ctx);
488
500
  }
489
501
  });
490
502
  }
491
- function useRepay() {
503
+ function useRepay({
504
+ mutationOptions
505
+ } = {}) {
492
506
  const { sodax } = useSodaxContext();
493
- return useMutation({
494
- mutationFn: async ({ params, spokeProvider }) => {
495
- if (!spokeProvider) {
496
- throw new Error("spokeProvider is not found");
497
- }
498
- const response = await sodax.moneyMarket.repay(params, spokeProvider);
499
- if (!response.ok) {
500
- throw response.error;
501
- }
502
- return response;
507
+ const queryClient = useQueryClient();
508
+ return useSafeMutation({
509
+ mutationKey: ["mm", "repay"],
510
+ ...mutationOptions,
511
+ mutationFn: async (vars) => unwrapResult(await sodax.moneyMarket.repay({ ...vars, raw: false })),
512
+ onSuccess: async (data, vars, ctx) => {
513
+ const { params } = vars;
514
+ queryClient.invalidateQueries({ queryKey: ["mm", "userReservesData", params.srcChainKey, params.srcAddress] });
515
+ queryClient.invalidateQueries({
516
+ queryKey: ["mm", "userFormattedSummary", params.srcChainKey, params.srcAddress]
517
+ });
518
+ queryClient.invalidateQueries({ queryKey: ["mm", "aTokensBalances"] });
519
+ queryClient.invalidateQueries({ queryKey: ["mm", "allowance", params.srcChainKey, params.token, params.action] });
520
+ queryClient.invalidateQueries({ queryKey: ["shared", "xBalances", params.srcChainKey] });
521
+ await mutationOptions?.onSuccess?.(data, vars, ctx);
503
522
  }
504
523
  });
505
524
  }
506
- function useSupply() {
525
+ function useSupply({
526
+ mutationOptions
527
+ } = {}) {
507
528
  const { sodax } = useSodaxContext();
508
- return useMutation({
509
- mutationFn: async ({ params, spokeProvider }) => {
510
- if (!spokeProvider) {
511
- throw new Error("spokeProvider is not found");
512
- }
513
- const response = await sodax.moneyMarket.supply(params, spokeProvider);
514
- if (!response.ok) {
515
- throw response.error;
516
- }
517
- return response;
529
+ const queryClient = useQueryClient();
530
+ return useSafeMutation({
531
+ mutationKey: ["mm", "supply"],
532
+ ...mutationOptions,
533
+ mutationFn: async (vars) => unwrapResult(await sodax.moneyMarket.supply({ ...vars, raw: false })),
534
+ onSuccess: async (data, vars, ctx) => {
535
+ const { params } = vars;
536
+ queryClient.invalidateQueries({ queryKey: ["mm", "userReservesData", params.srcChainKey, params.srcAddress] });
537
+ queryClient.invalidateQueries({
538
+ queryKey: ["mm", "userFormattedSummary", params.srcChainKey, params.srcAddress]
539
+ });
540
+ queryClient.invalidateQueries({ queryKey: ["mm", "aTokensBalances"] });
541
+ queryClient.invalidateQueries({ queryKey: ["mm", "allowance", params.srcChainKey, params.token, params.action] });
542
+ queryClient.invalidateQueries({ queryKey: ["shared", "xBalances", params.srcChainKey] });
543
+ await mutationOptions?.onSuccess?.(data, vars, ctx);
518
544
  }
519
545
  });
520
546
  }
521
- function useWithdraw() {
547
+ function useWithdraw({
548
+ mutationOptions
549
+ } = {}) {
522
550
  const { sodax } = useSodaxContext();
523
- return useMutation({
524
- mutationFn: async ({ params, spokeProvider }) => {
525
- if (!spokeProvider) {
526
- throw new Error("spokeProvider is not found");
527
- }
528
- const response = await sodax.moneyMarket.withdraw(params, spokeProvider);
529
- if (!response.ok) {
530
- throw response.error;
551
+ const queryClient = useQueryClient();
552
+ return useSafeMutation({
553
+ mutationKey: ["mm", "withdraw"],
554
+ ...mutationOptions,
555
+ mutationFn: async (vars) => unwrapResult(await sodax.moneyMarket.withdraw({ ...vars, raw: false })),
556
+ onSuccess: async (data, vars, ctx) => {
557
+ const { params } = vars;
558
+ queryClient.invalidateQueries({ queryKey: ["mm", "userReservesData", params.srcChainKey, params.srcAddress] });
559
+ queryClient.invalidateQueries({
560
+ queryKey: ["mm", "userFormattedSummary", params.srcChainKey, params.srcAddress]
561
+ });
562
+ queryClient.invalidateQueries({ queryKey: ["mm", "aTokensBalances"] });
563
+ const balanceChains = /* @__PURE__ */ new Set([params.srcChainKey, params.dstChainKey ?? params.srcChainKey]);
564
+ for (const chainKey of balanceChains) {
565
+ queryClient.invalidateQueries({ queryKey: ["shared", "xBalances", chainKey] });
531
566
  }
532
- return response;
567
+ await mutationOptions?.onSuccess?.(data, vars, ctx);
533
568
  }
534
569
  });
535
570
  }
536
- function isLegacyParams(params) {
537
- return "spokeProvider" in params || "address" in params;
538
- }
539
- function resolveParams(params) {
540
- if (isLegacyParams(params)) {
541
- return {
542
- spokeChainId: params.spokeProvider?.chainConfig.chain.id,
543
- userAddress: params.address
544
- };
545
- }
546
- return { spokeChainId: params.spokeChainId, userAddress: params.userAddress };
547
- }
548
- function useUserReservesData(params) {
571
+ function useUserReservesData({
572
+ params,
573
+ queryOptions
574
+ } = {}) {
549
575
  const { sodax } = useSodaxContext();
550
- const resolved = params ? resolveParams(params) : { spokeChainId: void 0, userAddress: void 0 };
551
- const defaultQueryOptions = {
552
- queryKey: ["mm", "userReservesData", resolved.spokeChainId, resolved.userAddress],
553
- enabled: !!resolved.spokeChainId && !!resolved.userAddress,
554
- refetchInterval: 5e3
555
- };
556
- const queryOptions = {
557
- ...defaultQueryOptions,
558
- ...params?.queryOptions
559
- // override default query options if provided
560
- };
576
+ const spokeChainKey = params?.spokeChainKey;
577
+ const userAddress = params?.userAddress;
561
578
  return useQuery({
562
- ...queryOptions,
579
+ queryKey: ["mm", "userReservesData", spokeChainKey, userAddress],
563
580
  queryFn: async () => {
564
- if (!resolved.spokeChainId || !resolved.userAddress) {
565
- throw new Error("spokeChainId or userAddress is not defined");
581
+ if (!spokeChainKey || !userAddress) {
582
+ throw new Error("spokeChainKey and userAddress are required");
566
583
  }
567
- return await sodax.moneyMarket.data.getUserReservesData(resolved.spokeChainId, resolved.userAddress);
568
- }
584
+ return sodax.moneyMarket.data.getUserReservesData(spokeChainKey, userAddress);
585
+ },
586
+ enabled: !!spokeChainKey && !!userAddress,
587
+ refetchInterval: 5e3,
588
+ ...queryOptions
569
589
  });
570
590
  }
571
- function useReservesData(params) {
572
- const defaultQueryOptions = {
591
+ function useReservesData({
592
+ queryOptions
593
+ } = {}) {
594
+ const { sodax } = useSodaxContext();
595
+ return useQuery({
573
596
  queryKey: ["mm", "reservesData"],
574
- refetchInterval: 5e3
575
- };
576
- const queryOptions = {
577
- ...defaultQueryOptions,
578
- ...params?.queryOptions
579
- // override default query options if provided
580
- };
597
+ queryFn: async () => sodax.moneyMarket.data.getReservesData(),
598
+ refetchInterval: 5e3,
599
+ ...queryOptions
600
+ });
601
+ }
602
+ function useReservesHumanized({
603
+ queryOptions
604
+ } = {}) {
581
605
  const { sodax } = useSodaxContext();
582
606
  return useQuery({
583
- ...queryOptions,
584
- queryFn: async () => {
585
- return await sodax.moneyMarket.data.getReservesData();
586
- }
607
+ queryKey: ["mm", "reservesHumanized"],
608
+ queryFn: async () => sodax.moneyMarket.data.getReservesHumanized(),
609
+ refetchInterval: 5e3,
610
+ ...queryOptions
611
+ });
612
+ }
613
+ function useReservesList({
614
+ queryOptions
615
+ } = {}) {
616
+ const { sodax } = useSodaxContext();
617
+ return useQuery({
618
+ queryKey: ["mm", "reservesList"],
619
+ queryFn: async () => sodax.moneyMarket.data.getReservesList(),
620
+ ...queryOptions
587
621
  });
588
622
  }
589
623
  function useMMAllowance({
590
624
  params,
591
- spokeProvider,
592
625
  queryOptions
593
- }) {
626
+ } = {}) {
594
627
  const { sodax } = useSodaxContext();
595
- const defaultQueryOptions = {
596
- queryKey: ["mm", "allowance", params?.token, params?.action],
597
- /**
598
- * IMPORTANT: Skip allowance checks for 'borrow' and 'withdraw' actions.
599
- *
600
- * Reason: According to the SDK's MoneyMarketService.isAllowanceValid() implementation,
601
- * borrow and withdraw actions do NOT require ERC-20 token approval. The SDK's
602
- * isAllowanceValid() method always returns `true` for these actions without making
603
- * any on-chain allowance checks.
604
- *
605
- * This optimization prevents unnecessary RPC calls and avoids showing confusing states for actions that don't actually need approval.
606
- *
607
- * Only 'supply' and 'repay' actions require token approval and should trigger allowance checks.
608
- */
609
- enabled: !!spokeProvider && !!params && params.action !== "borrow" && params.action !== "withdraw",
610
- refetchInterval: 5e3,
611
- gcTime: 0
612
- // Don't cache failed queries
613
- };
614
- queryOptions = {
615
- ...defaultQueryOptions,
616
- ...queryOptions
617
- // override default query options if provided
618
- };
628
+ const payload = params?.payload;
619
629
  return useQuery({
620
- ...queryOptions,
630
+ queryKey: ["mm", "allowance", payload?.srcChainKey, payload?.token, payload?.action],
621
631
  queryFn: async () => {
622
- if (!spokeProvider) throw new Error("Spoke provider is required");
623
- if (!params) throw new Error("Params are required");
624
- if (params.action === "borrow" || params.action === "withdraw") {
625
- return true;
632
+ if (!payload) {
633
+ throw new Error("Params are required");
626
634
  }
627
- const allowance = await sodax.moneyMarket.isAllowanceValid(params, spokeProvider);
628
- if (!allowance.ok) {
629
- throw allowance.error;
635
+ if (payload.action === "borrow" || payload.action === "withdraw") {
636
+ return true;
630
637
  }
631
- return allowance.value;
632
- }
638
+ const result = await sodax.moneyMarket.isAllowanceValid({ params: payload });
639
+ if (!result.ok) throw result.error;
640
+ return result.value;
641
+ },
642
+ enabled: !!payload && payload.action !== "borrow" && payload.action !== "withdraw",
643
+ refetchInterval: 5e3,
644
+ gcTime: 0,
645
+ ...queryOptions
633
646
  });
634
647
  }
635
- function useMMApprove() {
648
+ function useMMApprove({
649
+ mutationOptions
650
+ } = {}) {
636
651
  const { sodax } = useSodaxContext();
637
652
  const queryClient = useQueryClient();
638
- return useMutation({
639
- mutationFn: async ({ params, spokeProvider }) => {
640
- if (!spokeProvider) {
641
- throw new Error("Spoke provider not found");
642
- }
643
- const allowance = await sodax.moneyMarket.approve(params, spokeProvider, false);
644
- if (!allowance.ok) {
645
- throw allowance.error;
646
- }
647
- return allowance.value;
648
- },
649
- onSuccess: (_, { params, spokeProvider }) => {
653
+ return useSafeMutation({
654
+ mutationKey: ["mm", "approve"],
655
+ ...mutationOptions,
656
+ mutationFn: async (vars) => unwrapResult(await sodax.moneyMarket.approve({ ...vars, raw: false })),
657
+ onSuccess: async (data, vars, ctx) => {
658
+ const { params } = vars;
650
659
  queryClient.invalidateQueries({
651
- queryKey: ["mm", "allowance", spokeProvider?.chainConfig.chain.id, params.token, params.action]
660
+ queryKey: ["mm", "allowance", params.srcChainKey, params.token, params.action]
652
661
  });
662
+ await mutationOptions?.onSuccess?.(data, vars, ctx);
653
663
  }
654
664
  });
655
665
  }
656
- function useAToken({ aToken, queryOptions }) {
666
+ function useAToken({ params, queryOptions } = {}) {
657
667
  const { sodax } = useSodaxContext();
658
- const defaultQueryOptions = {
659
- queryKey: ["mm", "aToken", aToken],
660
- enabled: !!aToken
661
- };
662
- queryOptions = {
663
- ...defaultQueryOptions,
664
- ...queryOptions
665
- // override default query options if provided
666
- };
668
+ const aToken = params?.aToken;
667
669
  return useQuery({
668
- ...queryOptions,
670
+ queryKey: ["mm", "aToken", aToken],
669
671
  queryFn: async () => {
670
672
  if (!aToken) {
671
- throw new Error("aToken address or hub provider is not defined");
673
+ throw new Error("aToken address is required");
672
674
  }
673
675
  if (!isAddress(aToken)) {
674
676
  throw new Error("aToken address is not a valid address");
@@ -676,121 +678,92 @@ function useAToken({ aToken, queryOptions }) {
676
678
  const aTokenData = await sodax.moneyMarket.data.getATokenData(aToken);
677
679
  return {
678
680
  ...aTokenData,
679
- xChainId: sodax.hubProvider.chainConfig.chain.id
681
+ chainKey: sodax.hubProvider.chainConfig.chain.key
680
682
  };
681
- }
683
+ },
684
+ enabled: !!aToken && isAddress(aToken ?? ""),
685
+ ...queryOptions
682
686
  });
683
687
  }
684
688
  function useATokensBalances({
685
- aTokens,
686
- spokeProvider,
687
- userAddress,
689
+ params,
688
690
  queryOptions
689
- }) {
691
+ } = {}) {
690
692
  const { sodax } = useSodaxContext();
691
- const defaultQueryOptions = {
692
- queryKey: ["mm", "aTokensBalances", aTokens, spokeProvider?.chainConfig.chain.id, userAddress],
693
- enabled: aTokens.length > 0 && aTokens.every((token) => isAddress(token)) && !!spokeProvider && !!userAddress
694
- };
695
- queryOptions = {
696
- ...defaultQueryOptions,
697
- ...queryOptions
698
- // override default query options if provided
699
- };
693
+ const aTokens = params?.aTokens ?? [];
694
+ const spokeChainKey = params?.spokeChainKey;
695
+ const userAddress = params?.userAddress;
700
696
  return useQuery({
701
- ...queryOptions,
697
+ queryKey: ["mm", "aTokensBalances", aTokens, spokeChainKey, userAddress],
702
698
  queryFn: async () => {
703
699
  if (aTokens.length === 0) {
704
700
  return /* @__PURE__ */ new Map();
705
701
  }
706
- if (!spokeProvider || !userAddress) {
707
- throw new Error("Spoke provider and user address are required");
702
+ if (!spokeChainKey || !userAddress) {
703
+ throw new Error("spokeChainKey and userAddress are required");
708
704
  }
709
705
  for (const aToken of aTokens) {
710
706
  if (!isAddress(aToken)) {
711
707
  throw new Error(`Invalid aToken address: ${aToken}`);
712
708
  }
713
709
  }
714
- const hubWalletAddress = await HubService.getUserHubWalletAddress(userAddress, spokeProvider.chainConfig.chain.id, sodax.hubProvider);
715
- return await sodax.moneyMarket.data.getATokensBalances(aTokens, hubWalletAddress);
716
- }
710
+ const hubWalletAddress = await sodax.hubProvider.getUserHubWalletAddress(userAddress, spokeChainKey);
711
+ return sodax.moneyMarket.data.getATokensBalances(aTokens, hubWalletAddress);
712
+ },
713
+ enabled: aTokens.length > 0 && !!spokeChainKey && !!userAddress,
714
+ ...queryOptions
717
715
  });
718
716
  }
719
- function useReservesUsdFormat(params) {
717
+ function useReservesUsdFormat({
718
+ queryOptions
719
+ } = {}) {
720
720
  const { sodax } = useSodaxContext();
721
- const defaultQueryOptions = { queryKey: ["mm", "reservesUsdFormat"] };
722
- const queryOptions = {
723
- ...defaultQueryOptions,
724
- ...params?.queryOptions
725
- // override default query options if provided
726
- };
727
721
  return useQuery({
728
- ...queryOptions,
722
+ queryKey: ["mm", "reservesUsdFormat"],
729
723
  queryFn: async () => {
730
724
  const reserves = await sodax.moneyMarket.data.getReservesHumanized();
731
725
  return sodax.moneyMarket.data.formatReservesUSD(sodax.moneyMarket.data.buildReserveDataWithPrice(reserves));
732
- }
726
+ },
727
+ ...queryOptions
733
728
  });
734
729
  }
735
- function isLegacyParams2(params) {
736
- return "spokeProvider" in params || "address" in params;
737
- }
738
- function resolveParams2(params) {
739
- if (isLegacyParams2(params)) {
740
- return {
741
- spokeChainId: params.spokeProvider?.chainConfig.chain.id,
742
- userAddress: params.address
743
- };
744
- }
745
- return { spokeChainId: params.spokeChainId, userAddress: params.userAddress };
746
- }
747
- function useUserFormattedSummary(params) {
730
+ function useUserFormattedSummary({
731
+ params,
732
+ queryOptions
733
+ } = {}) {
748
734
  const { sodax } = useSodaxContext();
749
- const resolved = params ? resolveParams2(params) : { spokeChainId: void 0, userAddress: void 0 };
750
- const defaultQueryOptions = {
751
- queryKey: ["mm", "userFormattedSummary", resolved.spokeChainId, resolved.userAddress],
752
- enabled: !!resolved.spokeChainId && !!resolved.userAddress,
753
- refetchInterval: 5e3
754
- };
755
- const queryOptions = {
756
- ...defaultQueryOptions,
757
- ...params?.queryOptions
758
- // override default query options if provided
759
- };
735
+ const spokeChainKey = params?.spokeChainKey;
736
+ const userAddress = params?.userAddress;
760
737
  return useQuery({
761
- ...queryOptions,
738
+ queryKey: ["mm", "userFormattedSummary", spokeChainKey, userAddress],
762
739
  queryFn: async () => {
763
- if (!resolved.spokeChainId || !resolved.userAddress) {
764
- throw new Error("spokeChainId or userAddress is not defined");
740
+ if (!spokeChainKey || !userAddress) {
741
+ throw new Error("spokeChainKey and userAddress are required");
765
742
  }
766
- const reserves = await sodax.moneyMarket.data.getReservesHumanized();
743
+ const [reserves, userReserves] = await Promise.all([
744
+ sodax.moneyMarket.data.getReservesHumanized(),
745
+ sodax.moneyMarket.data.getUserReservesHumanized(spokeChainKey, userAddress)
746
+ ]);
767
747
  const formattedReserves = sodax.moneyMarket.data.formatReservesUSD(
768
748
  sodax.moneyMarket.data.buildReserveDataWithPrice(reserves)
769
749
  );
770
- const userReserves = await sodax.moneyMarket.data.getUserReservesHumanized(
771
- resolved.spokeChainId,
772
- resolved.userAddress
773
- );
774
750
  return sodax.moneyMarket.data.formatUserSummary(
775
751
  sodax.moneyMarket.data.buildUserSummaryRequest(reserves, formattedReserves, userReserves)
776
752
  );
777
- }
753
+ },
754
+ enabled: !!spokeChainKey && !!userAddress,
755
+ refetchInterval: 5e3,
756
+ ...queryOptions
778
757
  });
779
758
  }
780
- var useQuote = (payload) => {
759
+ var useQuote = ({
760
+ params,
761
+ queryOptions
762
+ } = {}) => {
781
763
  const { sodax } = useSodaxContext();
782
- const queryKey = useMemo(() => {
783
- if (!payload) return ["quote", void 0];
784
- return [
785
- "quote",
786
- {
787
- ...payload,
788
- amount: payload.amount.toString()
789
- }
790
- ];
791
- }, [payload]);
764
+ const payload = params?.payload;
792
765
  return useQuery({
793
- queryKey,
766
+ queryKey: ["swap", "quote", payload && { ...payload, amount: payload.amount.toString() }],
794
767
  queryFn: async () => {
795
768
  if (!payload) {
796
769
  return void 0;
@@ -798,1239 +771,1324 @@ var useQuote = (payload) => {
798
771
  return sodax.swaps.getQuote(payload);
799
772
  },
800
773
  enabled: !!payload,
801
- refetchInterval: 3e3
774
+ refetchInterval: 3e3,
775
+ ...queryOptions
802
776
  });
803
777
  };
804
- function useSwap(spokeProvider) {
778
+ function useSwap({
779
+ mutationOptions
780
+ } = {}) {
805
781
  const { sodax } = useSodaxContext();
806
782
  const queryClient = useQueryClient();
807
- return useMutation({
808
- mutationFn: async (params) => {
809
- if (!spokeProvider) {
810
- throw new Error("Spoke provider not found");
811
- }
812
- return sodax.swaps.swap({
813
- intentParams: params,
814
- spokeProvider
815
- });
816
- },
817
- onSuccess: () => {
818
- queryClient.invalidateQueries({ queryKey: ["xBalances"] });
783
+ return useSafeMutation({
784
+ mutationKey: ["swap"],
785
+ ...mutationOptions,
786
+ mutationFn: async (vars) => unwrapResult(await sodax.swaps.swap({ ...vars, raw: false })),
787
+ onSuccess: async (data, vars, ctx) => {
788
+ queryClient.invalidateQueries({ queryKey: ["shared", "xBalances", vars.params.srcChainKey] });
789
+ queryClient.invalidateQueries({ queryKey: ["shared", "xBalances", vars.params.dstChainKey] });
790
+ await mutationOptions?.onSuccess?.(data, vars, ctx);
819
791
  }
820
792
  });
821
793
  }
822
- var useStatus = (intent_tx_hash) => {
794
+ var useStatus = ({
795
+ params,
796
+ queryOptions
797
+ } = {}) => {
823
798
  const { sodax } = useSodaxContext();
799
+ const intentTxHash = params?.intentTxHash;
824
800
  return useQuery({
825
- queryKey: [intent_tx_hash],
801
+ queryKey: ["swap", "status", intentTxHash],
826
802
  queryFn: async () => {
827
- return sodax.swaps.getStatus({ intent_tx_hash });
803
+ if (!intentTxHash) return void 0;
804
+ return sodax.swaps.getStatus({ intent_tx_hash: intentTxHash });
828
805
  },
829
- refetchInterval: 3e3
830
- // 3s
806
+ enabled: !!intentTxHash,
807
+ refetchInterval: 3e3,
808
+ ...queryOptions
831
809
  });
832
810
  };
833
- function useSwapAllowance(params, spokeProvider) {
811
+ function useSwapAllowance({
812
+ params,
813
+ queryOptions
814
+ } = {}) {
834
815
  const { sodax } = useSodaxContext();
816
+ const payload = params?.payload;
817
+ const srcChainKey = params?.srcChainKey;
818
+ const walletProvider = params?.walletProvider;
835
819
  return useQuery({
836
- queryKey: ["allowance", params],
820
+ // Extract the (chain, owner, token, amount) tuple that actually scopes the allowance —
821
+ // raw-object keys break per Rule 4 (bigints) and churn on every render.
822
+ queryKey: [
823
+ "swap",
824
+ "allowance",
825
+ payload?.srcChainKey,
826
+ payload?.srcAddress,
827
+ payload?.inputToken,
828
+ payload?.inputAmount?.toString()
829
+ ],
837
830
  queryFn: async () => {
838
- if (!spokeProvider || !params) {
831
+ if (!srcChainKey || !walletProvider || !payload) {
839
832
  return false;
840
833
  }
841
834
  const allowance = await sodax.swaps.isAllowanceValid({
842
- intentParams: params,
843
- spokeProvider
835
+ params: payload,
836
+ raw: false,
837
+ walletProvider
844
838
  });
845
- if (allowance.ok) {
846
- return allowance.value;
847
- }
848
- return false;
839
+ return allowance.ok ? allowance.value : false;
849
840
  },
850
- enabled: !!spokeProvider && !!params,
851
- refetchInterval: 2e3
841
+ enabled: !!srcChainKey && !!walletProvider && !!payload,
842
+ refetchInterval: 2e3,
843
+ ...queryOptions
852
844
  });
853
845
  }
854
- function useSwapApprove(params, spokeProvider) {
846
+ function useSwapApprove({
847
+ mutationOptions
848
+ } = {}) {
855
849
  const { sodax } = useSodaxContext();
856
850
  const queryClient = useQueryClient();
857
- const {
858
- mutateAsync: approve,
859
- isPending,
860
- error,
861
- reset: resetError
862
- } = useMutation({
863
- mutationFn: async ({ params: params2 }) => {
864
- if (!spokeProvider) {
865
- throw new Error("Spoke provider not found");
866
- }
867
- if (!params2) {
868
- throw new Error("Swap Params not found");
869
- }
870
- const allowance = await sodax.swaps.approve({
871
- intentParams: params2,
872
- spokeProvider
873
- });
874
- if (!allowance.ok) {
875
- throw new Error("Failed to approve input token");
876
- }
877
- return allowance.ok;
878
- },
879
- onSuccess: () => {
880
- queryClient.invalidateQueries({ queryKey: ["allowance", params] });
851
+ return useSafeMutation({
852
+ mutationKey: ["swap", "approve"],
853
+ ...mutationOptions,
854
+ mutationFn: async ({ params, walletProvider }) => unwrapResult(
855
+ await sodax.swaps.approve({
856
+ params,
857
+ raw: false,
858
+ walletProvider
859
+ })
860
+ ),
861
+ onSuccess: async (data, vars, ctx) => {
862
+ queryClient.invalidateQueries({ queryKey: ["swap", "allowance"] });
863
+ await mutationOptions?.onSuccess?.(data, vars, ctx);
881
864
  }
882
865
  });
883
- return {
884
- approve,
885
- isLoading: isPending,
886
- error,
887
- resetError
888
- };
889
866
  }
890
- function useCancelSwap(spokeProvider) {
867
+
868
+ // src/hooks/swap/useCancelSwap.ts
869
+ function useCancelSwap({
870
+ mutationOptions
871
+ } = {}) {
891
872
  const { sodax } = useSodaxContext();
892
- return useMutation({
893
- mutationFn: async ({ intent, raw = false }) => {
894
- if (!spokeProvider) {
895
- throw new Error("Spoke provider not found");
896
- }
897
- return sodax.swaps.cancelIntent(intent, spokeProvider, raw);
898
- }
873
+ return useSafeMutation({
874
+ mutationKey: ["swap", "cancel"],
875
+ ...mutationOptions,
876
+ mutationFn: async ({ srcChainKey, walletProvider, intent }) => unwrapResult(await sodax.swaps.cancelIntent({ params: { srcChainKey, intent }, walletProvider }))
899
877
  });
900
878
  }
901
- function useCreateLimitOrder(spokeProvider) {
879
+
880
+ // src/hooks/swap/useCreateLimitOrder.ts
881
+ function useCreateLimitOrder({
882
+ mutationOptions
883
+ } = {}) {
902
884
  const { sodax } = useSodaxContext();
903
- return useMutation({
904
- mutationFn: async (params) => {
905
- if (!spokeProvider) {
906
- throw new Error("Spoke provider not found");
907
- }
908
- return sodax.swaps.createLimitOrder({
909
- intentParams: params,
910
- spokeProvider
911
- });
912
- }
885
+ return useSafeMutation({
886
+ mutationKey: ["swap", "limitOrder", "create"],
887
+ ...mutationOptions,
888
+ mutationFn: async (vars) => unwrapResult(await sodax.swaps.createLimitOrder({ ...vars, raw: false }))
913
889
  });
914
890
  }
915
- function useCancelLimitOrder() {
891
+
892
+ // src/hooks/swap/useCancelLimitOrder.ts
893
+ function useCancelLimitOrder({
894
+ mutationOptions
895
+ } = {}) {
916
896
  const { sodax } = useSodaxContext();
917
- return useMutation({
918
- mutationFn: async ({ intent, spokeProvider, timeout }) => {
919
- return sodax.swaps.cancelLimitOrder({
920
- intent,
921
- spokeProvider,
922
- timeout
923
- });
924
- }
897
+ return useSafeMutation({
898
+ mutationKey: ["swap", "limitOrder", "cancel"],
899
+ ...mutationOptions,
900
+ mutationFn: async ({ srcChainKey, walletProvider, intent, timeout }) => unwrapResult(await sodax.swaps.cancelLimitOrder({ params: { srcChainKey, intent }, walletProvider, timeout }))
925
901
  });
926
902
  }
927
- var useBackendIntentByTxHash = (params) => {
903
+ var useBackendIntentByTxHash = ({
904
+ params,
905
+ queryOptions
906
+ } = {}) => {
928
907
  const { sodax } = useSodaxContext();
929
- const defaultQueryOptions = {
930
- queryKey: ["api", "intent", "txHash", params?.params?.txHash],
931
- enabled: !!params?.params?.txHash && params?.params?.txHash.length > 0,
932
- retry: 3,
933
- refetchInterval: 1e3
934
- };
935
- const queryOptions = {
936
- ...defaultQueryOptions,
937
- ...params?.queryOptions
938
- };
908
+ const txHash = params?.txHash;
939
909
  return useQuery({
940
- ...queryOptions,
910
+ queryKey: ["backend", "intent", "txHash", txHash],
941
911
  queryFn: async () => {
942
- if (!params?.params?.txHash) {
943
- return void 0;
944
- }
945
- return sodax.backendApi.getIntentByTxHash(params.params.txHash);
946
- }
912
+ if (!txHash) return void 0;
913
+ const result = await sodax.backendApi.getIntentByTxHash(txHash);
914
+ return result.ok ? result.value : void 0;
915
+ },
916
+ enabled: !!txHash && txHash.length > 0,
917
+ retry: 3,
918
+ refetchInterval: 1e3,
919
+ ...queryOptions
947
920
  });
948
921
  };
949
- var useBackendIntentByHash = (params) => {
922
+ var useBackendIntentByHash = ({
923
+ params,
924
+ queryOptions
925
+ } = {}) => {
950
926
  const { sodax } = useSodaxContext();
951
- const defaultQueryOptions = {
952
- queryKey: ["api", "intent", "hash", params?.params?.intentHash],
953
- enabled: !!params?.params?.intentHash && params?.params?.intentHash.length > 0,
954
- retry: 3
955
- };
956
- const queryOptions = {
957
- ...defaultQueryOptions,
958
- ...params?.queryOptions
959
- };
927
+ const intentHash = params?.intentHash;
960
928
  return useQuery({
961
- ...queryOptions,
929
+ queryKey: ["backend", "intent", "hash", intentHash],
962
930
  queryFn: async () => {
963
- if (!params?.params?.intentHash) {
964
- return void 0;
965
- }
966
- return sodax.backendApi.getIntentByHash(params.params.intentHash);
967
- }
931
+ if (!intentHash) return void 0;
932
+ const result = await sodax.backendApi.getIntentByHash(intentHash);
933
+ return result.ok ? result.value : void 0;
934
+ },
935
+ enabled: !!intentHash && intentHash.length > 0,
936
+ retry: 3,
937
+ ...queryOptions
968
938
  });
969
939
  };
970
940
  var useBackendUserIntents = ({
971
941
  params,
972
942
  queryOptions
973
- }) => {
943
+ } = {}) => {
974
944
  const { sodax } = useSodaxContext();
975
- const defaultQueryOptions = {
976
- queryKey: ["api", "intent", "user", params],
977
- enabled: !!params && !!params.userAddress && params.userAddress.length > 0,
978
- retry: 3
979
- };
980
- queryOptions = {
981
- ...defaultQueryOptions,
982
- ...queryOptions
983
- // override default query options if provided
984
- };
945
+ const userAddress = params?.userAddress;
946
+ const startDate = params?.startDate;
947
+ const endDate = params?.endDate;
985
948
  return useQuery({
986
- ...queryOptions,
949
+ queryKey: ["backend", "intent", "user", userAddress, startDate, endDate],
987
950
  queryFn: async () => {
988
- if (!params?.userAddress) {
989
- return void 0;
990
- }
991
- return sodax.backendApi.getUserIntents(params);
992
- }
951
+ if (!userAddress) return void 0;
952
+ return unwrapResult(await sodax.backendApi.getUserIntents({ userAddress, startDate, endDate }));
953
+ },
954
+ enabled: !!userAddress && userAddress.length > 0,
955
+ retry: 3,
956
+ ...queryOptions
993
957
  });
994
958
  };
995
- var useBackendSubmitSwapTx = (params) => {
959
+
960
+ // src/hooks/backend/useBackendSubmitSwapTx.ts
961
+ var useBackendSubmitSwapTx = ({
962
+ mutationOptions
963
+ } = {}) => {
996
964
  const { sodax } = useSodaxContext();
997
- const defaultMutationOptions = {
998
- retry: 3
999
- };
1000
- const mutationOptions = {
1001
- ...defaultMutationOptions,
1002
- ...params?.mutationOptions
1003
- };
1004
- return useMutation({
965
+ return useSafeMutation({
966
+ mutationKey: ["backend", "submitSwapTx"],
967
+ retry: 3,
1005
968
  ...mutationOptions,
1006
- mutationFn: async (request) => {
1007
- return sodax.backendApi.submitSwapTx(request, params?.apiConfig);
1008
- }
969
+ mutationFn: async ({ request, apiConfig }) => unwrapResult(await sodax.backendApi.submitSwapTx(request, apiConfig))
1009
970
  });
1010
971
  };
1011
- var useBackendSubmitSwapTxStatus = (params) => {
972
+ var useBackendSubmitSwapTxStatus = ({
973
+ params,
974
+ queryOptions
975
+ } = {}) => {
1012
976
  const { sodax } = useSodaxContext();
1013
- const defaultQueryOptions = {
1014
- queryKey: ["api", "swaps", "submit-tx", "status", params?.params?.txHash, params?.params?.srcChainId],
1015
- enabled: !!params?.params?.txHash && params.params.txHash.length > 0,
977
+ const txHash = params?.txHash;
978
+ const srcChainKey = params?.srcChainKey;
979
+ const apiConfig = params?.apiConfig;
980
+ return useQuery({
981
+ queryKey: ["backend", "submitSwapTx", "status", txHash, srcChainKey],
982
+ queryFn: async () => {
983
+ if (!txHash) return void 0;
984
+ return unwrapResult(
985
+ await sodax.backendApi.getSubmitSwapTxStatus(
986
+ {
987
+ txHash,
988
+ srcChainKey
989
+ },
990
+ apiConfig
991
+ )
992
+ );
993
+ },
994
+ enabled: !!txHash && txHash.length > 0,
1016
995
  retry: 3,
1017
996
  refetchInterval: (query) => {
1018
997
  const status = query.state.data?.data?.status;
1019
998
  if (status === "executed" || status === "failed") return false;
1020
999
  return 1e3;
1021
- }
1022
- };
1023
- const queryOptions = {
1024
- ...defaultQueryOptions,
1025
- ...params?.queryOptions
1026
- };
1027
- return useQuery({
1028
- ...queryOptions,
1029
- queryFn: async () => {
1030
- if (!params?.params?.txHash) {
1031
- return void 0;
1032
- }
1033
- return sodax.backendApi.getSubmitSwapTxStatus(
1034
- {
1035
- txHash: params.params.txHash,
1036
- srcChainId: params.params.srcChainId
1037
- },
1038
- params.apiConfig
1039
- );
1040
- }
1000
+ },
1001
+ ...queryOptions
1041
1002
  });
1042
1003
  };
1043
- var useBackendOrderbook = (params) => {
1004
+ var useBackendOrderbook = ({
1005
+ params,
1006
+ queryOptions
1007
+ } = {}) => {
1044
1008
  const { sodax } = useSodaxContext();
1045
- const defaultQueryOptions = {
1046
- queryKey: ["api", "solver", "orderbook", params?.pagination?.offset, params?.pagination?.limit],
1047
- enabled: !!params?.pagination && !!params?.pagination.offset && !!params?.pagination.limit,
1048
- staleTime: 30 * 1e3,
1049
- // 30 seconds for real-time data
1050
- retry: 3
1051
- };
1052
- const queryOptions = {
1053
- ...defaultQueryOptions,
1054
- ...params?.queryOptions
1055
- // override default query options if provided
1056
- };
1009
+ const pagination = params?.pagination;
1057
1010
  return useQuery({
1058
- ...queryOptions,
1011
+ queryKey: ["backend", "orderbook", pagination?.offset, pagination?.limit],
1059
1012
  queryFn: async () => {
1060
- if (!params?.pagination || !params?.pagination.offset || !params?.pagination.limit) {
1061
- return void 0;
1013
+ if (!pagination?.offset || !pagination?.limit) {
1014
+ throw new Error("Pagination offset and limit are required");
1062
1015
  }
1063
- return sodax.backendApi.getOrderbook(params.pagination);
1064
- }
1016
+ return unwrapResult(await sodax.backendApi.getOrderbook(pagination));
1017
+ },
1018
+ enabled: !!pagination?.offset && !!pagination?.limit,
1019
+ staleTime: 30 * 1e3,
1020
+ retry: 3,
1021
+ ...queryOptions
1065
1022
  });
1066
1023
  };
1067
- var useBackendMoneyMarketPosition = (params) => {
1024
+ var useBackendMoneyMarketPosition = ({
1025
+ params,
1026
+ queryOptions
1027
+ } = {}) => {
1068
1028
  const { sodax } = useSodaxContext();
1069
- const defaultQueryOptions = {
1070
- queryKey: ["api", "mm", "position", params?.userAddress],
1071
- enabled: !!params?.userAddress && params?.userAddress.length > 0,
1072
- retry: 3
1073
- };
1074
- const queryOptions = {
1075
- ...defaultQueryOptions,
1076
- ...params?.queryOptions
1077
- };
1029
+ const userAddress = params?.userAddress;
1078
1030
  return useQuery({
1079
- ...queryOptions,
1031
+ queryKey: ["backend", "mm", "position", userAddress],
1080
1032
  queryFn: async () => {
1081
- if (!params?.userAddress) {
1082
- return void 0;
1083
- }
1084
- return sodax.backendApi.getMoneyMarketPosition(params.userAddress);
1085
- }
1033
+ if (!userAddress) return void 0;
1034
+ return unwrapResult(await sodax.backendApi.getMoneyMarketPosition(userAddress));
1035
+ },
1036
+ enabled: !!userAddress && userAddress.length > 0,
1037
+ retry: 3,
1038
+ ...queryOptions
1086
1039
  });
1087
1040
  };
1088
- var useBackendAllMoneyMarketAssets = (params) => {
1041
+ var useBackendAllMoneyMarketAssets = ({
1042
+ queryOptions
1043
+ } = {}) => {
1089
1044
  const { sodax } = useSodaxContext();
1090
- const defaultQueryOptions = {
1091
- queryKey: ["api", "mm", "assets", "all"],
1092
- enabled: true,
1093
- retry: 3
1094
- };
1095
- const queryOptions = {
1096
- ...defaultQueryOptions,
1097
- ...params?.queryOptions
1098
- };
1099
1045
  return useQuery({
1100
- ...queryOptions,
1046
+ queryKey: ["backend", "mm", "assets", "all"],
1101
1047
  queryFn: async () => {
1102
- return sodax.backendApi.getAllMoneyMarketAssets();
1103
- }
1048
+ return unwrapResult(await sodax.backendApi.getAllMoneyMarketAssets());
1049
+ },
1050
+ retry: 3,
1051
+ ...queryOptions
1104
1052
  });
1105
1053
  };
1106
- var useBackendMoneyMarketAsset = (params) => {
1054
+ var useBackendMoneyMarketAsset = ({
1055
+ params,
1056
+ queryOptions
1057
+ } = {}) => {
1107
1058
  const { sodax } = useSodaxContext();
1108
- const defaultQueryOptions = {
1109
- queryKey: ["api", "mm", "asset", params?.params?.reserveAddress],
1110
- enabled: !!params?.params?.reserveAddress && params?.params?.reserveAddress.length > 0,
1111
- retry: 3
1112
- };
1113
- const queryOptions = {
1114
- ...defaultQueryOptions,
1115
- ...params?.queryOptions
1116
- };
1059
+ const reserveAddress = params?.reserveAddress;
1117
1060
  return useQuery({
1118
- ...queryOptions,
1061
+ queryKey: ["backend", "mm", "asset", reserveAddress],
1119
1062
  queryFn: async () => {
1120
- if (!params?.params?.reserveAddress) {
1121
- return void 0;
1122
- }
1123
- return sodax.backendApi.getMoneyMarketAsset(params.params.reserveAddress);
1124
- }
1063
+ if (!reserveAddress) return void 0;
1064
+ return unwrapResult(await sodax.backendApi.getMoneyMarketAsset(reserveAddress));
1065
+ },
1066
+ enabled: !!reserveAddress && reserveAddress.length > 0,
1067
+ retry: 3,
1068
+ ...queryOptions
1125
1069
  });
1126
1070
  };
1127
- var useBackendMoneyMarketAssetBorrowers = (params) => {
1071
+ var useBackendMoneyMarketAssetBorrowers = ({
1072
+ params,
1073
+ queryOptions
1074
+ } = {}) => {
1128
1075
  const { sodax } = useSodaxContext();
1129
- const defaultQueryOptions = {
1130
- queryKey: ["api", "mm", "asset", "borrowers", params],
1131
- enabled: !!params?.params?.reserveAddress && !!params.pagination.offset && !!params.pagination.limit,
1132
- retry: 3
1133
- };
1134
- const queryOptions = {
1135
- ...defaultQueryOptions,
1136
- ...params?.queryOptions
1137
- };
1076
+ const reserveAddress = params?.reserveAddress;
1077
+ const pagination = params?.pagination;
1138
1078
  return useQuery({
1139
- ...queryOptions,
1079
+ queryKey: ["backend", "mm", "asset", "borrowers", reserveAddress, pagination],
1140
1080
  queryFn: async () => {
1141
- if (!params?.params?.reserveAddress || !params.pagination.offset || !params.pagination.limit) {
1081
+ if (!reserveAddress || !pagination?.offset || !pagination?.limit) {
1142
1082
  return void 0;
1143
1083
  }
1144
- return sodax.backendApi.getMoneyMarketAssetBorrowers(params.params.reserveAddress, {
1145
- offset: params.pagination.offset,
1146
- limit: params.pagination.limit
1147
- });
1148
- }
1084
+ return unwrapResult(
1085
+ await sodax.backendApi.getMoneyMarketAssetBorrowers(reserveAddress, {
1086
+ offset: pagination.offset,
1087
+ limit: pagination.limit
1088
+ })
1089
+ );
1090
+ },
1091
+ enabled: !!reserveAddress && !!pagination?.offset && !!pagination?.limit,
1092
+ retry: 3,
1093
+ ...queryOptions
1149
1094
  });
1150
1095
  };
1151
- var useBackendMoneyMarketAssetSuppliers = (params) => {
1096
+ var useBackendMoneyMarketAssetSuppliers = ({
1097
+ params,
1098
+ queryOptions
1099
+ } = {}) => {
1152
1100
  const { sodax } = useSodaxContext();
1153
- const defaultQueryOptions = {
1154
- queryKey: ["api", "mm", "asset", "suppliers", params],
1155
- enabled: !!params?.params?.reserveAddress && !!params.pagination.offset && !!params.pagination.limit,
1156
- retry: 3
1157
- };
1158
- const queryOptions = {
1159
- ...defaultQueryOptions,
1160
- ...params?.queryOptions
1161
- };
1101
+ const reserveAddress = params?.reserveAddress;
1102
+ const pagination = params?.pagination;
1162
1103
  return useQuery({
1163
- ...queryOptions,
1104
+ queryKey: ["backend", "mm", "asset", "suppliers", reserveAddress, pagination],
1164
1105
  queryFn: async () => {
1165
- if (!params?.params?.reserveAddress || !params.pagination.offset || !params.pagination.limit) {
1106
+ if (!reserveAddress || !pagination?.offset || !pagination?.limit) {
1166
1107
  return void 0;
1167
1108
  }
1168
- return sodax.backendApi.getMoneyMarketAssetSuppliers(params.params.reserveAddress, {
1169
- offset: params.pagination.offset,
1170
- limit: params.pagination.limit
1171
- });
1172
- }
1109
+ return unwrapResult(
1110
+ await sodax.backendApi.getMoneyMarketAssetSuppliers(reserveAddress, {
1111
+ offset: pagination.offset,
1112
+ limit: pagination.limit
1113
+ })
1114
+ );
1115
+ },
1116
+ enabled: !!reserveAddress && !!pagination?.offset && !!pagination?.limit,
1117
+ retry: 3,
1118
+ ...queryOptions
1173
1119
  });
1174
1120
  };
1175
- var useBackendAllMoneyMarketBorrowers = (params) => {
1121
+ var useBackendAllMoneyMarketBorrowers = ({
1122
+ params,
1123
+ queryOptions
1124
+ } = {}) => {
1176
1125
  const { sodax } = useSodaxContext();
1177
- const defaultQueryOptions = {
1178
- queryKey: ["api", "mm", "borrowers", "all", params],
1179
- enabled: !!params && !!params.pagination.offset && !!params.pagination.limit,
1180
- retry: 3
1181
- };
1182
- const queryOptions = {
1183
- ...defaultQueryOptions,
1184
- ...params?.queryOptions
1185
- };
1126
+ const pagination = params?.pagination;
1186
1127
  return useQuery({
1187
- ...queryOptions,
1128
+ queryKey: ["backend", "mm", "borrowers", "all", pagination],
1188
1129
  queryFn: async () => {
1189
- if (!params || !params.pagination.offset || !params.pagination.limit) {
1130
+ if (!pagination?.offset || !pagination?.limit) {
1190
1131
  return void 0;
1191
1132
  }
1192
- return sodax.backendApi.getAllMoneyMarketBorrowers({
1193
- offset: params.pagination.offset,
1194
- limit: params.pagination.limit
1195
- });
1196
- }
1133
+ return unwrapResult(
1134
+ await sodax.backendApi.getAllMoneyMarketBorrowers({
1135
+ offset: pagination.offset,
1136
+ limit: pagination.limit
1137
+ })
1138
+ );
1139
+ },
1140
+ enabled: !!pagination?.offset && !!pagination?.limit,
1141
+ retry: 3,
1142
+ ...queryOptions
1197
1143
  });
1198
1144
  };
1199
- function useBridgeAllowance(params, spokeProvider) {
1145
+ function useBridge({
1146
+ mutationOptions
1147
+ } = {}) {
1200
1148
  const { sodax } = useSodaxContext();
1149
+ const queryClient = useQueryClient();
1150
+ return useSafeMutation({
1151
+ mutationKey: ["bridge"],
1152
+ ...mutationOptions,
1153
+ mutationFn: async (vars) => unwrapResult(await sodax.bridge.bridge({ ...vars, raw: false })),
1154
+ onSuccess: async (data, vars, ctx) => {
1155
+ queryClient.invalidateQueries({ queryKey: ["shared", "xBalances", vars.params.srcChainKey] });
1156
+ queryClient.invalidateQueries({ queryKey: ["shared", "xBalances", vars.params.dstChainKey] });
1157
+ await mutationOptions?.onSuccess?.(data, vars, ctx);
1158
+ }
1159
+ });
1160
+ }
1161
+ function useBridgeAllowance({
1162
+ params,
1163
+ queryOptions
1164
+ } = {}) {
1165
+ const { sodax } = useSodaxContext();
1166
+ const payload = params?.payload;
1167
+ const walletProvider = params?.walletProvider;
1201
1168
  return useQuery({
1202
- queryKey: ["bridge-allowance", params],
1169
+ // Extract the (chain, owner, token, amount) tuple that actually scopes the allowance —
1170
+ // raw-object keys break per Rule 4 (bigints) and churn on every render.
1171
+ queryKey: [
1172
+ "bridge",
1173
+ "allowance",
1174
+ payload?.srcChainKey,
1175
+ payload?.srcAddress,
1176
+ payload?.srcToken,
1177
+ payload?.amount?.toString()
1178
+ ],
1203
1179
  queryFn: async () => {
1204
- if (!spokeProvider || !params) {
1180
+ if (!payload || !walletProvider) {
1205
1181
  return false;
1206
1182
  }
1207
- const allowance = await sodax.bridge.isAllowanceValid({
1208
- params,
1209
- spokeProvider
1183
+ const result = await sodax.bridge.isAllowanceValid({
1184
+ params: payload,
1185
+ raw: false,
1186
+ walletProvider
1210
1187
  });
1211
- if (allowance.ok) {
1212
- return allowance.value;
1213
- }
1214
- return false;
1188
+ return result.ok ? result.value : false;
1215
1189
  },
1216
- enabled: !!spokeProvider && !!params
1190
+ enabled: !!payload && !!walletProvider,
1191
+ refetchInterval: 2e3,
1192
+ gcTime: 0,
1193
+ ...queryOptions
1217
1194
  });
1218
1195
  }
1219
- function useBridgeApprove(spokeProvider) {
1196
+ function useBridgeApprove({
1197
+ mutationOptions
1198
+ } = {}) {
1220
1199
  const { sodax } = useSodaxContext();
1221
1200
  const queryClient = useQueryClient();
1222
- const {
1223
- mutateAsync: approve,
1224
- isPending,
1225
- error,
1226
- reset: resetError
1227
- } = useMutation({
1228
- mutationFn: async (params) => {
1229
- if (!spokeProvider) {
1230
- throw new Error("Spoke provider not found");
1231
- }
1232
- const allowance = await sodax.bridge.approve({
1233
- params,
1234
- spokeProvider
1235
- });
1236
- if (!allowance.ok) {
1237
- throw new Error("Failed to approve tokens for bridge");
1238
- }
1239
- return true;
1240
- },
1241
- onSuccess: (_, params) => {
1242
- queryClient.invalidateQueries({ queryKey: ["bridge-allowance", params] });
1243
- }
1244
- });
1245
- return {
1246
- approve,
1247
- isLoading: isPending,
1248
- error,
1249
- resetError
1250
- };
1251
- }
1252
- function useBridge(spokeProvider) {
1253
- const { sodax } = useSodaxContext();
1254
- return useMutation({
1255
- mutationFn: async (params) => {
1256
- if (!spokeProvider) {
1257
- throw new Error("Spoke provider not found");
1258
- }
1259
- const result = await sodax.bridge.bridge({
1260
- params,
1261
- spokeProvider
1262
- });
1263
- if (!result.ok) {
1264
- throw new Error(`Bridge failed: ${result.error.code}`);
1265
- }
1266
- return result;
1201
+ return useSafeMutation({
1202
+ mutationKey: ["bridge", "approve"],
1203
+ ...mutationOptions,
1204
+ mutationFn: async (vars) => unwrapResult(await sodax.bridge.approve({ ...vars, raw: false })),
1205
+ onSuccess: async (data, vars, ctx) => {
1206
+ queryClient.invalidateQueries({ queryKey: ["bridge", "allowance"] });
1207
+ await mutationOptions?.onSuccess?.(data, vars, ctx);
1267
1208
  }
1268
1209
  });
1269
1210
  }
1270
- function useGetBridgeableAmount(from, to) {
1211
+ function useGetBridgeableAmount({
1212
+ params,
1213
+ queryOptions
1214
+ } = {}) {
1271
1215
  const { sodax } = useSodaxContext();
1216
+ const from = params?.from;
1217
+ const to = params?.to;
1272
1218
  return useQuery({
1273
- queryKey: ["spoke-asset-manager-token-balance", from, to],
1219
+ queryKey: ["bridge", "bridgeableAmount", from, to],
1274
1220
  queryFn: async () => {
1275
1221
  if (!from || !to) {
1276
- return { amount: 0n, decimals: 0, type: "DEPOSIT_LIMIT" };
1222
+ throw new Error("from and to tokens are required");
1277
1223
  }
1278
1224
  const result = await sodax.bridge.getBridgeableAmount(from, to);
1279
- if (result.ok) {
1280
- return result.value;
1225
+ if (!result.ok) {
1226
+ throw result.error;
1281
1227
  }
1282
- console.error("Error getting bridgeable amount:", result.error);
1283
- return { amount: 0n, decimals: 0, type: "DEPOSIT_LIMIT" };
1228
+ return result.value;
1284
1229
  },
1285
- enabled: !!from && !!to
1230
+ enabled: !!from && !!to,
1231
+ ...queryOptions
1286
1232
  });
1287
1233
  }
1288
- function useGetBridgeableTokens(from, to, token) {
1234
+ function useGetBridgeableTokens({
1235
+ params,
1236
+ queryOptions
1237
+ } = {}) {
1289
1238
  const { sodax } = useSodaxContext();
1239
+ const from = params?.from;
1240
+ const to = params?.to;
1241
+ const token = params?.token;
1290
1242
  return useQuery({
1291
- queryKey: ["bridgeable-tokens", from, to, token],
1292
- queryFn: async () => {
1243
+ queryKey: ["bridge", "bridgeableTokens", from, to, token],
1244
+ queryFn: () => {
1293
1245
  if (!from || !to || !token) {
1294
- return [];
1246
+ throw new Error("from, to and token are required");
1295
1247
  }
1296
1248
  const result = sodax.bridge.getBridgeableTokens(from, to, token);
1297
- if (result.ok) {
1298
- return result.value;
1249
+ if (!result.ok) {
1250
+ throw result.error;
1299
1251
  }
1300
- console.error("Error getting bridgeable tokens:", result.error);
1301
- return [];
1252
+ return result.value;
1302
1253
  },
1303
- enabled: !!from && !!to && !!token
1254
+ enabled: !!from && !!to && !!token,
1255
+ ...queryOptions
1304
1256
  });
1305
1257
  }
1306
- function useStake(spokeProvider) {
1258
+ function useStake({
1259
+ mutationOptions
1260
+ } = {}) {
1307
1261
  const { sodax } = useSodaxContext();
1308
- return useMutation({
1309
- mutationFn: async (params) => {
1310
- if (!spokeProvider) {
1311
- throw new Error("Spoke provider not found");
1312
- }
1313
- const result = await sodax.staking.stake(params, spokeProvider);
1314
- if (!result.ok) {
1315
- throw new Error(`Stake failed: ${result.error.code}`);
1316
- }
1317
- return result.value;
1262
+ const queryClient = useQueryClient();
1263
+ return useSafeMutation({
1264
+ mutationKey: ["staking", "stake"],
1265
+ ...mutationOptions,
1266
+ mutationFn: async (vars) => unwrapResult(await sodax.staking.stake({ ...vars, raw: false })),
1267
+ onSuccess: async (data, vars, ctx) => {
1268
+ const { params } = vars;
1269
+ queryClient.invalidateQueries({ queryKey: ["staking", "info", params.srcChainKey, params.srcAddress] });
1270
+ queryClient.invalidateQueries({ queryKey: ["staking", "allowance", params.srcChainKey, "stake"] });
1271
+ queryClient.invalidateQueries({ queryKey: ["staking", "stakeRatio"] });
1272
+ queryClient.invalidateQueries({ queryKey: ["staking", "convertedAssets"] });
1273
+ queryClient.invalidateQueries({ queryKey: ["shared", "xBalances", params.srcChainKey] });
1274
+ await mutationOptions?.onSuccess?.(data, vars, ctx);
1318
1275
  }
1319
1276
  });
1320
1277
  }
1321
- function useStakeApprove(spokeProvider) {
1278
+ function useStakeApprove({
1279
+ mutationOptions
1280
+ } = {}) {
1322
1281
  const { sodax } = useSodaxContext();
1323
- return useMutation({
1324
- mutationFn: async (params) => {
1325
- if (!spokeProvider) {
1326
- throw new Error("Spoke provider not found");
1327
- }
1328
- const result = await sodax.staking.approve({
1282
+ const queryClient = useQueryClient();
1283
+ return useSafeMutation({
1284
+ mutationKey: ["staking", "approve", "stake"],
1285
+ ...mutationOptions,
1286
+ mutationFn: async ({ params, walletProvider }) => unwrapResult(
1287
+ await sodax.staking.approve({
1329
1288
  params: { ...params, action: "stake" },
1330
- spokeProvider
1331
- });
1332
- if (!result.ok) {
1333
- throw new Error(`Stake approval failed: ${result.error.code}`);
1334
- }
1335
- return result.value;
1289
+ raw: false,
1290
+ walletProvider
1291
+ })
1292
+ ),
1293
+ onSuccess: async (data, vars, ctx) => {
1294
+ queryClient.invalidateQueries({ queryKey: ["staking", "allowance", vars.params.srcChainKey, "stake"] });
1295
+ await mutationOptions?.onSuccess?.(data, vars, ctx);
1336
1296
  }
1337
1297
  });
1338
1298
  }
1339
- function useStakeAllowance(params, spokeProvider) {
1299
+ function useStakeAllowance({
1300
+ params,
1301
+ queryOptions
1302
+ } = {}) {
1340
1303
  const { sodax } = useSodaxContext();
1304
+ const payload = params?.payload;
1341
1305
  return useQuery({
1342
- queryKey: ["soda", "stakeAllowance", params, spokeProvider?.chainConfig.chain.id],
1306
+ queryKey: ["staking", "allowance", payload?.srcChainKey, "stake", payload?.srcAddress, payload?.amount?.toString()],
1343
1307
  queryFn: async () => {
1344
- if (!params || !spokeProvider) {
1345
- return false;
1308
+ if (!payload) {
1309
+ throw new Error("Params are required");
1346
1310
  }
1347
1311
  const result = await sodax.staking.isAllowanceValid({
1348
- params: { ...params, action: "stake" },
1349
- spokeProvider
1312
+ params: { ...payload, action: "stake" },
1313
+ raw: true
1350
1314
  });
1351
- if (!result.ok) {
1352
- throw new Error(`Allowance check failed: ${result.error.code}`);
1353
- }
1315
+ if (!result.ok) throw result.error;
1354
1316
  return result.value;
1355
1317
  },
1356
- enabled: !!params && !!spokeProvider,
1357
- refetchInterval: 5e3
1358
- // Refetch every 5 seconds
1318
+ enabled: !!payload,
1319
+ refetchInterval: 5e3,
1320
+ gcTime: 0,
1321
+ ...queryOptions
1359
1322
  });
1360
1323
  }
1361
- function useUnstake(spokeProvider) {
1324
+ function useUnstake({
1325
+ mutationOptions
1326
+ } = {}) {
1362
1327
  const { sodax } = useSodaxContext();
1363
1328
  const queryClient = useQueryClient();
1364
- return useMutation({
1365
- mutationFn: async (params) => {
1366
- if (!spokeProvider) {
1367
- throw new Error("Spoke provider not found");
1368
- }
1369
- const result = await sodax.staking.unstake({ ...params, action: "unstake" }, spokeProvider);
1370
- if (!result.ok) {
1371
- throw new Error(`Unstake failed: ${result.error.code}`);
1372
- }
1373
- return result.value;
1374
- },
1375
- onSuccess: () => {
1376
- queryClient.invalidateQueries({ queryKey: ["stakingInfo"] });
1377
- queryClient.invalidateQueries({ queryKey: ["unstakingInfo"] });
1378
- queryClient.invalidateQueries({ queryKey: ["unstakingInfoWithPenalty"] });
1329
+ return useSafeMutation({
1330
+ mutationKey: ["staking", "unstake"],
1331
+ ...mutationOptions,
1332
+ mutationFn: async (vars) => unwrapResult(await sodax.staking.unstake({ ...vars, raw: false })),
1333
+ onSuccess: async (data, vars, ctx) => {
1334
+ const { params } = vars;
1335
+ queryClient.invalidateQueries({ queryKey: ["staking", "info", params.srcChainKey, params.srcAddress] });
1336
+ queryClient.invalidateQueries({ queryKey: ["staking", "unstakingInfo", params.srcChainKey, params.srcAddress] });
1337
+ queryClient.invalidateQueries({
1338
+ queryKey: ["staking", "unstakingInfoWithPenalty", params.srcChainKey, params.srcAddress]
1339
+ });
1340
+ queryClient.invalidateQueries({ queryKey: ["staking", "allowance", params.srcChainKey, "unstake"] });
1341
+ await mutationOptions?.onSuccess?.(data, vars, ctx);
1379
1342
  }
1380
1343
  });
1381
1344
  }
1382
- function useClaim(spokeProvider) {
1345
+ function useClaim({
1346
+ mutationOptions
1347
+ } = {}) {
1383
1348
  const { sodax } = useSodaxContext();
1384
- return useMutation({
1385
- mutationFn: async (params) => {
1386
- if (!spokeProvider) {
1387
- throw new Error("Spoke provider not found");
1388
- }
1389
- const result = await sodax.staking.claim({ ...params, action: "claim" }, spokeProvider);
1390
- if (!result.ok) {
1391
- throw new Error(`Claim failed: ${result.error.code}`);
1392
- }
1393
- return result.value;
1349
+ const queryClient = useQueryClient();
1350
+ return useSafeMutation({
1351
+ mutationKey: ["staking", "claim"],
1352
+ ...mutationOptions,
1353
+ mutationFn: async (vars) => unwrapResult(await sodax.staking.claim({ ...vars, raw: false })),
1354
+ onSuccess: async (data, vars, ctx) => {
1355
+ const { params } = vars;
1356
+ queryClient.invalidateQueries({ queryKey: ["staking", "unstakingInfo", params.srcChainKey, params.srcAddress] });
1357
+ queryClient.invalidateQueries({
1358
+ queryKey: ["staking", "unstakingInfoWithPenalty", params.srcChainKey, params.srcAddress]
1359
+ });
1360
+ queryClient.invalidateQueries({ queryKey: ["shared", "xBalances", params.srcChainKey] });
1361
+ await mutationOptions?.onSuccess?.(data, vars, ctx);
1394
1362
  }
1395
1363
  });
1396
1364
  }
1397
- function useCancelUnstake(spokeProvider) {
1365
+ function useCancelUnstake({
1366
+ mutationOptions
1367
+ } = {}) {
1398
1368
  const { sodax } = useSodaxContext();
1399
1369
  const queryClient = useQueryClient();
1400
- return useMutation({
1401
- mutationFn: async (params) => {
1402
- if (!spokeProvider) {
1403
- throw new Error("Spoke provider not available");
1404
- }
1405
- const result = await sodax.staking.cancelUnstake({ ...params, action: "cancelUnstake" }, spokeProvider);
1406
- if (!result.ok) {
1407
- throw new Error(`Cancel unstake failed: ${result.error.code}`);
1408
- }
1409
- return result.value;
1410
- },
1411
- onSuccess: () => {
1412
- queryClient.invalidateQueries({ queryKey: ["stakingInfo"] });
1413
- queryClient.invalidateQueries({ queryKey: ["unstakingInfo"] });
1414
- queryClient.invalidateQueries({ queryKey: ["unstakingInfoWithPenalty"] });
1370
+ return useSafeMutation({
1371
+ mutationKey: ["staking", "cancelUnstake"],
1372
+ ...mutationOptions,
1373
+ mutationFn: async (vars) => unwrapResult(await sodax.staking.cancelUnstake({ ...vars, raw: false })),
1374
+ onSuccess: async (data, vars, ctx) => {
1375
+ const { params } = vars;
1376
+ queryClient.invalidateQueries({ queryKey: ["staking", "unstakingInfo", params.srcChainKey, params.srcAddress] });
1377
+ queryClient.invalidateQueries({
1378
+ queryKey: ["staking", "unstakingInfoWithPenalty", params.srcChainKey, params.srcAddress]
1379
+ });
1380
+ queryClient.invalidateQueries({ queryKey: ["staking", "info", params.srcChainKey, params.srcAddress] });
1381
+ await mutationOptions?.onSuccess?.(data, vars, ctx);
1415
1382
  }
1416
1383
  });
1417
1384
  }
1418
- function useStakingInfo(spokeProvider, refetchInterval = 5e3) {
1385
+ function useStakingInfo({ params, queryOptions } = {}) {
1419
1386
  const { sodax } = useSodaxContext();
1387
+ const srcAddress = params?.srcAddress;
1388
+ const srcChainKey = params?.srcChainKey;
1420
1389
  return useQuery({
1421
- queryKey: ["soda", "stakingInfo", spokeProvider?.chainConfig.chain.id],
1390
+ queryKey: ["staking", "info", srcChainKey, srcAddress],
1422
1391
  queryFn: async () => {
1423
- if (!spokeProvider) {
1424
- throw new Error("Spoke provider not found");
1425
- }
1426
- const result = await sodax.staking.getStakingInfoFromSpoke(spokeProvider);
1427
- if (!result.ok) {
1428
- throw new Error(`Failed to fetch staking info: ${result.error.code}`);
1392
+ if (!srcAddress || !srcChainKey) {
1393
+ throw new Error("srcAddress and srcChainKey are required");
1429
1394
  }
1395
+ const result = await sodax.staking.getStakingInfoFromSpoke(srcAddress, srcChainKey);
1396
+ if (!result.ok) throw result.error;
1430
1397
  return result.value;
1431
1398
  },
1432
- enabled: !!spokeProvider,
1433
- refetchInterval
1399
+ enabled: !!srcAddress && !!srcChainKey,
1400
+ refetchInterval: 5e3,
1401
+ ...queryOptions
1434
1402
  });
1435
1403
  }
1436
- function useUnstakingInfoWithPenalty(userAddress, spokeProvider, refetchInterval = 5e3) {
1404
+ function useUnstakingInfoWithPenalty({
1405
+ params,
1406
+ queryOptions
1407
+ } = {}) {
1437
1408
  const { sodax } = useSodaxContext();
1409
+ const srcAddress = params?.srcAddress;
1410
+ const srcChainKey = params?.srcChainKey;
1438
1411
  return useQuery({
1439
- queryKey: ["soda", "unstakingInfoWithPenalty", spokeProvider?.chainConfig.chain.id, userAddress],
1412
+ queryKey: ["staking", "unstakingInfoWithPenalty", srcChainKey, srcAddress],
1440
1413
  queryFn: async () => {
1441
- if (!spokeProvider) {
1442
- throw new Error("Spoke provider not found");
1443
- }
1444
- const penaltyResult = await sodax.staking.getUnstakingInfoWithPenalty(spokeProvider);
1445
- if (!penaltyResult.ok) {
1446
- throw new Error(`Failed to fetch unstaking info with penalty: ${penaltyResult.error.code}`);
1414
+ if (!srcAddress || !srcChainKey) {
1415
+ throw new Error("srcAddress and srcChainKey are required");
1447
1416
  }
1448
- return penaltyResult.value;
1417
+ const result = await sodax.staking.getUnstakingInfoWithPenalty(srcAddress, srcChainKey);
1418
+ if (!result.ok) throw result.error;
1419
+ return result.value;
1449
1420
  },
1450
- enabled: !!spokeProvider && !!userAddress,
1451
- refetchInterval
1421
+ enabled: !!srcAddress && !!srcChainKey,
1422
+ refetchInterval: 5e3,
1423
+ ...queryOptions
1452
1424
  });
1453
1425
  }
1454
- function useStakingConfig(refetchInterval = 3e4) {
1426
+ function useStakingConfig({
1427
+ queryOptions
1428
+ } = {}) {
1455
1429
  const { sodax } = useSodaxContext();
1456
1430
  return useQuery({
1457
- queryKey: ["soda", "stakingConfig"],
1431
+ queryKey: ["staking", "config"],
1458
1432
  queryFn: async () => {
1459
1433
  const result = await sodax.staking.getStakingConfig();
1460
- if (!result.ok) {
1461
- throw new Error(`Failed to fetch staking config: ${result.error.code}`);
1434
+ if (!result.ok) throw result.error;
1435
+ return result.value;
1436
+ },
1437
+ staleTime: Number.POSITIVE_INFINITY,
1438
+ ...queryOptions
1439
+ });
1440
+ }
1441
+ function useStakeRatio({
1442
+ params,
1443
+ queryOptions
1444
+ } = {}) {
1445
+ const { sodax } = useSodaxContext();
1446
+ const amount = params?.amount;
1447
+ return useQuery({
1448
+ queryKey: ["staking", "stakeRatio", amount?.toString()],
1449
+ queryFn: async () => {
1450
+ if (amount === void 0) {
1451
+ throw new Error("amount is required");
1462
1452
  }
1453
+ const result = await sodax.staking.getStakeRatio(amount);
1454
+ if (!result.ok) throw result.error;
1463
1455
  return result.value;
1464
1456
  },
1465
- refetchInterval
1457
+ enabled: amount !== void 0,
1458
+ refetchInterval: 1e4,
1459
+ ...queryOptions
1466
1460
  });
1467
1461
  }
1468
- function useStakeRatio(amount, refetchInterval = 1e4) {
1462
+ function useInstantUnstakeRatio({
1463
+ params,
1464
+ queryOptions
1465
+ } = {}) {
1469
1466
  const { sodax } = useSodaxContext();
1467
+ const amount = params?.amount;
1470
1468
  return useQuery({
1471
- queryKey: ["soda", "stakeRatio", amount?.toString()],
1469
+ queryKey: ["staking", "instantUnstakeRatio", amount?.toString()],
1472
1470
  queryFn: async () => {
1473
- if (!amount || amount <= 0n) {
1474
- throw new Error("Amount must be greater than 0");
1471
+ if (amount === void 0) {
1472
+ throw new Error("amount is required");
1475
1473
  }
1476
- if (!sodax?.staking) {
1477
- throw new Error("Staking service not available");
1474
+ const result = await sodax.staking.getInstantUnstakeRatio(amount);
1475
+ if (!result.ok) throw result.error;
1476
+ return result.value;
1477
+ },
1478
+ enabled: amount !== void 0,
1479
+ refetchInterval: 1e4,
1480
+ ...queryOptions
1481
+ });
1482
+ }
1483
+ function useConvertedAssets({
1484
+ params,
1485
+ queryOptions
1486
+ } = {}) {
1487
+ const { sodax } = useSodaxContext();
1488
+ const amount = params?.amount;
1489
+ return useQuery({
1490
+ queryKey: ["staking", "convertedAssets", amount?.toString()],
1491
+ queryFn: async () => {
1492
+ if (amount === void 0) {
1493
+ throw new Error("amount is required");
1478
1494
  }
1479
- const result = await sodax.staking.getStakeRatio(amount);
1480
- if (!result.ok) {
1481
- throw new Error(`Failed to fetch stake ratio: ${result.error.code}`);
1495
+ const result = await sodax.staking.getConvertedAssets(amount);
1496
+ if (!result.ok) throw result.error;
1497
+ return result.value;
1498
+ },
1499
+ enabled: amount !== void 0,
1500
+ refetchInterval: 1e4,
1501
+ ...queryOptions
1502
+ });
1503
+ }
1504
+ function useInstantUnstake({
1505
+ mutationOptions
1506
+ } = {}) {
1507
+ const { sodax } = useSodaxContext();
1508
+ const queryClient = useQueryClient();
1509
+ return useSafeMutation({
1510
+ mutationKey: ["staking", "instantUnstake"],
1511
+ ...mutationOptions,
1512
+ mutationFn: async (vars) => unwrapResult(await sodax.staking.instantUnstake({ ...vars, raw: false })),
1513
+ onSuccess: async (data, vars, ctx) => {
1514
+ const { params } = vars;
1515
+ queryClient.invalidateQueries({ queryKey: ["staking", "info", params.srcChainKey, params.srcAddress] });
1516
+ queryClient.invalidateQueries({ queryKey: ["staking", "instantUnstakeRatio"] });
1517
+ queryClient.invalidateQueries({ queryKey: ["staking", "allowance", params.srcChainKey, "instantUnstake"] });
1518
+ queryClient.invalidateQueries({ queryKey: ["shared", "xBalances", params.srcChainKey] });
1519
+ await mutationOptions?.onSuccess?.(data, vars, ctx);
1520
+ }
1521
+ });
1522
+ }
1523
+ function useUnstakeAllowance({
1524
+ params,
1525
+ queryOptions
1526
+ } = {}) {
1527
+ const { sodax } = useSodaxContext();
1528
+ const payload = params?.payload;
1529
+ return useQuery({
1530
+ queryKey: [
1531
+ "staking",
1532
+ "allowance",
1533
+ payload?.srcChainKey,
1534
+ "unstake",
1535
+ payload?.srcAddress,
1536
+ payload?.amount?.toString()
1537
+ ],
1538
+ queryFn: async () => {
1539
+ if (!payload) {
1540
+ throw new Error("Params are required");
1482
1541
  }
1542
+ const result = await sodax.staking.isAllowanceValid({
1543
+ params: { ...payload, action: "unstake" },
1544
+ raw: true
1545
+ });
1546
+ if (!result.ok) throw result.error;
1483
1547
  return result.value;
1484
1548
  },
1485
- enabled: !!amount && amount > 0n && !!sodax?.staking,
1486
- refetchInterval
1549
+ enabled: !!payload,
1550
+ refetchInterval: 5e3,
1551
+ gcTime: 0,
1552
+ ...queryOptions
1487
1553
  });
1488
1554
  }
1489
- function useInstantUnstakeRatio(amount, refetchInterval = 1e4) {
1555
+ function useUnstakeApprove({
1556
+ mutationOptions
1557
+ } = {}) {
1490
1558
  const { sodax } = useSodaxContext();
1491
- console.log("useInstantUnstakeRatio hook called with:", { amount: amount?.toString(), sodax: !!sodax });
1559
+ const queryClient = useQueryClient();
1560
+ return useSafeMutation({
1561
+ mutationKey: ["staking", "approve", "unstake"],
1562
+ ...mutationOptions,
1563
+ mutationFn: async ({ params, walletProvider }) => unwrapResult(
1564
+ await sodax.staking.approve({
1565
+ params: { ...params, action: "unstake" },
1566
+ raw: false,
1567
+ walletProvider
1568
+ })
1569
+ ),
1570
+ onSuccess: async (data, vars, ctx) => {
1571
+ queryClient.invalidateQueries({ queryKey: ["staking", "allowance", vars.params.srcChainKey, "unstake"] });
1572
+ await mutationOptions?.onSuccess?.(data, vars, ctx);
1573
+ }
1574
+ });
1575
+ }
1576
+ function useUnstakingInfo({
1577
+ params,
1578
+ queryOptions
1579
+ } = {}) {
1580
+ const { sodax } = useSodaxContext();
1581
+ const srcAddress = params?.srcAddress;
1582
+ const srcChainKey = params?.srcChainKey;
1492
1583
  return useQuery({
1493
- queryKey: ["soda", "instantUnstakeRatio", amount?.toString()],
1584
+ queryKey: ["staking", "unstakingInfo", srcChainKey, srcAddress],
1494
1585
  queryFn: async () => {
1495
- console.log("useInstantUnstakeRatio queryFn called with amount:", amount?.toString());
1496
- if (!amount || amount <= 0n) {
1497
- throw new Error("Amount must be greater than 0");
1586
+ if (!srcAddress || !srcChainKey) {
1587
+ throw new Error("srcAddress and srcChainKey are required");
1498
1588
  }
1499
- if (!sodax?.staking) {
1500
- throw new Error("Staking service not available");
1589
+ const result = await sodax.staking.getUnstakingInfo(srcAddress, srcChainKey);
1590
+ if (!result.ok) throw result.error;
1591
+ return result.value;
1592
+ },
1593
+ enabled: !!srcAddress && !!srcChainKey,
1594
+ refetchInterval: 5e3,
1595
+ ...queryOptions
1596
+ });
1597
+ }
1598
+ function useInstantUnstakeApprove({
1599
+ mutationOptions
1600
+ } = {}) {
1601
+ const { sodax } = useSodaxContext();
1602
+ const queryClient = useQueryClient();
1603
+ return useSafeMutation({
1604
+ mutationKey: ["staking", "approve", "instantUnstake"],
1605
+ ...mutationOptions,
1606
+ mutationFn: async ({ params, walletProvider }) => unwrapResult(
1607
+ await sodax.staking.approve({
1608
+ params: { ...params, action: "instantUnstake" },
1609
+ raw: false,
1610
+ walletProvider
1611
+ })
1612
+ ),
1613
+ onSuccess: async (data, vars, ctx) => {
1614
+ queryClient.invalidateQueries({
1615
+ queryKey: ["staking", "allowance", vars.params.srcChainKey, "instantUnstake"]
1616
+ });
1617
+ await mutationOptions?.onSuccess?.(data, vars, ctx);
1618
+ }
1619
+ });
1620
+ }
1621
+ function useInstantUnstakeAllowance({
1622
+ params,
1623
+ queryOptions
1624
+ } = {}) {
1625
+ const { sodax } = useSodaxContext();
1626
+ const payload = params?.payload;
1627
+ return useQuery({
1628
+ queryKey: [
1629
+ "staking",
1630
+ "allowance",
1631
+ payload?.srcChainKey,
1632
+ "instantUnstake",
1633
+ payload?.srcAddress,
1634
+ payload?.amount?.toString()
1635
+ ],
1636
+ queryFn: async () => {
1637
+ if (!payload) {
1638
+ throw new Error("Params are required");
1501
1639
  }
1502
- const result = await sodax.staking.getInstantUnstakeRatio(amount);
1503
- if (!result.ok) {
1504
- throw new Error(`Failed to fetch instant unstake ratio: ${result.error.code}`);
1640
+ const result = await sodax.staking.isAllowanceValid({
1641
+ params: { ...payload, action: "instantUnstake" },
1642
+ raw: true
1643
+ });
1644
+ if (!result.ok) throw result.error;
1645
+ return result.value;
1646
+ },
1647
+ enabled: !!payload,
1648
+ refetchInterval: 5e3,
1649
+ gcTime: 0,
1650
+ ...queryOptions
1651
+ });
1652
+ }
1653
+ function useFetchAssetsBalances({
1654
+ params,
1655
+ queryOptions
1656
+ } = {}) {
1657
+ const { sodax } = useSodaxContext();
1658
+ const queryAddress = params?.queryAddress;
1659
+ return useQuery({
1660
+ queryKey: ["partner", "feeClaim", "assetsBalances", queryAddress],
1661
+ queryFn: async () => {
1662
+ if (!queryAddress) {
1663
+ throw new Error("queryAddress is required");
1505
1664
  }
1665
+ const result = await sodax.partners.feeClaim.fetchAssetsBalances(queryAddress);
1666
+ if (!result.ok) throw result.error;
1506
1667
  return result.value;
1507
1668
  },
1508
- enabled: !!amount && amount > 0n && !!sodax?.staking,
1509
- refetchInterval
1669
+ enabled: !!queryAddress,
1670
+ ...queryOptions
1510
1671
  });
1511
1672
  }
1512
- function useConvertedAssets(amount, refetchInterval = 1e4) {
1673
+ function useGetAutoSwapPreferences({
1674
+ params,
1675
+ queryOptions
1676
+ } = {}) {
1513
1677
  const { sodax } = useSodaxContext();
1678
+ const queryAddress = params?.queryAddress;
1514
1679
  return useQuery({
1515
- queryKey: ["soda", "convertedAssets", amount?.toString()],
1680
+ queryKey: ["partner", "feeClaim", "autoSwapPreferences", queryAddress],
1516
1681
  queryFn: async () => {
1517
- if (!amount || amount <= 0n) {
1518
- throw new Error("Amount must be greater than 0");
1682
+ if (!queryAddress) {
1683
+ throw new Error("queryAddress is required");
1519
1684
  }
1520
- const result = await sodax.staking.getConvertedAssets(amount);
1521
- if (!result.ok) {
1522
- throw new Error(`Failed to fetch converted assets: ${result.error.code}`);
1685
+ const result = await sodax.partners.feeClaim.getAutoSwapPreferences(queryAddress);
1686
+ if (!result.ok) throw result.error;
1687
+ return result.value;
1688
+ },
1689
+ enabled: !!queryAddress,
1690
+ ...queryOptions
1691
+ });
1692
+ }
1693
+ function useIsTokenApproved({
1694
+ params,
1695
+ queryOptions
1696
+ } = {}) {
1697
+ const { sodax } = useSodaxContext();
1698
+ const payload = params?.payload;
1699
+ return useQuery({
1700
+ queryKey: ["partner", "feeClaim", "isTokenApproved", payload?.srcChainKey, payload?.srcAddress, payload?.token],
1701
+ queryFn: async () => {
1702
+ if (!payload) {
1703
+ throw new Error("params are required");
1523
1704
  }
1705
+ const result = await sodax.partners.feeClaim.isTokenApproved(payload);
1706
+ if (!result.ok) throw result.error;
1524
1707
  return result.value;
1525
1708
  },
1526
- enabled: !!amount && amount > 0n && !!sodax?.staking,
1527
- refetchInterval
1709
+ enabled: !!payload,
1710
+ refetchInterval: 5e3,
1711
+ gcTime: 0,
1712
+ ...queryOptions
1528
1713
  });
1529
1714
  }
1530
- function useInstantUnstake(spokeProvider) {
1715
+ function useApproveToken({
1716
+ mutationOptions
1717
+ } = {}) {
1531
1718
  const { sodax } = useSodaxContext();
1532
- return useMutation({
1533
- mutationFn: async (params) => {
1534
- if (!spokeProvider) {
1535
- throw new Error("spokeProvider is not found");
1536
- }
1537
- const result = await sodax.staking.instantUnstake({ ...params, action: "instantUnstake" }, spokeProvider);
1538
- if (!result.ok) {
1539
- throw new Error(`Instant unstake failed: ${result.error.code}`);
1540
- }
1541
- return result.value;
1542
- },
1543
- onError: (error) => {
1544
- console.error("Instant unstake error:", error);
1719
+ const queryClient = useQueryClient();
1720
+ return useSafeMutation({
1721
+ mutationKey: ["partner", "approveToken"],
1722
+ ...mutationOptions,
1723
+ mutationFn: async (vars) => unwrapResult(await sodax.partners.feeClaim.approveToken({ ...vars, raw: false })),
1724
+ onSuccess: async (data, vars, ctx) => {
1725
+ const { params } = vars;
1726
+ queryClient.invalidateQueries({
1727
+ queryKey: ["partner", "feeClaim", "isTokenApproved", params.srcChainKey, params.srcAddress, params.token]
1728
+ });
1729
+ await mutationOptions?.onSuccess?.(data, vars, ctx);
1545
1730
  }
1546
1731
  });
1547
1732
  }
1548
- function useUnstakeAllowance(params, spokeProvider) {
1733
+ function useSetSwapPreference({
1734
+ mutationOptions
1735
+ } = {}) {
1549
1736
  const { sodax } = useSodaxContext();
1550
- return useQuery({
1551
- queryKey: ["soda", "unstakeAllowance", params, spokeProvider?.chainConfig.chain.id],
1552
- queryFn: async () => {
1553
- if (!params || !spokeProvider) {
1554
- return false;
1555
- }
1556
- const result = await sodax.staking.isAllowanceValid({
1557
- params: { ...params, action: "unstake" },
1558
- spokeProvider
1737
+ const queryClient = useQueryClient();
1738
+ return useSafeMutation({
1739
+ mutationKey: ["partner", "setSwapPreference"],
1740
+ ...mutationOptions,
1741
+ mutationFn: async (vars) => unwrapResult(await sodax.partners.feeClaim.setSwapPreference({ ...vars, raw: false })),
1742
+ onSuccess: async (data, vars, ctx) => {
1743
+ queryClient.invalidateQueries({
1744
+ queryKey: ["partner", "feeClaim", "autoSwapPreferences", vars.params.srcAddress]
1559
1745
  });
1560
- if (!result.ok) {
1561
- console.error(`Unstake allowance check failed: ${result.error.code}, error: ${result.error.error}`);
1562
- throw new Error(`Unstake allowance check failed: ${result.error.code}`);
1563
- }
1564
- return result.value;
1565
- },
1566
- enabled: !!params && !!spokeProvider,
1567
- refetchInterval: 5e3
1568
- // Refetch every 5 seconds
1746
+ await mutationOptions?.onSuccess?.(data, vars, ctx);
1747
+ }
1569
1748
  });
1570
1749
  }
1571
- function useUnstakeApprove(spokeProvider) {
1750
+ function useFeeClaimSwap({
1751
+ mutationOptions
1752
+ } = {}) {
1572
1753
  const { sodax } = useSodaxContext();
1573
- return useMutation({
1574
- mutationFn: async (params) => {
1575
- console.log("useUnstakeApprove called with params:", params);
1576
- if (!spokeProvider) {
1577
- throw new Error("Spoke provider not found");
1578
- }
1579
- const result = await sodax.staking.approve({
1580
- params: { ...params, action: "unstake" },
1581
- spokeProvider
1754
+ const queryClient = useQueryClient();
1755
+ return useSafeMutation({
1756
+ mutationKey: ["partner", "feeClaimSwap"],
1757
+ ...mutationOptions,
1758
+ mutationFn: async (vars) => unwrapResult(await sodax.partners.feeClaim.swap({ ...vars, raw: false })),
1759
+ onSuccess: async (data, vars, ctx) => {
1760
+ queryClient.invalidateQueries({
1761
+ queryKey: ["partner", "feeClaim", "assetsBalances", vars.params.srcAddress]
1582
1762
  });
1583
- if (!result.ok) {
1584
- throw new Error(`Unstake approval failed: ${result.error.code}`);
1585
- }
1586
- return result.value;
1763
+ await mutationOptions?.onSuccess?.(data, vars, ctx);
1587
1764
  }
1588
1765
  });
1589
1766
  }
1590
- function useUnstakingInfo(userAddress, spokeProvider, refetchInterval = 5e3) {
1767
+ function useHubAssetBalances({
1768
+ params,
1769
+ queryOptions
1770
+ } = {}) {
1591
1771
  const { sodax } = useSodaxContext();
1772
+ const chainKey = params?.chainKey;
1773
+ const srcAddress = params?.srcAddress;
1592
1774
  return useQuery({
1593
- queryKey: ["soda", "unstakingInfoWithPenalty", spokeProvider?.chainConfig.chain.id, userAddress],
1775
+ queryKey: ["recovery", "hubAssetBalances", chainKey, srcAddress],
1594
1776
  queryFn: async () => {
1595
- if (!spokeProvider || !userAddress) {
1596
- throw new Error("Spoke provider or user address not found");
1597
- }
1598
- const result = await sodax.staking.getUnstakingInfo(spokeProvider);
1599
- if (!result.ok) {
1600
- throw new Error(`Failed to fetch unstaking info: ${result.error.code}`);
1777
+ if (!chainKey || !srcAddress) {
1778
+ throw new Error("chainKey and srcAddress are required");
1601
1779
  }
1780
+ const result = await sodax.recovery.fetchHubAssetBalances({ chainKey, srcAddress });
1781
+ if (!result.ok) throw result.error;
1602
1782
  return result.value;
1603
1783
  },
1604
- enabled: !!spokeProvider && !!userAddress,
1605
- refetchInterval
1784
+ enabled: !!chainKey && !!srcAddress,
1785
+ staleTime: 1e4,
1786
+ ...queryOptions
1606
1787
  });
1607
1788
  }
1608
- function useInstantUnstakeApprove(spokeProvider) {
1789
+ function useWithdrawHubAsset({
1790
+ mutationOptions
1791
+ } = {}) {
1609
1792
  const { sodax } = useSodaxContext();
1610
- return useMutation({
1611
- mutationFn: async (params) => {
1612
- console.log("useInstantUnstakeApprove called with params:", params);
1613
- if (!spokeProvider) {
1614
- throw new Error("Spoke provider not found");
1615
- }
1616
- const result = await sodax.staking.approve({
1617
- params: { ...params, action: "instantUnstake" },
1618
- spokeProvider
1793
+ const queryClient = useQueryClient();
1794
+ return useSafeMutation({
1795
+ mutationKey: ["recovery", "withdrawHubAsset"],
1796
+ ...mutationOptions,
1797
+ mutationFn: async (vars) => unwrapResult(await sodax.recovery.withdrawHubAsset({ ...vars, raw: false })),
1798
+ onSuccess: async (data, vars, ctx) => {
1799
+ const { params } = vars;
1800
+ queryClient.invalidateQueries({
1801
+ queryKey: ["recovery", "hubAssetBalances", params.srcChainKey, params.srcAddress]
1619
1802
  });
1620
- if (!result.ok) {
1621
- throw new Error(`Instant unstake approval failed: ${result.error.code}`);
1622
- }
1623
- return result.value;
1803
+ queryClient.invalidateQueries({ queryKey: ["shared", "xBalances", params.srcChainKey] });
1804
+ await mutationOptions?.onSuccess?.(data, vars, ctx);
1624
1805
  }
1625
1806
  });
1626
1807
  }
1627
- function useInstantUnstakeAllowance(params, spokeProvider) {
1808
+ function useMigrateIcxToSoda({
1809
+ mutationOptions
1810
+ } = {}) {
1628
1811
  const { sodax } = useSodaxContext();
1629
- return useQuery({
1630
- queryKey: ["soda", "instantUnstakeAllowance", params, spokeProvider?.chainConfig.chain.id],
1631
- queryFn: async () => {
1632
- if (!params || !spokeProvider) {
1633
- return false;
1634
- }
1635
- const result = await sodax.staking.isAllowanceValid({
1636
- params: { ...params, action: "instantUnstake" },
1637
- spokeProvider
1638
- });
1639
- if (!result.ok) {
1640
- console.error(`Unstake allowance check failed: ${result.error.code}, error: ${result.error.error}`);
1641
- throw new Error(`Unstake allowance check failed: ${result.error.code}`);
1642
- }
1643
- return result.value;
1644
- },
1645
- enabled: !!params && !!spokeProvider,
1646
- refetchInterval: 5e3
1647
- // Refetch every 5 seconds
1812
+ const queryClient = useQueryClient();
1813
+ return useSafeMutation({
1814
+ mutationKey: ["migrate", "icxToSoda"],
1815
+ ...mutationOptions,
1816
+ mutationFn: async (vars) => unwrapResult(await sodax.migration.migrateIcxToSoda({ ...vars, raw: false })),
1817
+ onSuccess: async (data, vars, ctx) => {
1818
+ queryClient.invalidateQueries({ queryKey: ["migrate", "allowance"] });
1819
+ queryClient.invalidateQueries({ queryKey: ["shared", "xBalances", vars.params.srcChainKey] });
1820
+ queryClient.invalidateQueries({ queryKey: ["shared", "xBalances", ChainKeys.SONIC_MAINNET] });
1821
+ await mutationOptions?.onSuccess?.(data, vars, ctx);
1822
+ }
1648
1823
  });
1649
1824
  }
1650
-
1651
- // src/hooks/migrate/types.ts
1652
- var MIGRATION_MODE_ICX_SODA = "icxsoda";
1653
- var MIGRATION_MODE_BNUSD = "bnusd";
1654
-
1655
- // src/hooks/migrate/useMigrate.tsx
1656
- function useMigrate(spokeProvider) {
1657
- const { sodax } = useSodaxContext();
1658
- return useMutation({
1659
- mutationFn: async (params) => {
1660
- const { token, amount, migrationMode = MIGRATION_MODE_ICX_SODA, toToken, destinationAddress } = params;
1661
- const amountToMigrate = parseUnits(amount ?? "0", token?.decimals ?? 0);
1662
- if (!spokeProvider) {
1663
- throw new Error("Spoke provider not found");
1664
- }
1665
- if (migrationMode === MIGRATION_MODE_ICX_SODA) {
1666
- if (token?.xChainId === ICON_MAINNET_CHAIN_ID) {
1667
- const params2 = {
1668
- address: spokeChainConfig[ICON_MAINNET_CHAIN_ID].nativeToken,
1669
- amount: amountToMigrate,
1670
- to: destinationAddress
1671
- };
1672
- const result2 = await sodax.migration.migrateIcxToSoda(params2, spokeProvider, 3e4);
1673
- if (result2.ok) {
1674
- const [spokeTxHash, hubTxHash] = result2.value;
1675
- return { spokeTxHash, hubTxHash };
1676
- }
1677
- throw new Error("ICX to SODA migration failed. Please try again.");
1678
- }
1679
- const revertParams = {
1680
- amount: amountToMigrate,
1681
- to: destinationAddress
1682
- };
1683
- const result = await sodax.migration.revertMigrateSodaToIcx(
1684
- revertParams,
1685
- spokeProvider,
1686
- 3e4
1687
- );
1688
- if (result.ok) {
1689
- const [hubTxHash, spokeTxHash] = result.value;
1690
- return { spokeTxHash, hubTxHash };
1691
- }
1692
- throw new Error("SODA to ICX migration failed. Please try again.");
1693
- }
1694
- if (migrationMode === MIGRATION_MODE_BNUSD) {
1695
- const params2 = {
1696
- srcChainId: token?.xChainId,
1697
- dstChainId: toToken?.xChainId,
1698
- srcbnUSD: token?.address,
1699
- dstbnUSD: toToken?.address,
1700
- amount: amountToMigrate,
1701
- to: destinationAddress
1702
- };
1703
- const result = await sodax.migration.migratebnUSD(params2, spokeProvider, 3e4);
1704
- if (result.ok) {
1705
- const [spokeTxHash, hubTxHash] = result.value;
1706
- return { spokeTxHash, hubTxHash };
1707
- }
1708
- const errorMessage = isLegacybnUSDToken(token?.address) ? "bnUSD migration failed. Please try again." : "bnUSD reverse migration failed. Please try again.";
1709
- throw new Error(errorMessage);
1710
- }
1711
- throw new Error("Invalid migration mode");
1825
+ function useRevertMigrateSodaToIcx({
1826
+ mutationOptions
1827
+ } = {}) {
1828
+ const { sodax } = useSodaxContext();
1829
+ const queryClient = useQueryClient();
1830
+ return useSafeMutation({
1831
+ mutationKey: ["migrate", "revertSodaToIcx"],
1832
+ ...mutationOptions,
1833
+ mutationFn: async (vars) => unwrapResult(await sodax.migration.revertMigrateSodaToIcx({ ...vars, raw: false })),
1834
+ onSuccess: async (data, vars, ctx) => {
1835
+ queryClient.invalidateQueries({ queryKey: ["migrate", "allowance"] });
1836
+ queryClient.invalidateQueries({ queryKey: ["shared", "xBalances", vars.params.srcChainKey] });
1837
+ queryClient.invalidateQueries({ queryKey: ["shared", "xBalances", ChainKeys.ICON_MAINNET] });
1838
+ await mutationOptions?.onSuccess?.(data, vars, ctx);
1712
1839
  }
1713
1840
  });
1714
1841
  }
1715
- function useMigrationAllowance(params, spokeProvider) {
1842
+ function useMigratebnUSD({
1843
+ mutationOptions
1844
+ } = {}) {
1716
1845
  const { sodax } = useSodaxContext();
1717
- return useQuery({
1718
- queryKey: ["migration-allowance", params],
1719
- queryFn: async () => {
1720
- if (!spokeProvider || !params) {
1721
- return false;
1722
- }
1723
- const { token, amount, migrationMode = MIGRATION_MODE_ICX_SODA, toToken, destinationAddress } = params;
1724
- if (token?.xChainId === ICON_MAINNET_CHAIN_ID) {
1725
- return true;
1726
- }
1727
- if (!spokeProvider) throw new Error("Spoke provider is required");
1728
- const amountToMigrate = parseUnits(amount ?? "0", token?.decimals ?? 0);
1729
- let migrationParams;
1730
- if (migrationMode === MIGRATION_MODE_ICX_SODA) {
1731
- migrationParams = {
1732
- amount: amountToMigrate,
1733
- to: destinationAddress
1734
- };
1735
- } else {
1736
- if (!toToken) throw new Error("Destination token is required for bnUSD migration");
1737
- migrationParams = {
1738
- srcChainId: token?.xChainId,
1739
- dstChainId: toToken?.xChainId,
1740
- srcbnUSD: token?.address,
1741
- dstbnUSD: toToken?.address,
1742
- amount: amountToMigrate,
1743
- to: destinationAddress
1744
- };
1745
- }
1746
- const allowance = await sodax.migration.isAllowanceValid(migrationParams, "revert", spokeProvider);
1747
- if (allowance.ok) {
1748
- return allowance.value;
1749
- }
1750
- return false;
1751
- },
1752
- enabled: !!spokeProvider && !!params,
1753
- refetchInterval: 2e3
1846
+ const queryClient = useQueryClient();
1847
+ return useSafeMutation({
1848
+ mutationKey: ["migrate", "bnUSD"],
1849
+ ...mutationOptions,
1850
+ mutationFn: async (vars) => unwrapResult(await sodax.migration.migratebnUSD({ ...vars, raw: false })),
1851
+ onSuccess: async (data, vars, ctx) => {
1852
+ const { params } = vars;
1853
+ queryClient.invalidateQueries({ queryKey: ["migrate", "allowance"] });
1854
+ queryClient.invalidateQueries({ queryKey: ["shared", "xBalances", params.srcChainKey] });
1855
+ queryClient.invalidateQueries({ queryKey: ["shared", "xBalances", params.dstChainKey] });
1856
+ await mutationOptions?.onSuccess?.(data, vars, ctx);
1857
+ }
1754
1858
  });
1755
1859
  }
1756
- function useMigrationApprove(params, spokeProvider) {
1860
+ function useMigrateBaln({
1861
+ mutationOptions
1862
+ } = {}) {
1757
1863
  const { sodax } = useSodaxContext();
1758
- const [isLoading, setIsLoading] = useState(false);
1759
- const [error, setError] = useState(null);
1760
- const [isApproved, setIsApproved] = useState(false);
1761
1864
  const queryClient = useQueryClient();
1762
- const prevTokenAddress = useRef(void 0);
1763
- const prevAmount = useRef(void 0);
1764
- useEffect(() => {
1765
- if (prevTokenAddress.current !== params?.token?.address || prevAmount.current !== params?.amount) {
1766
- setIsApproved(false);
1767
- prevTokenAddress.current = params?.token?.address;
1768
- prevAmount.current = params?.amount;
1865
+ return useSafeMutation({
1866
+ mutationKey: ["migrate", "baln"],
1867
+ ...mutationOptions,
1868
+ mutationFn: async (vars) => unwrapResult(await sodax.migration.migrateBaln({ ...vars, raw: false })),
1869
+ onSuccess: async (data, vars, ctx) => {
1870
+ queryClient.invalidateQueries({ queryKey: ["migrate", "allowance"] });
1871
+ queryClient.invalidateQueries({ queryKey: ["shared", "xBalances", ChainKeys.ICON_MAINNET] });
1872
+ queryClient.invalidateQueries({ queryKey: ["shared", "xBalances", ChainKeys.SONIC_MAINNET] });
1873
+ queryClient.invalidateQueries({ queryKey: ["staking", "info"] });
1874
+ await mutationOptions?.onSuccess?.(data, vars, ctx);
1769
1875
  }
1770
- }, [params?.token?.address, params?.amount]);
1771
- const approve = useCallback(
1772
- async ({ params: approveParams }) => {
1773
- try {
1774
- setIsLoading(true);
1775
- setError(null);
1776
- if (!spokeProvider) {
1777
- throw new Error("Spoke provider not found");
1778
- }
1779
- if (!approveParams) {
1780
- throw new Error("Migration intent not found");
1781
- }
1782
- const { token, amount, migrationMode = MIGRATION_MODE_ICX_SODA, toToken, destinationAddress } = approveParams;
1783
- const amountToMigrate = parseUnits(amount ?? "0", token?.decimals ?? 0);
1784
- let result;
1785
- if (migrationMode === MIGRATION_MODE_ICX_SODA) {
1786
- const revertParams = {
1787
- amount: amountToMigrate,
1788
- to: destinationAddress
1789
- };
1790
- result = await sodax.migration.approve(revertParams, "revert", spokeProvider, false);
1791
- } else if (migrationMode === MIGRATION_MODE_BNUSD) {
1792
- if (!toToken) throw new Error("Destination token is required for bnUSD migration");
1793
- const migrationParams = {
1794
- srcChainId: token?.xChainId,
1795
- dstChainId: toToken?.xChainId,
1796
- srcbnUSD: token?.address,
1797
- dstbnUSD: toToken?.address,
1798
- amount: amountToMigrate,
1799
- to: destinationAddress
1800
- };
1801
- result = await sodax.migration.approve(migrationParams, "revert", spokeProvider, false);
1802
- } else {
1803
- throw new Error("Invalid migration mode");
1804
- }
1805
- if (!result.ok) {
1806
- throw new Error("Failed to approve tokens");
1807
- }
1808
- setIsApproved(true);
1809
- queryClient.invalidateQueries({ queryKey: ["migration-allowance", params] });
1810
- return result.ok;
1811
- } catch (err) {
1812
- const error2 = err instanceof Error ? err : new Error("An unknown error occurred");
1813
- setError(error2);
1814
- throw error2;
1815
- } finally {
1816
- setIsLoading(false);
1817
- }
1818
- },
1819
- [spokeProvider, sodax, queryClient, params]
1820
- );
1821
- const resetError = useCallback(() => {
1822
- setError(null);
1823
- }, []);
1824
- return {
1825
- approve,
1826
- isLoading,
1827
- error,
1828
- resetError,
1829
- isApproved
1830
- };
1876
+ });
1831
1877
  }
1832
- function usePools(params) {
1878
+ function useMigrationApprove({
1879
+ mutationOptions
1880
+ } = {}) {
1833
1881
  const { sodax } = useSodaxContext();
1834
- const defaultQueryOptions = {
1835
- queryKey: ["dex", "pools"],
1836
- staleTime: Number.POSITIVE_INFINITY
1837
- // Pools list is static, cache indefinitely
1838
- };
1839
- const queryOptions = { ...defaultQueryOptions, ...params?.queryOptions };
1882
+ const queryClient = useQueryClient();
1883
+ return useSafeMutation({
1884
+ mutationKey: ["migrate", "approve"],
1885
+ ...mutationOptions,
1886
+ mutationFn: async ({ action, ...actionParams }) => unwrapResult(
1887
+ await sodax.migration.approve(
1888
+ { ...actionParams, raw: false },
1889
+ action
1890
+ )
1891
+ ),
1892
+ onSuccess: async (data, vars, ctx) => {
1893
+ queryClient.invalidateQueries({ queryKey: ["migrate", "allowance"] });
1894
+ await mutationOptions?.onSuccess?.(data, vars, ctx);
1895
+ }
1896
+ });
1897
+ }
1898
+ var REFETCH_INTERVAL_MS2 = 2e3;
1899
+ function useMigrationAllowance({
1900
+ params,
1901
+ queryOptions
1902
+ } = {}) {
1903
+ const { sodax } = useSodaxContext();
1904
+ const migrationParams = params?.params;
1905
+ const action = params?.action;
1840
1906
  return useQuery({
1841
- ...queryOptions,
1907
+ // Extract the (chain, owner, token, amount) tuple that actually scopes the allowance —
1908
+ // raw-object keys break per Rule 4 (bigints) and churn on every render.
1909
+ queryKey: [
1910
+ "migrate",
1911
+ "allowance",
1912
+ action,
1913
+ migrationParams?.srcChainKey,
1914
+ migrationParams?.srcAddress,
1915
+ migrationParams && "srcbnUSD" in migrationParams ? migrationParams.srcbnUSD : void 0,
1916
+ migrationParams?.amount?.toString()
1917
+ ],
1842
1918
  queryFn: async () => {
1843
- return sodax.dex.clService.getPools();
1844
- }
1919
+ if (!migrationParams || !action) return false;
1920
+ const result = await sodax.migration.isAllowanceValid(migrationParams, action);
1921
+ return result.ok ? result.value : false;
1922
+ },
1923
+ enabled: !!migrationParams && !!action,
1924
+ refetchInterval: REFETCH_INTERVAL_MS2,
1925
+ ...queryOptions
1845
1926
  });
1846
1927
  }
1847
- function usePoolData({
1848
- poolKey,
1849
- queryOptions = {
1850
- queryKey: ["dex", "poolData", poolKey],
1851
- enabled: poolKey !== null,
1852
- staleTime: 1e4,
1853
- refetchInterval: 3e4
1854
- }
1855
- }) {
1928
+ function usePools({ queryOptions } = {}) {
1856
1929
  const { sodax } = useSodaxContext();
1857
1930
  return useQuery({
1858
- ...queryOptions,
1931
+ queryKey: ["dex", "pools"],
1932
+ queryFn: () => sodax.dex.clService.getPools(),
1933
+ staleTime: Number.POSITIVE_INFINITY,
1934
+ ...queryOptions
1935
+ });
1936
+ }
1937
+ function usePoolData({ params, queryOptions } = {}) {
1938
+ const { sodax } = useSodaxContext();
1939
+ const poolKey = params?.poolKey ?? null;
1940
+ return useQuery({
1941
+ queryKey: ["dex", "poolData", poolKey],
1859
1942
  queryFn: async () => {
1860
1943
  if (!poolKey) {
1861
1944
  throw new Error("Pool key is required");
1862
1945
  }
1863
- return await sodax.dex.clService.getPoolData(poolKey, sodax.hubProvider.publicClient);
1946
+ const result = await sodax.dex.clService.getPoolData(poolKey, sodax.hubProvider.publicClient);
1947
+ if (!result.ok) throw result.error;
1948
+ return result.value;
1864
1949
  },
1865
- enabled: poolKey !== null
1950
+ enabled: poolKey !== null,
1951
+ staleTime: 1e4,
1952
+ refetchInterval: 3e4,
1953
+ ...queryOptions
1866
1954
  });
1867
1955
  }
1868
1956
  function usePoolBalances({
1869
- poolData,
1870
- poolKey,
1871
- spokeProvider,
1872
- enabled = true,
1873
- queryOptions = {
1874
- queryKey: [
1875
- "dex",
1876
- "poolBalances",
1877
- poolData?.poolKey,
1878
- spokeProvider?.chainConfig.chain.id
1879
- ],
1880
- enabled: enabled && poolData !== null && poolKey !== null && spokeProvider !== null,
1881
- staleTime: 5e3,
1882
- // Consider data stale after 5 seconds
1883
- refetchInterval: 1e4
1884
- // Refetch every 10 seconds
1885
- }
1886
- }) {
1957
+ params,
1958
+ queryOptions
1959
+ } = {}) {
1887
1960
  const { sodax } = useSodaxContext();
1961
+ const poolData = params?.poolData ?? null;
1962
+ const poolKey = params?.poolKey ?? null;
1963
+ const spokeChainKey = params?.spokeChainKey;
1964
+ const userAddress = params?.userAddress;
1888
1965
  return useQuery({
1889
- ...queryOptions,
1966
+ queryKey: ["dex", "poolBalances", poolData?.poolId, spokeChainKey, userAddress],
1890
1967
  queryFn: async () => {
1891
- if (!poolData || !spokeProvider || !poolKey) {
1892
- throw new Error("Pool data, pool key, and spoke provider are required");
1893
- }
1894
- const [balance0, balance1] = await Promise.all([
1895
- sodax.dex.assetService.getDeposit(poolData.token0.address, spokeProvider),
1896
- sodax.dex.assetService.getDeposit(poolData.token1.address, spokeProvider)
1968
+ if (!poolData || !poolKey || !spokeChainKey || !userAddress) {
1969
+ throw new Error("poolData, poolKey, spokeChainKey, and userAddress are required");
1970
+ }
1971
+ const hubWallet = await sodax.hubProvider.getUserHubWalletAddress(userAddress, spokeChainKey);
1972
+ const [token0Balance, token1Balance] = await Promise.all([
1973
+ sodax.hubProvider.publicClient.readContract({
1974
+ address: poolData.token0.address,
1975
+ abi: erc20Abi,
1976
+ functionName: "balanceOf",
1977
+ args: [hubWallet]
1978
+ }),
1979
+ sodax.hubProvider.publicClient.readContract({
1980
+ address: poolData.token1.address,
1981
+ abi: erc20Abi,
1982
+ functionName: "balanceOf",
1983
+ args: [hubWallet]
1984
+ })
1897
1985
  ]);
1898
- return {
1899
- token0Balance: balance0,
1900
- token1Balance: balance1
1901
- };
1902
- }
1986
+ return { token0Balance, token1Balance };
1987
+ },
1988
+ enabled: !!poolData && !!poolKey && !!spokeChainKey && !!userAddress,
1989
+ staleTime: 5e3,
1990
+ refetchInterval: 1e4,
1991
+ ...queryOptions
1903
1992
  });
1904
1993
  }
1905
1994
  function usePositionInfo({
1906
- tokenId,
1907
- poolKey,
1908
- queryOptions = {
1909
- queryKey: ["dex", "positionInfo", tokenId, poolKey],
1910
- enabled: tokenId !== null && poolKey !== null && tokenId !== "",
1911
- staleTime: 1e4
1912
- // Consider data stale after 10 seconds
1913
- }
1914
- }) {
1995
+ params,
1996
+ queryOptions
1997
+ } = {}) {
1915
1998
  const { sodax } = useSodaxContext();
1999
+ const tokenId = params?.tokenId ?? null;
2000
+ const poolKey = params?.poolKey ?? null;
1916
2001
  return useQuery({
2002
+ queryKey: ["dex", "positionInfo", tokenId, poolKey],
1917
2003
  queryFn: async () => {
1918
2004
  if (!tokenId || !poolKey) {
1919
2005
  throw new Error("Token ID and pool key are required");
1920
2006
  }
1921
- const tokenIdBigInt = BigInt(tokenId);
1922
- const publicClient = sodax.hubProvider.publicClient;
1923
- const info = await sodax.dex.clService.getPositionInfo(tokenIdBigInt, publicClient);
2007
+ const infoResult = await sodax.dex.clService.getPositionInfo(BigInt(tokenId), sodax.hubProvider.publicClient);
2008
+ if (!infoResult.ok) throw infoResult.error;
2009
+ const info = infoResult.value;
1924
2010
  const isValid = info.poolKey.currency0.toLowerCase() === poolKey.currency0.toLowerCase() && info.poolKey.currency1.toLowerCase() === poolKey.currency1.toLowerCase() && info.poolKey.fee === poolKey.fee;
1925
- return {
1926
- positionInfo: info,
1927
- isValid
1928
- };
2011
+ return { positionInfo: info, isValid };
1929
2012
  },
2013
+ enabled: tokenId !== null && tokenId !== "" && poolKey !== null,
2014
+ staleTime: 1e4,
1930
2015
  ...queryOptions
1931
2016
  });
1932
2017
  }
1933
- function useDexDeposit() {
2018
+ function useDexDeposit({
2019
+ mutationOptions
2020
+ } = {}) {
1934
2021
  const { sodax } = useSodaxContext();
1935
2022
  const queryClient = useQueryClient();
1936
- return useMutation({
1937
- mutationFn: async ({ params, spokeProvider }) => {
1938
- if (!spokeProvider) {
1939
- throw new Error("Spoke provider is required");
1940
- }
1941
- if (!params) {
1942
- throw new Error("Deposit params are required");
1943
- }
1944
- const depositResult = await sodax.dex.assetService.deposit({
1945
- params,
1946
- spokeProvider
2023
+ return useSafeMutation({
2024
+ mutationKey: ["dex", "deposit"],
2025
+ ...mutationOptions,
2026
+ mutationFn: async (vars) => unwrapResult(await sodax.dex.assetService.deposit({ ...vars, raw: false })),
2027
+ onSuccess: async (data, vars, ctx) => {
2028
+ const { params } = vars;
2029
+ queryClient.invalidateQueries({ queryKey: ["dex", "poolBalances", params.srcChainKey, params.srcAddress] });
2030
+ queryClient.invalidateQueries({
2031
+ queryKey: ["dex", "allowance", params.srcChainKey, params.asset, params.amount.toString()]
1947
2032
  });
1948
- if (!depositResult.ok) {
1949
- throw new Error(`Deposit failed: ${depositResult.error?.code || "Unknown error"}`);
1950
- }
1951
- return depositResult.value;
1952
- },
1953
- onSuccess: () => {
1954
- queryClient.invalidateQueries({ queryKey: ["dex", "poolBalances"] });
2033
+ queryClient.invalidateQueries({ queryKey: ["shared", "xBalances", params.srcChainKey] });
2034
+ await mutationOptions?.onSuccess?.(data, vars, ctx);
1955
2035
  }
1956
2036
  });
1957
2037
  }
1958
- function useDexWithdraw() {
2038
+ function useDexWithdraw({
2039
+ mutationOptions
2040
+ } = {}) {
1959
2041
  const { sodax } = useSodaxContext();
1960
2042
  const queryClient = useQueryClient();
1961
- return useMutation({
1962
- mutationFn: async ({ params, spokeProvider }) => {
1963
- if (!spokeProvider) {
1964
- throw new Error("Spoke provider is required");
1965
- }
1966
- const withdrawResult = await sodax.dex.assetService.withdraw({
1967
- params,
1968
- spokeProvider
1969
- });
1970
- if (!withdrawResult.ok) {
1971
- throw new Error(`Withdraw failed: ${withdrawResult.error.code}`);
1972
- }
1973
- return withdrawResult.value;
1974
- },
1975
- onSuccess: () => {
1976
- queryClient.invalidateQueries({ queryKey: ["dex", "poolBalances"] });
2043
+ return useSafeMutation({
2044
+ mutationKey: ["dex", "withdraw"],
2045
+ ...mutationOptions,
2046
+ mutationFn: async (vars) => unwrapResult(await sodax.dex.assetService.withdraw({ ...vars, raw: false })),
2047
+ onSuccess: async (data, vars, ctx) => {
2048
+ const { params } = vars;
2049
+ queryClient.invalidateQueries({ queryKey: ["dex", "poolBalances", params.srcChainKey, params.srcAddress] });
2050
+ queryClient.invalidateQueries({ queryKey: ["shared", "xBalances", params.srcChainKey] });
2051
+ await mutationOptions?.onSuccess?.(data, vars, ctx);
1977
2052
  }
1978
2053
  });
1979
2054
  }
1980
2055
  function useDexAllowance({
1981
2056
  params,
1982
- spokeProvider,
1983
- queryOptions = {
1984
- queryKey: [
1985
- "dex",
1986
- "allowance",
1987
- params?.asset,
1988
- params?.poolToken,
1989
- params?.amount.toString(),
1990
- spokeProvider?.chainConfig.chain.id
1991
- ],
1992
- enabled: !!params && !!spokeProvider
1993
- }
1994
- }) {
2057
+ queryOptions
2058
+ } = {}) {
1995
2059
  const { sodax } = useSodaxContext();
2060
+ const payload = params?.payload;
1996
2061
  return useQuery({
1997
- ...queryOptions,
2062
+ queryKey: ["dex", "allowance", payload?.srcChainKey, payload?.asset, payload?.amount?.toString()],
1998
2063
  queryFn: async () => {
1999
- if (!params || !spokeProvider) {
2000
- throw new Error("Params and spoke provider are required");
2001
- }
2002
- const allowanceResult = await sodax.dex.assetService.isAllowanceValid({
2003
- params: {
2004
- asset: params.asset,
2005
- amount: params.amount,
2006
- poolToken: params.poolToken
2007
- },
2008
- spokeProvider
2009
- });
2010
- if (!allowanceResult.ok) {
2011
- return false;
2064
+ if (!payload) {
2065
+ throw new Error("Params are required");
2012
2066
  }
2013
- return allowanceResult.value;
2014
- }
2067
+ const result = await sodax.dex.assetService.isAllowanceValid({ params: payload, raw: true });
2068
+ if (!result.ok) throw result.error;
2069
+ return result.value;
2070
+ },
2071
+ enabled: !!payload,
2072
+ refetchInterval: 5e3,
2073
+ gcTime: 0,
2074
+ ...queryOptions
2015
2075
  });
2016
2076
  }
2017
- function useDexApprove() {
2077
+ function useDexApprove({
2078
+ mutationOptions
2079
+ } = {}) {
2018
2080
  const { sodax } = useSodaxContext();
2019
2081
  const queryClient = useQueryClient();
2020
- return useMutation({
2021
- mutationFn: async ({ params, spokeProvider }) => {
2022
- const approveResult = await sodax.dex.assetService.approve({
2023
- params,
2024
- spokeProvider,
2025
- raw: false
2082
+ return useSafeMutation({
2083
+ mutationKey: ["dex", "approve"],
2084
+ ...mutationOptions,
2085
+ mutationFn: async (vars) => unwrapResult(await sodax.dex.assetService.approve({ ...vars, raw: false })),
2086
+ onSuccess: async (data, vars, ctx) => {
2087
+ const { params } = vars;
2088
+ queryClient.invalidateQueries({
2089
+ queryKey: ["dex", "allowance", params.srcChainKey, params.asset, params.amount.toString()]
2026
2090
  });
2027
- if (!approveResult.ok) {
2028
- throw new Error("Approval failed");
2029
- }
2030
- return approveResult.value;
2031
- },
2032
- onSuccess: () => {
2033
- queryClient.invalidateQueries({ queryKey: ["dex", "allowance"] });
2091
+ await mutationOptions?.onSuccess?.(data, vars, ctx);
2034
2092
  }
2035
2093
  });
2036
2094
  }
@@ -2042,48 +2100,28 @@ function useLiquidityAmounts(minPrice, maxPrice, poolData) {
2042
2100
  const parsedMin = Number.parseFloat(minPrice);
2043
2101
  const parsedMax = Number.parseFloat(maxPrice);
2044
2102
  const isValid = parsedMin > 0 && parsedMax > 0 && parsedMin < parsedMax;
2045
- return {
2046
- minPriceNum: parsedMin,
2047
- maxPriceNum: parsedMax,
2048
- isValidPriceRange: isValid
2049
- };
2103
+ return { minPriceNum: parsedMin, maxPriceNum: parsedMax, isValidPriceRange: isValid };
2050
2104
  }, [minPrice, maxPrice]);
2051
2105
  const { tickLower, tickUpper, currentTick } = useMemo(() => {
2052
2106
  if (!poolData || !isValidPriceRange) {
2053
- return {
2054
- tickLower: null,
2055
- tickUpper: null,
2056
- currentTick: null
2057
- };
2107
+ return { tickLower: null, tickUpper: null, currentTick: null };
2058
2108
  }
2059
2109
  try {
2060
2110
  const lower = ClService.priceToTick(minPriceNum, poolData.token0, poolData.token1, poolData.tickSpacing);
2061
2111
  const upper = ClService.priceToTick(maxPriceNum, poolData.token0, poolData.token1, poolData.tickSpacing);
2062
- return {
2063
- tickLower: lower,
2064
- tickUpper: upper,
2065
- currentTick: BigInt(poolData.currentTick)
2066
- };
2112
+ return { tickLower: lower, tickUpper: upper, currentTick: BigInt(poolData.currentTick) };
2067
2113
  } catch (err) {
2068
2114
  console.error("Failed to calculate ticks:", err);
2069
- return {
2070
- tickLower: null,
2071
- tickUpper: null,
2072
- currentTick: null
2073
- };
2115
+ return { tickLower: null, tickUpper: null, currentTick: null };
2074
2116
  }
2075
2117
  }, [minPriceNum, maxPriceNum, poolData, isValidPriceRange]);
2076
2118
  const handleToken0AmountChange = useCallback(
2077
2119
  (value) => {
2078
2120
  setLiquidityToken0Amount(value);
2079
2121
  setLastEditedToken("token0");
2080
- if (!value || !poolData || !tickLower || !tickUpper || !currentTick) {
2081
- return;
2082
- }
2122
+ if (!value || !poolData || !tickLower || !tickUpper || !currentTick) return;
2083
2123
  const amount0 = Number.parseFloat(value);
2084
- if (amount0 <= 0 || !isValidPriceRange) {
2085
- return;
2086
- }
2124
+ if (amount0 <= 0 || !isValidPriceRange) return;
2087
2125
  try {
2088
2126
  const amount0BigInt = BigInt(Math.floor(amount0 * 10 ** poolData.token0.decimals));
2089
2127
  const amount1BigInt = ClService.calculateAmount1FromAmount0(
@@ -2105,13 +2143,9 @@ function useLiquidityAmounts(minPrice, maxPrice, poolData) {
2105
2143
  (value) => {
2106
2144
  setLiquidityToken1Amount(value);
2107
2145
  setLastEditedToken("token1");
2108
- if (!value || !poolData || !tickLower || !tickUpper || !currentTick) {
2109
- return;
2110
- }
2146
+ if (!value || !poolData || !tickLower || !tickUpper || !currentTick) return;
2111
2147
  const amount1 = Number.parseFloat(value);
2112
- if (amount1 <= 0 || !isValidPriceRange) {
2113
- return;
2114
- }
2148
+ if (amount1 <= 0 || !isValidPriceRange) return;
2115
2149
  try {
2116
2150
  const amount1BigInt = BigInt(Math.floor(amount1 * 10 ** poolData.token1.decimals));
2117
2151
  const amount0BigInt = ClService.calculateAmount0FromAmount1(
@@ -2130,9 +2164,7 @@ function useLiquidityAmounts(minPrice, maxPrice, poolData) {
2130
2164
  [poolData, tickLower, tickUpper, currentTick, isValidPriceRange]
2131
2165
  );
2132
2166
  useEffect(() => {
2133
- if (!poolData || !tickLower || !tickUpper || !lastEditedToken || !isValidPriceRange) {
2134
- return;
2135
- }
2167
+ if (!poolData || !tickLower || !tickUpper || !lastEditedToken || !isValidPriceRange) return;
2136
2168
  if (lastEditedToken === "token0" && liquidityToken0Amount) {
2137
2169
  handleToken0AmountChange(liquidityToken0Amount);
2138
2170
  } else if (lastEditedToken === "token1" && liquidityToken1Amount) {
@@ -2159,73 +2191,71 @@ function useLiquidityAmounts(minPrice, maxPrice, poolData) {
2159
2191
  handleToken1AmountChange
2160
2192
  };
2161
2193
  }
2162
- function useSupplyLiquidity() {
2194
+ function useSupplyLiquidity({
2195
+ mutationOptions
2196
+ } = {}) {
2163
2197
  const { sodax } = useSodaxContext();
2164
2198
  const queryClient = useQueryClient();
2165
- return useMutation({
2166
- mutationFn: async ({ params, spokeProvider }) => {
2167
- if (params.tokenId && params.isValidPosition) {
2168
- const increaseResult = await sodax.dex.clService.increaseLiquidity({
2169
- params: {
2170
- poolKey: params.poolKey,
2171
- tokenId: BigInt(params.tokenId),
2172
- tickLower: params.tickLower,
2173
- tickUpper: params.tickUpper,
2174
- liquidity: params.liquidity,
2175
- amount0Max: params.amount0Max,
2176
- amount1Max: params.amount1Max,
2177
- sqrtPriceX96: params.sqrtPriceX96
2178
- },
2179
- spokeProvider
2180
- });
2181
- if (!increaseResult.ok) {
2182
- throw new Error(`Increase liquidity failed: ${increaseResult.error?.code || "Unknown error"}`);
2183
- }
2184
- return increaseResult.value;
2185
- }
2186
- const supplyResult = await sodax.dex.clService.supplyLiquidity({
2187
- params: {
2188
- poolKey: params.poolKey,
2189
- tickLower: params.tickLower,
2190
- tickUpper: params.tickUpper,
2191
- liquidity: params.liquidity,
2192
- amount0Max: params.amount0Max,
2193
- amount1Max: params.amount1Max,
2194
- sqrtPriceX96: params.sqrtPriceX96
2195
- },
2196
- spokeProvider
2197
- });
2198
- if (!supplyResult.ok) {
2199
- throw new Error(`Supply liquidity failed: ${supplyResult.error?.code || "Unknown error"}`);
2199
+ return useSafeMutation({
2200
+ mutationKey: ["dex", "supplyLiquidity"],
2201
+ ...mutationOptions,
2202
+ mutationFn: async ({ params, walletProvider, timeout }) => {
2203
+ const sharedParams = {
2204
+ srcChainKey: params.srcChainKey,
2205
+ srcAddress: params.srcAddress,
2206
+ poolKey: params.poolKey,
2207
+ tickLower: params.tickLower,
2208
+ tickUpper: params.tickUpper,
2209
+ liquidity: params.liquidity,
2210
+ amount0Max: params.amount0Max,
2211
+ amount1Max: params.amount1Max,
2212
+ sqrtPriceX96: params.sqrtPriceX96
2213
+ };
2214
+ if (params.tokenId !== void 0 && params.isValidPosition) {
2215
+ const increaseParams = {
2216
+ ...sharedParams,
2217
+ tokenId: typeof params.tokenId === "bigint" ? params.tokenId : BigInt(params.tokenId)
2218
+ };
2219
+ return unwrapResult(
2220
+ await sodax.dex.clService.increaseLiquidity({ params: increaseParams, raw: false, walletProvider, timeout })
2221
+ );
2200
2222
  }
2201
- return supplyResult.value;
2223
+ return unwrapResult(
2224
+ await sodax.dex.clService.supplyLiquidity({ params: sharedParams, raw: false, walletProvider, timeout })
2225
+ );
2202
2226
  },
2203
- onSuccess: () => {
2204
- queryClient.invalidateQueries({ queryKey: ["dex", "poolBalances"] });
2205
- queryClient.invalidateQueries({ queryKey: ["dex", "positionInfo"] });
2227
+ onSuccess: async (data, vars, ctx) => {
2228
+ const { params } = vars;
2229
+ if (params.tokenId !== void 0 && params.isValidPosition) {
2230
+ const tokenIdStr = String(params.tokenId);
2231
+ queryClient.invalidateQueries({ queryKey: ["dex", "positionInfo", tokenIdStr, params.poolKey] });
2232
+ } else {
2233
+ queryClient.invalidateQueries({ queryKey: ["dex", "positionInfo"] });
2234
+ }
2235
+ queryClient.invalidateQueries({ queryKey: ["dex", "poolData", params.poolKey] });
2236
+ queryClient.invalidateQueries({ queryKey: ["dex", "poolBalances", params.srcChainKey, params.srcAddress] });
2237
+ queryClient.invalidateQueries({ queryKey: ["shared", "xBalances", params.srcChainKey] });
2238
+ await mutationOptions?.onSuccess?.(data, vars, ctx);
2206
2239
  }
2207
2240
  });
2208
2241
  }
2209
- function useDecreaseLiquidity() {
2242
+ function useDecreaseLiquidity({
2243
+ mutationOptions
2244
+ } = {}) {
2210
2245
  const { sodax } = useSodaxContext();
2211
2246
  const queryClient = useQueryClient();
2212
- return useMutation({
2213
- mutationFn: async ({ params, spokeProvider }) => {
2214
- if (!spokeProvider) {
2215
- throw new Error("Spoke provider is required");
2216
- }
2217
- const decreaseResult = await sodax.dex.clService.decreaseLiquidity({
2218
- params,
2219
- spokeProvider
2247
+ return useSafeMutation({
2248
+ mutationKey: ["dex", "decreaseLiquidity"],
2249
+ ...mutationOptions,
2250
+ mutationFn: async (vars) => unwrapResult(await sodax.dex.clService.decreaseLiquidity({ ...vars, raw: false })),
2251
+ onSuccess: async (data, vars, ctx) => {
2252
+ const { params } = vars;
2253
+ queryClient.invalidateQueries({
2254
+ queryKey: ["dex", "positionInfo", params.tokenId.toString(), params.poolKey]
2220
2255
  });
2221
- if (!decreaseResult.ok) {
2222
- throw new Error(`Decrease liquidity failed: ${decreaseResult.error?.code || "Unknown error"}`);
2223
- }
2224
- return decreaseResult.value;
2225
- },
2226
- onSuccess: () => {
2227
- queryClient.invalidateQueries({ queryKey: ["dex", "poolBalances"] });
2228
- queryClient.invalidateQueries({ queryKey: ["dex", "positionInfo"] });
2256
+ queryClient.invalidateQueries({ queryKey: ["dex", "poolData", params.poolKey] });
2257
+ queryClient.invalidateQueries({ queryKey: ["dex", "poolBalances", params.srcChainKey, params.srcAddress] });
2258
+ await mutationOptions?.onSuccess?.(data, vars, ctx);
2229
2259
  }
2230
2260
  });
2231
2261
  }
@@ -2433,37 +2463,57 @@ function useCreateWithdrawParams({
2433
2463
  return createWithdrawParamsProps({ tokenIndex, amount, poolData, poolSpokeAssets, dst });
2434
2464
  }, [tokenIndex, amount, poolData, poolSpokeAssets, dst]);
2435
2465
  }
2436
- function useClaimRewards() {
2466
+ function useClaimRewards({
2467
+ mutationOptions
2468
+ } = {}) {
2437
2469
  const { sodax } = useSodaxContext();
2438
2470
  const queryClient = useQueryClient();
2439
- return useMutation({
2440
- mutationFn: async ({ params, spokeProvider }) => {
2441
- if (!spokeProvider) {
2442
- throw new Error("Spoke provider is required");
2443
- }
2444
- const result = await sodax.dex.clService.claimRewards({
2445
- params,
2446
- spokeProvider
2447
- });
2448
- if (!result.ok) {
2449
- throw new Error(`Claim rewards failed: ${result.error?.code || "Unknown error"}`);
2450
- }
2451
- return result.value;
2452
- },
2453
- onSuccess: (_, { params, spokeProvider }) => {
2471
+ return useSafeMutation({
2472
+ mutationKey: ["dex", "claimRewards"],
2473
+ ...mutationOptions,
2474
+ mutationFn: async (vars) => unwrapResult(await sodax.dex.clService.claimRewards({ ...vars, raw: false })),
2475
+ onSuccess: async (data, vars, ctx) => {
2476
+ const { params } = vars;
2454
2477
  queryClient.invalidateQueries({
2455
- queryKey: ["dex", "poolBalances", params.poolKey, spokeProvider.chainConfig.chain.id]
2478
+ queryKey: ["dex", "positionInfo", params.tokenId.toString(), params.poolKey]
2456
2479
  });
2457
- queryClient.invalidateQueries({ queryKey: ["dex", "positionInfo", params.tokenId, params.poolKey] });
2458
- queryClient.invalidateQueries({ queryKey: ["dex", "poolData", params.poolKey] });
2480
+ queryClient.invalidateQueries({ queryKey: ["dex", "poolBalances", params.srcChainKey, params.srcAddress] });
2481
+ await mutationOptions?.onSuccess?.(data, vars, ctx);
2459
2482
  }
2460
2483
  });
2461
2484
  }
2462
- var SodaxProvider = ({ children, testnet = false, config, rpcConfig }) => {
2463
- const sodax = new Sodax(config);
2464
- return /* @__PURE__ */ React.createElement(SodaxContext.Provider, { value: { sodax, testnet, rpcConfig } }, children);
2485
+ var SodaxProvider = ({ children, config }) => {
2486
+ const configRef = useRef(config);
2487
+ const frozen = configRef.current;
2488
+ const sodax = useMemo(() => new Sodax(frozen), [frozen]);
2489
+ return /* @__PURE__ */ jsx(SodaxContext.Provider, { value: { sodax }, children });
2490
+ };
2491
+ var defaultOnMutationError = (error) => {
2492
+ console.error("[sodax] Mutation error:", error);
2465
2493
  };
2494
+ function createSodaxQueryClient({
2495
+ onMutationError = defaultOnMutationError,
2496
+ config
2497
+ } = {}) {
2498
+ if (config?.mutationCache) {
2499
+ config.mutationCache.subscribe((event) => {
2500
+ if (event.type === "updated" && event.action.type === "error" && event.mutation.options.meta?.silent !== true) {
2501
+ onMutationError(event.action.error);
2502
+ }
2503
+ });
2504
+ return new QueryClient(config);
2505
+ }
2506
+ return new QueryClient({
2507
+ ...config,
2508
+ mutationCache: new MutationCache({
2509
+ onError: (error, _vars, _ctx, mutation) => {
2510
+ if (mutation.options.meta?.silent === true) return;
2511
+ onMutationError(error);
2512
+ }
2513
+ })
2514
+ });
2515
+ }
2466
2516
 
2467
- export { MIGRATION_MODE_BNUSD, MIGRATION_MODE_ICX_SODA, SodaxProvider, clearRadfiSession, createDecreaseLiquidityParamsProps, createDepositParamsProps, createSupplyLiquidityParamsProps, createWithdrawParamsProps, loadRadfiSession, saveRadfiSession, useAToken, useATokensBalances, useBackendAllMoneyMarketAssets, useBackendAllMoneyMarketBorrowers, useBackendIntentByHash, useBackendIntentByTxHash, useBackendMoneyMarketAsset, useBackendMoneyMarketAssetBorrowers, useBackendMoneyMarketAssetSuppliers, useBackendMoneyMarketPosition, useBackendOrderbook, useBackendSubmitSwapTx, useBackendSubmitSwapTxStatus, useBackendUserIntents, useBitcoinBalance, useBorrow, useBridge, useBridgeAllowance, useBridgeApprove, useCancelLimitOrder, useCancelSwap, useCancelUnstake, useClaim, useClaimRewards, useConvertedAssets, useCreateDecreaseLiquidityParams, useCreateDepositParams, useCreateLimitOrder, useCreateSupplyLiquidityParams, useCreateWithdrawParams, useDecreaseLiquidity, useDeriveUserWalletAddress, useDexAllowance, useDexApprove, useDexDeposit, useDexWithdraw, useEstimateGas, useExpiredUtxos, useFundTradingWallet, useGetBridgeableAmount, useGetBridgeableTokens, useGetUserHubWalletAddress, useHubProvider, useInstantUnstake, useInstantUnstakeAllowance, useInstantUnstakeApprove, useInstantUnstakeRatio, useLiquidityAmounts, useMMAllowance, useMMApprove, useMigrate, useMigrationAllowance, useMigrationApprove, usePoolBalances, usePoolData, usePools, usePositionInfo, useQuote, useRadfiAuth, useRadfiSession, useRadfiWithdraw, useRenewUtxos, useRepay, useRequestTrustline, useReservesData, useReservesUsdFormat, useSodaxContext, useSpokeProvider, useStake, useStakeAllowance, useStakeApprove, useStakeRatio, useStakingConfig, useStakingInfo, useStatus, useStellarTrustlineCheck, useSupply, useSupplyLiquidity, useSwap, useSwapAllowance, useSwapApprove, useTradingWalletBalance, useUnstake, useUnstakeAllowance, useUnstakeApprove, useUnstakingInfo, useUnstakingInfoWithPenalty, useUserFormattedSummary, useUserReservesData, useWithdraw };
2517
+ export { SodaxProvider, clearRadfiSession, createDecreaseLiquidityParamsProps, createDepositParamsProps, createSodaxQueryClient, createSupplyLiquidityParamsProps, createWithdrawParamsProps, getXBalancesQueryOptions, loadRadfiSession, saveRadfiSession, toResult, unwrapResult, useAToken, useATokensBalances, useApproveToken, useBackendAllMoneyMarketAssets, useBackendAllMoneyMarketBorrowers, useBackendIntentByHash, useBackendIntentByTxHash, useBackendMoneyMarketAsset, useBackendMoneyMarketAssetBorrowers, useBackendMoneyMarketAssetSuppliers, useBackendMoneyMarketPosition, useBackendOrderbook, useBackendSubmitSwapTx, useBackendSubmitSwapTxStatus, useBackendUserIntents, useBitcoinBalance, useBorrow, useBridge, useBridgeAllowance, useBridgeApprove, useCancelLimitOrder, useCancelSwap, useCancelUnstake, useClaim, useClaimRewards, useConvertedAssets, useCreateDecreaseLiquidityParams, useCreateDepositParams, useCreateLimitOrder, useCreateSupplyLiquidityParams, useCreateWithdrawParams, useDecreaseLiquidity, useDeriveUserWalletAddress, useDexAllowance, useDexApprove, useDexDeposit, useDexWithdraw, useEstimateGas, useExpiredUtxos, useFeeClaimSwap, useFetchAssetsBalances, useFundTradingWallet, useGetAutoSwapPreferences, useGetBridgeableAmount, useGetBridgeableTokens, useGetUserHubWalletAddress, useHubAssetBalances, useHubProvider, useInstantUnstake, useInstantUnstakeAllowance, useInstantUnstakeApprove, useInstantUnstakeRatio, useIsTokenApproved, useLiquidityAmounts, useMMAllowance, useMMApprove, useMigrateBaln, useMigrateIcxToSoda, useMigratebnUSD, useMigrationAllowance, useMigrationApprove, usePoolBalances, usePoolData, usePools, usePositionInfo, useQuote, useRadfiAuth, useRadfiSession, useRadfiWithdraw, useRenewUtxos, useRepay, useRequestTrustline, useReservesData, useReservesHumanized, useReservesList, useReservesUsdFormat, useRevertMigrateSodaToIcx, useSafeMutation, useSetSwapPreference, useSodaxContext, useStake, useStakeAllowance, useStakeApprove, useStakeRatio, useStakingConfig, useStakingInfo, useStatus, useStellarTrustlineCheck, useSupply, useSupplyLiquidity, useSwap, useSwapAllowance, useSwapApprove, useTradingWallet, useTradingWalletBalance, useUnstake, useUnstakeAllowance, useUnstakeApprove, useUnstakingInfo, useUnstakingInfoWithPenalty, useUserFormattedSummary, useUserReservesData, useWithdraw, useWithdrawHubAsset, useXBalances };
2468
2518
  //# sourceMappingURL=index.mjs.map
2469
2519
  //# sourceMappingURL=index.mjs.map