@sodax/dapp-kit 1.5.6-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 +1594 -1532
  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 +27 -84
  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 -2562
  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
@@ -1,82 +1,51 @@
1
- // packages/dapp-kit/src/hooks/mm/useATokens.ts
2
- import { isAddress, type Address } from 'viem';
3
- import { useQuery, type UseQueryOptions, type UseQueryResult } from '@tanstack/react-query';
4
- import { useSodaxContext } from '../shared/useSodaxContext';
5
- import type { SpokeProvider } from '@sodax/sdk';
6
- import { HubService } from '@sodax/sdk';
1
+ import type { SpokeChainKey } from '@sodax/sdk';
2
+ import { useQuery, type UseQueryResult } from '@tanstack/react-query';
3
+ import { type Address, isAddress } from 'viem';
4
+ import { useSodaxContext } from '../shared/useSodaxContext.js';
5
+ import type { ReadHookParams } from '../shared/types.js';
7
6
 
8
- export type UseATokensBalancesParams = {
9
- aTokens: readonly Address[];
10
- spokeProvider?: SpokeProvider;
11
- userAddress?: string;
12
- queryOptions?: UseQueryOptions<Map<Address, bigint>, Error>;
13
- };
7
+ export type UseATokensBalancesParams = ReadHookParams<
8
+ Map<Address, bigint>,
9
+ {
10
+ aTokens: readonly Address[];
11
+ spokeChainKey: SpokeChainKey | undefined;
12
+ userAddress: string | undefined;
13
+ }
14
+ >;
14
15
 
15
16
  /**
16
- * React hook to fetch and cache aToken balances for multiple aToken addresses in a single multicall.
17
- *
18
- * Accepts an array of aToken addresses, a spoke provider, and user address. The hook derives the user's
19
- * hub wallet address and then fetches balanceOf for each aToken in a single multicall. Returns a Map
20
- * of aToken address to balance, with querying/caching powered by React Query. This hook uses viem's
21
- * multicall to batch all requests into a single RPC call for better performance.
22
- *
23
- * @param {UseATokensBalancesParams} params - Required params object:
24
- * @property {readonly Address[]} aTokens - Array of aToken contract addresses to query balances for.
25
- * @property {SpokeProvider} spokeProvider - The spoke provider to derive hub wallet address from.
26
- * @property {string} userAddress - User's wallet address on the spoke chain.
27
- * @property {UseQueryOptions<Map<Address, bigint>, Error>} queryOptions - React Query options to control query (e.g., staleTime, refetch, etc.).
28
- *
29
- * @returns {UseQueryResult<Map<Address, bigint>, Error>} React Query result object:
30
- * - data: Map of aToken address to balance, if available
31
- * - isLoading: Boolean loading state
32
- * - error: Error, if API call fails
33
- *
34
- * @example
35
- * const { data: aTokenBalances, isLoading, error } = useATokensBalances({
36
- * aTokens: [aToken1, aToken2, aToken3],
37
- * spokeProvider,
38
- * userAddress: '0x...',
39
- * queryOptions: {}
40
- * });
41
- * const aToken1Balance = aTokenBalances?.get(aToken1);
17
+ * React hook to fetch aToken balances for a list of addresses in a single multicall.
18
+ * Derives the user's hub wallet via `EvmHubProvider.getUserHubWalletAddress` from the
19
+ * spoke `chainKey` + spoke wallet `userAddress`.
42
20
  */
43
21
  export function useATokensBalances({
44
- aTokens,
45
- spokeProvider,
46
- userAddress,
22
+ params,
47
23
  queryOptions,
48
- }: UseATokensBalancesParams): UseQueryResult<Map<Address, bigint>, Error> {
24
+ }: UseATokensBalancesParams = {}): UseQueryResult<Map<Address, bigint>, Error> {
49
25
  const { sodax } = useSodaxContext();
50
- const defaultQueryOptions = {
51
- queryKey: ['mm', 'aTokensBalances', aTokens, spokeProvider?.chainConfig.chain.id, userAddress],
52
- enabled: aTokens.length > 0 && aTokens.every(token => isAddress(token)) && !!spokeProvider && !!userAddress,
53
- };
54
- queryOptions = {
55
- ...defaultQueryOptions,
56
- ...queryOptions, // override default query options if provided
57
- };
26
+ const aTokens = params?.aTokens ?? [];
27
+ const spokeChainKey = params?.spokeChainKey;
28
+ const userAddress = params?.userAddress;
58
29
 
59
30
  return useQuery({
60
- ...queryOptions,
31
+ queryKey: ['mm', 'aTokensBalances', aTokens, spokeChainKey, userAddress],
61
32
  queryFn: async () => {
62
33
  if (aTokens.length === 0) {
63
- return new Map();
34
+ return new Map<Address, bigint>();
64
35
  }
65
-
66
- if (!spokeProvider || !userAddress) {
67
- throw new Error('Spoke provider and user address are required');
36
+ if (!spokeChainKey || !userAddress) {
37
+ throw new Error('spokeChainKey and userAddress are required');
68
38
  }
69
-
70
- // Validate all addresses
71
39
  for (const aToken of aTokens) {
72
40
  if (!isAddress(aToken)) {
73
41
  throw new Error(`Invalid aToken address: ${aToken}`);
74
42
  }
75
43
  }
76
44
 
77
- const hubWalletAddress = await HubService.getUserHubWalletAddress(userAddress, spokeProvider.chainConfig.chain.id, sodax.hubProvider);
78
-
79
- return await sodax.moneyMarket.data.getATokensBalances(aTokens, hubWalletAddress);
45
+ const hubWalletAddress = await sodax.hubProvider.getUserHubWalletAddress(userAddress, spokeChainKey);
46
+ return sodax.moneyMarket.data.getATokensBalances(aTokens, hubWalletAddress);
80
47
  },
48
+ enabled: aTokens.length > 0 && !!spokeChainKey && !!userAddress,
49
+ ...queryOptions,
81
50
  });
82
51
  }
@@ -1,67 +1,49 @@
1
- import { useMutation, type UseMutationResult } from '@tanstack/react-query';
2
- import { useSodaxContext } from '../shared/useSodaxContext';
3
- import type { MoneyMarketBorrowParams, MoneyMarketError, RelayErrorCode, SpokeProvider } from '@sodax/sdk';
1
+ // packages/dapp-kit/src/hooks/mm/useBorrow.ts
2
+ import type { MoneyMarketBorrowActionParams, SpokeChainKey, TxHashPair } from '@sodax/sdk';
3
+ import { useQueryClient } from '@tanstack/react-query';
4
+ import { useSodaxContext } from '../shared/useSodaxContext.js';
5
+ import type { MutationHookParams } from '../shared/types.js';
6
+ import { useSafeMutation, type SafeUseMutationResult } from '../shared/useSafeMutation.js';
7
+ import { unwrapResult } from '../shared/unwrapResult.js';
4
8
 
5
- interface BorrowResponse {
6
- ok: true;
7
- value: [string, string];
8
- }
9
-
10
- export type UseBorrowParams = {
11
- params: MoneyMarketBorrowParams;
12
- spokeProvider: SpokeProvider;
13
- };
9
+ /**
10
+ * Mutation variables for {@link useBorrow}. Generic over `K extends SpokeChainKey` (defaults to
11
+ * the full union). Sophisticated callers can lock K at the hook call site to narrow the
12
+ * `walletProvider` and `params.srcChainKey` types.
13
+ */
14
+ export type UseBorrowVars<K extends SpokeChainKey = SpokeChainKey> = Omit<
15
+ MoneyMarketBorrowActionParams<K, false>,
16
+ 'raw'
17
+ >;
14
18
 
15
19
  /**
16
- * React hook for borrowing tokens in the Sodax money market protocol.
17
- *
18
- * Encapsulates the async process to initiate a borrow transaction via the money market,
19
- * handling transaction creation, submission, and cross-chain logic.
20
- *
21
- * @returns {UseMutationResult<
22
- * BorrowResponse,
23
- * MoneyMarketError<'CREATE_BORROW_INTENT_FAILED' | 'BORROW_UNKNOWN_ERROR' | RelayErrorCode>,
24
- * UseBorrowParams
25
- * >} A React Query mutation result object containing:
26
- * - mutateAsync: (params: UseBorrowParams) => Promise<BorrowResponse>
27
- * Triggers the borrow action. Expects an object with valid borrow params and a `SpokeProvider`.
28
- * - isPending: `boolean` if a borrow transaction is in progress.
29
- * - error: `MoneyMarketError` if the transaction fails, or `null`.
20
+ * React hook for borrowing tokens from the Sodax money market protocol.
30
21
  *
31
- * @example
32
- * ```typescript
33
- * const { mutateAsync: borrow, isPending, error } = useBorrow();
34
- * await borrow({ params: borrowParams, spokeProvider });
35
- * ```
36
- *
37
- * @throws {Error} When:
38
- * - `spokeProvider` is missing or invalid.
39
- * - The underlying borrow transaction fails.
22
+ * Throws on SDK failure so React Query's native error model engages (`isError`, `error`,
23
+ * `onError`, `retry`). Returns the unwrapped `TxHashPair` on success.
40
24
  */
41
- export function useBorrow(): UseMutationResult<
42
- BorrowResponse,
43
- MoneyMarketError<'CREATE_BORROW_INTENT_FAILED' | 'BORROW_UNKNOWN_ERROR' | RelayErrorCode>,
44
- UseBorrowParams
45
- > {
25
+ export function useBorrow<K extends SpokeChainKey = SpokeChainKey>({
26
+ mutationOptions,
27
+ }: MutationHookParams<TxHashPair, UseBorrowVars<K>> = {}): SafeUseMutationResult<TxHashPair, Error, UseBorrowVars<K>> {
46
28
  const { sodax } = useSodaxContext();
29
+ const queryClient = useQueryClient();
47
30
 
48
- return useMutation<
49
- BorrowResponse,
50
- MoneyMarketError<'CREATE_BORROW_INTENT_FAILED' | 'BORROW_UNKNOWN_ERROR' | RelayErrorCode>,
51
- UseBorrowParams
52
- >({
53
- mutationFn: async ({ params, spokeProvider }: UseBorrowParams) => {
54
- if (!spokeProvider) {
55
- throw new Error('spokeProvider is not found');
31
+ return useSafeMutation<TxHashPair, Error, UseBorrowVars<K>>({
32
+ mutationKey: ['mm', 'borrow'],
33
+ ...mutationOptions,
34
+ mutationFn: async vars => unwrapResult(await sodax.moneyMarket.borrow({ ...vars, raw: false })),
35
+ onSuccess: async (data, vars, ctx) => {
36
+ const { params } = vars;
37
+ queryClient.invalidateQueries({ queryKey: ['mm', 'userReservesData', params.srcChainKey, params.srcAddress] });
38
+ queryClient.invalidateQueries({
39
+ queryKey: ['mm', 'userFormattedSummary', params.srcChainKey, params.srcAddress],
40
+ });
41
+ queryClient.invalidateQueries({ queryKey: ['mm', 'aTokensBalances'] });
42
+ const balanceChains = new Set([params.srcChainKey, params.dstChainKey ?? params.srcChainKey]);
43
+ for (const chainKey of balanceChains) {
44
+ queryClient.invalidateQueries({ queryKey: ['shared', 'xBalances', chainKey] });
56
45
  }
57
-
58
- const response = await sodax.moneyMarket.borrow(params, spokeProvider);
59
-
60
- if (!response.ok) {
61
- throw response.error;
62
- }
63
-
64
- return response;
46
+ await mutationOptions?.onSuccess?.(data, vars, ctx);
65
47
  },
66
48
  });
67
49
  }
@@ -1,93 +1,57 @@
1
- import { useQuery, type UseQueryOptions, type UseQueryResult } from '@tanstack/react-query';
2
- import { useSodaxContext } from '../shared/useSodaxContext';
3
- import type { MoneyMarketParams, SpokeProvider } from '@sodax/sdk';
4
-
5
- export type UseMMAllowanceParams = {
6
- params: MoneyMarketParams | undefined;
7
- spokeProvider: SpokeProvider | undefined;
8
- queryOptions?: UseQueryOptions<boolean, Error>;
9
- };
1
+ import type { MoneyMarketParams } from '@sodax/sdk';
2
+ import type { SpokeChainKey } from '@sodax/sdk';
3
+ import { useQuery, type UseQueryResult } from '@tanstack/react-query';
4
+ import { useSodaxContext } from '../shared/useSodaxContext.js';
5
+ import type { ReadHookParams } from '../shared/types.js';
6
+
7
+ export type UseMMAllowanceParams<K extends SpokeChainKey> = ReadHookParams<
8
+ boolean,
9
+ {
10
+ payload: MoneyMarketParams<K> | undefined;
11
+ }
12
+ >;
10
13
 
11
14
  /**
12
- * Hook for checking token allowance for money market operations.
13
- *
14
- * This hook verifies if the user has approved enough tokens for a specific money market action
15
- * (borrow/repay). It automatically queries and tracks the allowance status.
15
+ * Hook for checking token allowance / trustline sufficiency for money market operations.
16
16
  *
17
- * @param {XToken} token - The token to check allowance for. Must be an XToken with valid address and chain information.
18
- * @param {string} amount - The amount to check allowance for, as a decimal string
19
- * @param {MoneyMarketAction} action - The money market action to check allowance for ('borrow' or 'repay')
20
- * @param {SpokeProvider} spokeProvider - The spoke provider to use for allowance checks
17
+ * Skips the on-chain check entirely for `borrow` and `withdraw` actions those don't require
18
+ * approval, and the SDK already short-circuits to `true` for them. The early `enabled: false`
19
+ * here additionally avoids a render flash with `isLoading: true`.
21
20
  *
22
- * @returns {UseQueryResult<boolean, Error>} A React Query result containing:
23
- * - data: Boolean indicating if allowance is sufficient
24
- * - isLoading: Loading state indicator
25
- * - error: Any error that occurred during the check
21
+ * The query key matches the invalidation keys emitted by `useMMApprove` and the four mutation
22
+ * hooks: `['mm', 'allowance', srcChainKey, token, action]`.
26
23
  *
27
24
  * @example
28
- * ```typescript
29
- * const { data: hasAllowed, isLoading } = useMMAllowance(token, "100", "repay", provider);
25
+ * ```tsx
26
+ * const { data: hasAllowance } = useMMAllowance({ params: { payload: supplyParams } });
30
27
  * ```
31
28
  */
32
- export function useMMAllowance({
29
+ export function useMMAllowance<K extends SpokeChainKey>({
33
30
  params,
34
- spokeProvider,
35
31
  queryOptions,
36
- }: UseMMAllowanceParams): UseQueryResult<boolean, Error> {
32
+ }: UseMMAllowanceParams<K> = {}): UseQueryResult<boolean, Error> {
37
33
  const { sodax } = useSodaxContext();
34
+ const payload = params?.payload;
38
35
 
39
- const defaultQueryOptions = {
40
- queryKey: ['mm', 'allowance', params?.token, params?.action],
41
- /**
42
- * IMPORTANT: Skip allowance checks for 'borrow' and 'withdraw' actions.
43
- *
44
- * Reason: According to the SDK's MoneyMarketService.isAllowanceValid() implementation,
45
- * borrow and withdraw actions do NOT require ERC-20 token approval. The SDK's
46
- * isAllowanceValid() method always returns `true` for these actions without making
47
- * any on-chain allowance checks.
48
- *
49
- * This optimization prevents unnecessary RPC calls and avoids showing confusing states for actions that don't actually need approval.
50
- *
51
- * Only 'supply' and 'repay' actions require token approval and should trigger allowance checks.
52
- */
53
- enabled: !!spokeProvider && !!params && params.action !== 'borrow' && params.action !== 'withdraw',
54
- refetchInterval: 5000,
55
- gcTime: 0, // Don't cache failed queries
56
- };
57
-
58
- queryOptions = {
59
- ...defaultQueryOptions,
60
- ...queryOptions, // override default query options if provided
61
- };
62
-
63
- return useQuery({
64
- ...queryOptions,
36
+ return useQuery<boolean, Error>({
37
+ queryKey: ['mm', 'allowance', payload?.srcChainKey, payload?.token, payload?.action],
65
38
  queryFn: async () => {
66
- if (!spokeProvider) throw new Error('Spoke provider is required');
67
- if (!params) throw new Error('Params are required');
68
-
69
- /**
70
- * Early return for borrow/withdraw actions: these actions do NOT require ERC-20 token approval.
71
- *
72
- * The SDK's MoneyMarketService.isAllowanceValid() always returns `true` for borrow/withdraw
73
- * without checking on-chain allowance. This is because:
74
- * - Borrow: User receives tokens (no approval needed)
75
- * - Withdraw: User withdraws their own supplied tokens (no approval needed)
76
- *
77
- * By returning `true` here, we avoid unnecessary RPC calls and ensure consistent behavior
78
- * with the SDK's implementation.
79
- */
80
- if (params.action === 'borrow' || params.action === 'withdraw') {
81
- return true;
39
+ if (!payload) {
40
+ throw new Error('Params are required');
82
41
  }
83
42
 
84
- const allowance = await sodax.moneyMarket.isAllowanceValid(params, spokeProvider);
85
-
86
- if (!allowance.ok) {
87
- throw allowance.error;
43
+ // Borrow and withdraw don't require approval; SDK returns true instantly anyway.
44
+ if (payload.action === 'borrow' || payload.action === 'withdraw') {
45
+ return true;
88
46
  }
89
47
 
90
- return allowance.value;
48
+ const result = await sodax.moneyMarket.isAllowanceValid({ params: payload });
49
+ if (!result.ok) throw result.error;
50
+ return result.value;
91
51
  },
52
+ enabled: !!payload && payload.action !== 'borrow' && payload.action !== 'withdraw',
53
+ refetchInterval: 5000,
54
+ gcTime: 0,
55
+ ...queryOptions,
92
56
  });
93
57
  }
@@ -1,56 +1,49 @@
1
- import { useSodaxContext } from '../shared/useSodaxContext';
2
- import { useMutation, type UseMutationResult, useQueryClient } from '@tanstack/react-query';
3
- import type { MoneyMarketParams, SpokeProvider } from '@sodax/sdk';
1
+ // packages/dapp-kit/src/hooks/mm/useMMApprove.ts
2
+ import type { MoneyMarketApproveActionParams, SpokeChainKey, TxReturnType } from '@sodax/sdk';
3
+ import { useQueryClient } from '@tanstack/react-query';
4
+ import { useSodaxContext } from '../shared/useSodaxContext.js';
5
+ import type { MutationHookParams } from '../shared/types.js';
6
+ import { useSafeMutation, type SafeUseMutationResult } from '../shared/useSafeMutation.js';
7
+ import { unwrapResult } from '../shared/unwrapResult.js';
4
8
 
5
- export type UseMMApproveParams = {
6
- params: MoneyMarketParams;
7
- spokeProvider: SpokeProvider | undefined;
8
- };
9
+ /**
10
+ * Mutation variables for {@link useMMApprove}. Generic over `K extends SpokeChainKey` (defaults
11
+ * to the full union). Sophisticated callers can lock K at the hook call site to narrow the
12
+ * `walletProvider` and `params.srcChainKey` types.
13
+ */
14
+ export type UseMMApproveVars<K extends SpokeChainKey = SpokeChainKey> = Omit<
15
+ MoneyMarketApproveActionParams<K, false>,
16
+ 'raw'
17
+ >;
9
18
 
10
19
  /**
11
- * Hook for approving ERC20 token spending for Sodax money market actions.
12
- *
13
- * This hook manages the approval transaction, allowing the user
14
- * to grant the protocol permission to spend their tokens for specific money market actions
15
- * (such as supply, borrow, or repay). Upon successful approval, it also invalidates and
16
- * refetches the associated allowance status so the UI remains up-to-date.
17
- *
18
- * @returns {UseMutationResult<string, Error, UseMMApproveParams>} A React Query mutation result containing:
19
- * - mutateAsync: Function to trigger the approval (see below)
20
- * - isPending: Boolean indicating if approval transaction is in progress
21
- * - error: Error object if the last approval failed, null otherwise
20
+ * React hook for approving ERC-20 token spending (or trustline establishment) for a Sodax money
21
+ * market action.
22
22
  *
23
- * @example
24
- * ```tsx
25
- * const { mutateAsync: approve, isPending, error } = useMMApprove();
26
- * await approve({ params: { token, amount: "100", action: "supply", ... }, spokeProvider });
27
- * ```
28
- *
29
- * @throws {Error} When:
30
- * - spokeProvider is undefined or invalid
31
- * - Approval transaction fails for any reason
23
+ * Throws on SDK failure so React Query's native error model engages (`isError`, `error`,
24
+ * `onError`, `retry`). Returns the unwrapped tx return value on success. Invalidates the matching
25
+ * `['mm', 'allowance', srcChainKey, token, action]` query on confirmed success.
32
26
  */
33
- export function useMMApprove(): UseMutationResult<string, Error, UseMMApproveParams> {
27
+ export function useMMApprove<K extends SpokeChainKey = SpokeChainKey>({
28
+ mutationOptions,
29
+ }: MutationHookParams<TxReturnType<K, false>, UseMMApproveVars<K>> = {}): SafeUseMutationResult<
30
+ TxReturnType<K, false>,
31
+ Error,
32
+ UseMMApproveVars<K>
33
+ > {
34
34
  const { sodax } = useSodaxContext();
35
35
  const queryClient = useQueryClient();
36
36
 
37
- return useMutation({
38
- mutationFn: async ({ params, spokeProvider }: UseMMApproveParams) => {
39
- if (!spokeProvider) {
40
- throw new Error('Spoke provider not found');
41
- }
42
- const allowance = await sodax.moneyMarket.approve(params, spokeProvider, false);
43
- if (!allowance.ok) {
44
- throw allowance.error;
45
- }
46
-
47
- return allowance.value;
48
- },
49
- onSuccess: (_, { params, spokeProvider }: UseMMApproveParams) => {
50
- // Invalidate allowance query to refetch updated approval status
37
+ return useSafeMutation<TxReturnType<K, false>, Error, UseMMApproveVars<K>>({
38
+ mutationKey: ['mm', 'approve'],
39
+ ...mutationOptions,
40
+ mutationFn: async vars => unwrapResult(await sodax.moneyMarket.approve({ ...vars, raw: false })),
41
+ onSuccess: async (data, vars, ctx) => {
42
+ const { params } = vars;
51
43
  queryClient.invalidateQueries({
52
- queryKey: ['mm', 'allowance', spokeProvider?.chainConfig.chain.id, params.token, params.action],
44
+ queryKey: ['mm', 'allowance', params.srcChainKey, params.token, params.action],
53
45
  });
46
+ await mutationOptions?.onSuccess?.(data, vars, ctx);
54
47
  },
55
48
  });
56
49
  }
@@ -1,64 +1,44 @@
1
- import type { MoneyMarketError, MoneyMarketRepayParams, RelayErrorCode, SpokeProvider } from '@sodax/sdk';
2
- import { useMutation, type UseMutationResult } from '@tanstack/react-query';
3
- import { useSodaxContext } from '../shared/useSodaxContext';
1
+ // packages/dapp-kit/src/hooks/mm/useRepay.ts
2
+ import type { MoneyMarketRepayActionParams, SpokeChainKey, TxHashPair } from '@sodax/sdk';
3
+ import { useQueryClient } from '@tanstack/react-query';
4
+ import { useSodaxContext } from '../shared/useSodaxContext.js';
5
+ import type { MutationHookParams } from '../shared/types.js';
6
+ import { useSafeMutation, type SafeUseMutationResult } from '../shared/useSafeMutation.js';
7
+ import { unwrapResult } from '../shared/unwrapResult.js';
4
8
 
5
- interface RepayResponse {
6
- ok: true;
7
- value: [string, string];
8
- }
9
-
10
- export type UseRepayParams = {
11
- params: MoneyMarketRepayParams;
12
- spokeProvider: SpokeProvider;
13
- };
9
+ /**
10
+ * Mutation variables for {@link useRepay}. Generic over `K extends SpokeChainKey` (defaults to
11
+ * the full union). Sophisticated callers can lock K at the hook call site to narrow the
12
+ * `walletProvider` and `params.srcChainKey` types.
13
+ */
14
+ export type UseRepayVars<K extends SpokeChainKey = SpokeChainKey> = Omit<MoneyMarketRepayActionParams<K, false>, 'raw'>;
14
15
 
15
16
  /**
16
17
  * React hook for repaying a borrow in the Sodax money market protocol.
17
18
  *
18
- * This hook encapsulates the process of sending a repay transaction to the money market.
19
- * It manages the asynchronous operation for repayment, including sending the transaction
20
- * and error handling.
21
- *
22
- * @returns {UseMutationResult<RepayResponse, MoneyMarketError<'CREATE_REPAY_INTENT_FAILED' | 'REPAY_UNKNOWN_ERROR' | RelayErrorCode>, UseRepayParams>} React Query mutation result object containing:
23
- * - mutateAsync: (params: UseRepayParams) => Promise<RepayResponse>
24
- * Initiates a repay transaction using the given MoneyMarketRepayParams and SpokeProvider.
25
- * - isPending: boolean indicating if a transaction is in progress.
26
- * - error: MoneyMarketError if an error occurred while repaying, otherwise undefined.
27
- *
28
- * @example
29
- * ```typescript
30
- * const { mutateAsync: repay, isPending, error } = useRepay();
31
- * await repay({ params: repayParams, spokeProvider });
32
- * ```
33
- *
34
- * @throws {Error} When:
35
- * - `spokeProvider` is missing or invalid.
36
- * - The underlying repay transaction fails.
19
+ * Throws on SDK failure so React Query's native error model engages (`isError`, `error`,
20
+ * `onError`, `retry`). Returns the unwrapped `TxHashPair` on success.
37
21
  */
38
- export function useRepay(): UseMutationResult<
39
- RepayResponse,
40
- MoneyMarketError<'CREATE_REPAY_INTENT_FAILED' | 'REPAY_UNKNOWN_ERROR' | RelayErrorCode>,
41
- UseRepayParams
42
- > {
22
+ export function useRepay<K extends SpokeChainKey = SpokeChainKey>({
23
+ mutationOptions,
24
+ }: MutationHookParams<TxHashPair, UseRepayVars<K>> = {}): SafeUseMutationResult<TxHashPair, Error, UseRepayVars<K>> {
43
25
  const { sodax } = useSodaxContext();
26
+ const queryClient = useQueryClient();
44
27
 
45
- return useMutation<
46
- RepayResponse,
47
- MoneyMarketError<'CREATE_REPAY_INTENT_FAILED' | 'REPAY_UNKNOWN_ERROR' | RelayErrorCode>,
48
- UseRepayParams
49
- >({
50
- mutationFn: async ({ params, spokeProvider }: UseRepayParams) => {
51
- if (!spokeProvider) {
52
- throw new Error('spokeProvider is not found');
53
- }
54
-
55
- const response = await sodax.moneyMarket.repay(params, spokeProvider);
56
-
57
- if (!response.ok) {
58
- throw response.error;
59
- }
60
-
61
- return response;
28
+ return useSafeMutation<TxHashPair, Error, UseRepayVars<K>>({
29
+ mutationKey: ['mm', 'repay'],
30
+ ...mutationOptions,
31
+ mutationFn: async vars => unwrapResult(await sodax.moneyMarket.repay({ ...vars, raw: false })),
32
+ onSuccess: async (data, vars, ctx) => {
33
+ const { params } = vars;
34
+ queryClient.invalidateQueries({ queryKey: ['mm', 'userReservesData', params.srcChainKey, params.srcAddress] });
35
+ queryClient.invalidateQueries({
36
+ queryKey: ['mm', 'userFormattedSummary', params.srcChainKey, params.srcAddress],
37
+ });
38
+ queryClient.invalidateQueries({ queryKey: ['mm', 'aTokensBalances'] });
39
+ queryClient.invalidateQueries({ queryKey: ['mm', 'allowance', params.srcChainKey, params.token, params.action] });
40
+ queryClient.invalidateQueries({ queryKey: ['shared', 'xBalances', params.srcChainKey] });
41
+ await mutationOptions?.onSuccess?.(data, vars, ctx);
62
42
  },
63
43
  });
64
44
  }
@@ -1,49 +1,23 @@
1
- import { useQuery, type UseQueryResult, type UseQueryOptions } from '@tanstack/react-query';
2
- import { useSodaxContext } from '../shared/useSodaxContext';
3
1
  import type { AggregatedReserveData, BaseCurrencyInfo } from '@sodax/sdk';
2
+ import { useQuery, type UseQueryResult } from '@tanstack/react-query';
3
+ import { useSodaxContext } from '../shared/useSodaxContext.js';
4
+ import type { ReadHookParams } from '../shared/types.js';
4
5
 
5
- export type UseReservesDataParams = {
6
- queryOptions?: UseQueryOptions<readonly [readonly AggregatedReserveData[], BaseCurrencyInfo], Error>;
7
- };
6
+ export type UseReservesDataParams = ReadHookParams<readonly [readonly AggregatedReserveData[], BaseCurrencyInfo]>;
8
7
 
9
8
  /**
10
- * React hook for fetching the latest reserves data from the Sodax money market.
11
- *
12
- * Provides the full set of aggregated reserves and base currency information.
13
- * Optionally accepts React Query options for customizing the query key, cache time, and related behaviors.
14
- *
15
- * @param params (optional) - Object including:
16
- * - queryOptions: Custom React Query options
17
- *
18
- * @returns {UseQueryResult<readonly [readonly AggregatedReserveData[], BaseCurrencyInfo], Error>}
19
- * React Query result object containing:
20
- * - data: [aggregated reserves[], base currency info], or undefined if not loaded
21
- * - isLoading: True if the request is loading
22
- * - isError: True if the request failed
23
- * - error: Error object, if present
24
- *
25
- * @example
26
- * const { data, isLoading, error } = useReservesData();
27
- * const { data } = useReservesData({ queryOptions: { queryKey: ['custom', 'reservesData'] } });
9
+ * React hook for fetching the latest aggregated reserves data and base-currency info from the
10
+ * Sodax money market.
28
11
  */
29
- export function useReservesData(
30
- params?: UseReservesDataParams,
31
- ): UseQueryResult<readonly [readonly AggregatedReserveData[], BaseCurrencyInfo], Error> {
32
- const defaultQueryOptions = {
33
- queryKey: ['mm', 'reservesData'],
34
- refetchInterval: 5000,
35
- };
36
-
37
- const queryOptions = {
38
- ...defaultQueryOptions,
39
- ...params?.queryOptions, // override default query options if provided
40
- }
12
+ export function useReservesData({
13
+ queryOptions,
14
+ }: UseReservesDataParams = {}): UseQueryResult<readonly [readonly AggregatedReserveData[], BaseCurrencyInfo], Error> {
41
15
  const { sodax } = useSodaxContext();
42
16
 
43
17
  return useQuery({
18
+ queryKey: ['mm', 'reservesData'],
19
+ queryFn: async () => sodax.moneyMarket.data.getReservesData(),
20
+ refetchInterval: 5000,
44
21
  ...queryOptions,
45
- queryFn: async (): Promise<readonly [readonly AggregatedReserveData[], BaseCurrencyInfo]> => {
46
- return await sodax.moneyMarket.data.getReservesData();
47
- },
48
22
  });
49
23
  }