@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
@@ -1,62 +1,35 @@
1
- import type { SolverErrorResponse, SolverIntentQuoteRequest, SolverIntentQuoteResponse, Result } from '@sodax/sdk';
2
- import { useSodaxContext } from '../shared/useSodaxContext';
1
+ import type { SolverErrorResponse, SolverIntentQuoteRequest, SolverIntentQuoteResponse } from '@sodax/sdk';
2
+ import type { Result } from '@sodax/sdk';
3
+ import { useSodaxContext } from '../shared/useSodaxContext.js';
3
4
  import { useQuery, type UseQueryResult } from '@tanstack/react-query';
4
- import { useMemo } from 'react';
5
+ import type { ReadHookParams } from '../shared/types.js';
6
+
7
+ export type UseQuoteParams = ReadHookParams<
8
+ Result<SolverIntentQuoteResponse, SolverErrorResponse> | undefined,
9
+ { payload: SolverIntentQuoteRequest | undefined }
10
+ >;
5
11
 
6
12
  /**
7
13
  * Hook for fetching a quote for an intent-based swap.
8
14
  *
9
- * This hook provides real-time quote data for an intent-based swap.
10
- *
11
- * @param {SolverIntentQuoteRequest | undefined} payload - The intent quote request parameters. If undefined, the query will be disabled.
12
- *
13
- * @returns {UseQueryResult<Result<SolverIntentQuoteResponse, SolverErrorResponse> | undefined>} A query result object containing:
14
- * - data: The quote result from the solver
15
- * - isLoading: Boolean indicating if the quote is being fetched
16
- * - error: Error object if the quote request failed
17
- * - refetch: Function to manually trigger a quote refresh
18
- *
19
15
  * @example
20
16
  * ```typescript
21
- * const { data: quote, isLoading } = useQuote({
22
- * token_src: '0x...',
23
- * token_src_blockchain_id: '1',
24
- * token_dst: '0x...',
25
- * token_dst_blockchain_id: '2',
26
- * amount: '1000000000000000000',
27
- * quote_type: 'exact_input',
28
- * });
29
- *
30
- * if (isLoading) return <div>Loading quote...</div>;
31
- * if (quote) {
32
- * console.log('Quote received:', quote);
33
- * }
17
+ * const { data: quote, isLoading } = useQuote({ params: { payload } });
34
18
  * ```
35
19
  *
36
20
  * @remarks
37
21
  * - The quote is automatically refreshed every 3 seconds
38
22
  * - The query is disabled when payload is undefined
39
- * - Uses React Query for efficient caching and state management
40
23
  */
41
- export const useQuote = (
42
- payload: SolverIntentQuoteRequest | undefined,
43
- ): UseQueryResult<Result<SolverIntentQuoteResponse, SolverErrorResponse> | undefined> => {
24
+ export const useQuote = ({
25
+ params,
26
+ queryOptions,
27
+ }: UseQuoteParams = {}): UseQueryResult<Result<SolverIntentQuoteResponse, SolverErrorResponse> | undefined> => {
44
28
  const { sodax } = useSodaxContext();
45
-
46
- // Create a serializable query key by converting BigInt to string
47
- const queryKey = useMemo(() => {
48
- if (!payload) return ['quote', undefined];
49
- return [
50
- 'quote',
51
- {
52
- ...payload,
53
- amount: payload.amount.toString(),
54
- },
55
- ];
56
- }, [payload]);
29
+ const payload = params?.payload;
57
30
 
58
31
  return useQuery({
59
- queryKey,
32
+ queryKey: ['swap', 'quote', payload && { ...payload, amount: payload.amount.toString() }],
60
33
  queryFn: async () => {
61
34
  if (!payload) {
62
35
  return undefined;
@@ -65,5 +38,6 @@ export const useQuote = (
65
38
  },
66
39
  enabled: !!payload,
67
40
  refetchInterval: 3000,
41
+ ...queryOptions,
68
42
  });
69
43
  };
@@ -1,44 +1,37 @@
1
- import type { Hex, SolverErrorResponse, SolverIntentStatusResponse, Result } from '@sodax/sdk';
1
+ import type { Hex, SolverErrorResponse, SolverIntentStatusResponse } from '@sodax/sdk';
2
+ import type { Result } from '@sodax/sdk';
2
3
  import { useQuery, type UseQueryResult } from '@tanstack/react-query';
3
- import { useSodaxContext } from '../shared/useSodaxContext';
4
+ import { useSodaxContext } from '../shared/useSodaxContext.js';
5
+ import type { ReadHookParams } from '../shared/types.js';
6
+
7
+ export type UseStatusParams = ReadHookParams<
8
+ Result<SolverIntentStatusResponse, SolverErrorResponse> | undefined,
9
+ { intentTxHash: Hex | undefined }
10
+ >;
4
11
 
5
12
  /**
6
13
  * Hook for monitoring the status of an intent-based swap.
7
14
  *
8
- * This hook provides real-time status updates for an intent-based swap transaction.
9
- *
10
- * @param {Hex} intent_tx_hash - The transaction hash of the intent order on the hub chain
11
- *
12
- * @returns {UseQueryResult<Result<SolverIntentStatusResponse, SolverErrorResponse> | undefined>} A query result object containing:
13
- * - data: The status result from the solver
14
- * - isLoading: Boolean indicating if the status is being fetched
15
- * - error: Error object if the status request failed
16
- * - refetch: Function to manually trigger a status refresh
17
- *
18
15
  * @example
19
16
  * ```typescript
20
- * const { data: status, isLoading } = useStatus('0x...');
21
- *
22
- * if (isLoading) return <div>Loading status...</div>;
23
- * if (status?.ok) {
24
- * console.log('Status:', status.value);
25
- * }
17
+ * const { data: status, isLoading } = useStatus({ params: { intentTxHash } });
26
18
  * ```
27
- *
28
- * @remarks
29
- * - The status is automatically refreshed every 3 seconds
30
- * - Uses React Query for efficient caching and state management
31
19
  */
32
-
33
- export const useStatus = (
34
- intent_tx_hash: Hex,
35
- ): UseQueryResult<Result<SolverIntentStatusResponse, SolverErrorResponse> | undefined> => {
20
+ export const useStatus = ({
21
+ params,
22
+ queryOptions,
23
+ }: UseStatusParams = {}): UseQueryResult<Result<SolverIntentStatusResponse, SolverErrorResponse> | undefined> => {
36
24
  const { sodax } = useSodaxContext();
25
+ const intentTxHash = params?.intentTxHash;
26
+
37
27
  return useQuery({
38
- queryKey: [intent_tx_hash],
28
+ queryKey: ['swap', 'status', intentTxHash],
39
29
  queryFn: async () => {
40
- return sodax.swaps.getStatus({ intent_tx_hash });
30
+ if (!intentTxHash) return undefined;
31
+ return sodax.swaps.getStatus({ intent_tx_hash: intentTxHash });
41
32
  },
42
- refetchInterval: 3000, // 3s
33
+ enabled: !!intentTxHash,
34
+ refetchInterval: 3000,
35
+ ...queryOptions,
43
36
  });
44
37
  };
@@ -1,60 +1,42 @@
1
- import { useSodaxContext } from '../shared/useSodaxContext';
2
- import type {
3
- CreateIntentParams,
4
- SolverExecutionResponse,
5
- Result,
6
- IntentErrorCode,
7
- Intent,
8
- IntentError,
9
- SpokeProvider,
10
- IntentDeliveryInfo,
11
- } from '@sodax/sdk';
12
- import { useMutation, type UseMutationResult, useQueryClient } from '@tanstack/react-query';
1
+ // packages/dapp-kit/src/hooks/swap/useSwap.ts
2
+ import { useSodaxContext } from '../shared/useSodaxContext.js';
3
+ import type { SpokeChainKey, SwapActionParams, SwapResponse } from '@sodax/sdk';
4
+ import { useQueryClient } from '@tanstack/react-query';
5
+ import type { MutationHookParams } from '../shared/types.js';
6
+ import { useSafeMutation, type SafeUseMutationResult } from '../shared/useSafeMutation.js';
7
+ import { unwrapResult } from '../shared/unwrapResult.js';
13
8
 
14
- type CreateIntentResult = Result<[SolverExecutionResponse, Intent, IntentDeliveryInfo], IntentError<IntentErrorCode>>;
9
+ /**
10
+ * Mutation variables for {@link useSwap}. Generic over `K extends SpokeChainKey` (defaults to the
11
+ * 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 UseSwapVars<K extends SpokeChainKey = SpokeChainKey> = Omit<SwapActionParams<K, false>, 'raw'>;
15
15
 
16
16
  /**
17
- * Hook for creating and submitting an swap intent order for cross-chain swaps.
18
- * Uses React Query's useMutation for better state management and caching.
19
- *
20
- * @param {SpokeProvider} spokeProvider - The spoke provider to use for the swap
21
- * @returns {UseMutationResult} Mutation result object containing mutation function and state
17
+ * React hook for executing an intent-based cross-chain swap.
22
18
  *
23
- * @example
24
- * ```typescript
25
- * const { mutateAsync: swap, isPending } = useSwap(spokeProvider);
26
- *
27
- * const handleSwap = async () => {
28
- * const result = await swap({
29
- * token_src: '0x...',
30
- * token_src_blockchain_id: 'arbitrum',
31
- * token_dst: '0x...',
32
- * token_dst_blockchain_id: 'polygon',
33
- * amount: '1000000000000000000',
34
- * min_output_amount: '900000000000000000'
35
- * });
36
- * };
37
- * ```
19
+ * Throws on SDK failure so React Query's native error model engages (`isError`, `error`,
20
+ * `onError`, `retry`). Returns the unwrapped `SwapResponse` on success.
38
21
  */
39
- export function useSwap(
40
- spokeProvider: SpokeProvider | undefined,
41
- ): UseMutationResult<CreateIntentResult, Error, CreateIntentParams> {
22
+ export function useSwap<K extends SpokeChainKey = SpokeChainKey>({
23
+ mutationOptions,
24
+ }: MutationHookParams<SwapResponse, UseSwapVars<K>> = {}): SafeUseMutationResult<
25
+ SwapResponse,
26
+ Error,
27
+ UseSwapVars<K>
28
+ > {
42
29
  const { sodax } = useSodaxContext();
43
30
  const queryClient = useQueryClient();
44
31
 
45
- return useMutation<CreateIntentResult, Error, CreateIntentParams>({
46
- mutationFn: async (params: CreateIntentParams) => {
47
- if (!spokeProvider) {
48
- throw new Error('Spoke provider not found');
49
- }
50
- return sodax.swaps.swap({
51
- intentParams: params,
52
- spokeProvider,
53
- });
54
- },
55
- onSuccess: () => {
56
- // Invalidate balance queries to refresh both source and destination token balances
57
- queryClient.invalidateQueries({ queryKey: ['xBalances'] });
32
+ return useSafeMutation<SwapResponse, Error, UseSwapVars<K>>({
33
+ mutationKey: ['swap'],
34
+ ...mutationOptions,
35
+ mutationFn: async vars => unwrapResult(await sodax.swaps.swap({ ...vars, raw: false })),
36
+ onSuccess: async (data, vars, ctx) => {
37
+ queryClient.invalidateQueries({ queryKey: ['shared', 'xBalances', vars.params.srcChainKey] });
38
+ queryClient.invalidateQueries({ queryKey: ['shared', 'xBalances', vars.params.dstChainKey] });
39
+ await mutationOptions?.onSuccess?.(data, vars, ctx);
58
40
  },
59
41
  });
60
42
  }
@@ -1,48 +1,51 @@
1
1
  import { useQuery, type UseQueryResult } from '@tanstack/react-query';
2
- import { useSodaxContext } from '../shared/useSodaxContext';
3
- import type { CreateIntentParams, CreateLimitOrderParams, SpokeProvider } from '@sodax/sdk';
2
+ import { useSodaxContext } from '../shared/useSodaxContext.js';
3
+ import type { CreateIntentParams, CreateLimitOrderParams } from '@sodax/sdk';
4
+ import type { GetWalletProviderType, SpokeChainKey } from '@sodax/sdk';
5
+ import type { ReadHookParams } from '../shared/types.js';
4
6
 
5
- /**
6
- * Hook for checking token allowance for swap operations.
7
- *
8
- * This hook verifies if the user has approved enough tokens for a specific swap action.
9
- * It automatically queries and tracks the allowance status.
10
- *
11
- * @param {CreateIntentParams | CreateLimitOrderParams} params - The parameters for the intent to check allowance for.
12
- * @param {SpokeProvider} spokeProvider - The spoke provider to use for allowance checks
13
- *
14
- * @returns {UseQueryResult<boolean, Error>} A React Query result containing:
15
- * - data: Boolean indicating if allowance is sufficient
16
- * - isLoading: Loading state indicator
17
- * - error: Any error that occurred during the check
18
- *
19
- * @example
20
- * ```typescript
21
- * const { data: hasAllowed, isLoading } = useSwapAllowance(params, spokeProvider);
22
- * ```
23
- */
24
- export function useSwapAllowance(
25
- params: CreateIntentParams | CreateLimitOrderParams | undefined,
26
- spokeProvider: SpokeProvider | undefined,
27
- ): UseQueryResult<boolean, Error> {
7
+ export type UseSwapAllowanceParams<K extends SpokeChainKey> = ReadHookParams<
8
+ boolean,
9
+ {
10
+ payload: CreateIntentParams | CreateLimitOrderParams | undefined;
11
+ srcChainKey: K | undefined;
12
+ walletProvider: GetWalletProviderType<K> | undefined;
13
+ }
14
+ >;
15
+
16
+ export function useSwapAllowance<K extends SpokeChainKey>({
17
+ params,
18
+ queryOptions,
19
+ }: UseSwapAllowanceParams<K> = {}): UseQueryResult<boolean, Error> {
28
20
  const { sodax } = useSodaxContext();
21
+ const payload = params?.payload;
22
+ const srcChainKey = params?.srcChainKey;
23
+ const walletProvider = params?.walletProvider;
29
24
 
30
- return useQuery({
31
- queryKey: ['allowance', params],
25
+ return useQuery<boolean, Error>({
26
+ // Extract the (chain, owner, token, amount) tuple that actually scopes the allowance —
27
+ // raw-object keys break per Rule 4 (bigints) and churn on every render.
28
+ queryKey: [
29
+ 'swap',
30
+ 'allowance',
31
+ payload?.srcChainKey,
32
+ payload?.srcAddress,
33
+ payload?.inputToken,
34
+ payload?.inputAmount?.toString(),
35
+ ],
32
36
  queryFn: async () => {
33
- if (!spokeProvider || !params) {
37
+ if (!srcChainKey || !walletProvider || !payload) {
34
38
  return false;
35
39
  }
36
40
  const allowance = await sodax.swaps.isAllowanceValid({
37
- intentParams: params,
38
- spokeProvider,
41
+ params: payload as CreateIntentParams,
42
+ raw: false,
43
+ walletProvider,
39
44
  });
40
- if (allowance.ok) {
41
- return allowance.value;
42
- }
43
- return false;
45
+ return allowance.ok ? allowance.value : false;
44
46
  },
45
- enabled: !!spokeProvider && !!params,
47
+ enabled: !!srcChainKey && !!walletProvider && !!payload,
46
48
  refetchInterval: 2000,
49
+ ...queryOptions,
47
50
  });
48
51
  }
@@ -1,68 +1,59 @@
1
- import { useSodaxContext } from '../shared/useSodaxContext';
2
- import { useMutation, useQueryClient } from '@tanstack/react-query';
3
- import type { CreateIntentParams, CreateLimitOrderParams, SpokeProvider } from '@sodax/sdk';
1
+ // packages/dapp-kit/src/hooks/swap/useSwapApprove.ts
2
+ import { useSodaxContext } from '../shared/useSodaxContext.js';
3
+ import { useQueryClient } from '@tanstack/react-query';
4
+ import type {
5
+ CreateIntentParams,
6
+ CreateLimitOrderParams,
7
+ GetWalletProviderType,
8
+ SpokeChainKey,
9
+ TxReturnType,
10
+ } from '@sodax/sdk';
11
+ import type { MutationHookParams } from '../shared/types.js';
12
+ import { useSafeMutation, type SafeUseMutationResult } from '../shared/useSafeMutation.js';
13
+ import { unwrapResult } from '../shared/unwrapResult.js';
4
14
 
5
- interface UseApproveReturn {
6
- approve: ({ params }: { params: CreateIntentParams | CreateLimitOrderParams }) => Promise<boolean>;
7
- isLoading: boolean;
8
- error: Error | null;
9
- resetError: () => void;
10
- }
15
+ /**
16
+ * Mutation variables for {@link useSwapApprove}. Generic over `K extends SpokeChainKey` (defaults
17
+ * to the full union). Sophisticated callers can lock K at the call site to narrow the
18
+ * `walletProvider` and `params.srcChainKey` types.
19
+ */
20
+ export type UseSwapApproveVars<K extends SpokeChainKey = SpokeChainKey> = {
21
+ params: CreateIntentParams<K> | CreateLimitOrderParams<K>;
22
+ walletProvider: GetWalletProviderType<K>;
23
+ };
11
24
 
12
25
  /**
13
- * Hook for approving token spending for swap actions
14
- * @param params The parameters for the intent to approve spending for
15
- * @param spokeProvider The spoke provider instance for the chain
16
- * @returns Object containing approve function, loading state, error state and reset function
17
- * @example
18
- * ```tsx
19
- * const { approve, isLoading, error } = useApprove(token, spokeProvider);
26
+ * React hook for approving ERC-20 token spending (or trustline establishment) for a swap or
27
+ * limit-order intent.
20
28
  *
21
- * // Approve tokens for supply action
22
- * await approve({ amount: "100", action: "supply" });
23
- * ```
29
+ * Throws on SDK failure so React Query's native error model engages (`isError`, `error`,
30
+ * `onError`, `retry`). Returns the unwrapped tx return value on success. Invalidates all
31
+ * `['swap', 'allowance', ...]` queries so any pending allowance check refreshes.
24
32
  */
25
-
26
- export function useSwapApprove(
27
- params: CreateIntentParams | CreateLimitOrderParams | undefined,
28
- spokeProvider: SpokeProvider | undefined,
29
- ): UseApproveReturn {
33
+ export function useSwapApprove<K extends SpokeChainKey = SpokeChainKey>({
34
+ mutationOptions,
35
+ }: MutationHookParams<TxReturnType<K, false>, UseSwapApproveVars<K>> = {}): SafeUseMutationResult<
36
+ TxReturnType<K, false>,
37
+ Error,
38
+ UseSwapApproveVars<K>
39
+ > {
30
40
  const { sodax } = useSodaxContext();
31
41
  const queryClient = useQueryClient();
32
42
 
33
- const {
34
- mutateAsync: approve,
35
- isPending,
36
- error,
37
- reset: resetError,
38
- } = useMutation({
39
- mutationFn: async ({ params }: { params: CreateIntentParams | CreateLimitOrderParams | undefined }) => {
40
- if (!spokeProvider) {
41
- throw new Error('Spoke provider not found');
42
- }
43
- if (!params) {
44
- throw new Error('Swap Params not found');
45
- }
46
-
47
- const allowance = await sodax.swaps.approve({
48
- intentParams: params,
49
- spokeProvider,
50
- });
51
- if (!allowance.ok) {
52
- throw new Error('Failed to approve input token');
53
- }
54
- return allowance.ok;
55
- },
56
- onSuccess: () => {
57
- // Invalidate allowance query to refetch the new allowance
58
- queryClient.invalidateQueries({ queryKey: ['allowance', params] });
43
+ return useSafeMutation<TxReturnType<K, false>, Error, UseSwapApproveVars<K>>({
44
+ mutationKey: ['swap', 'approve'],
45
+ ...mutationOptions,
46
+ mutationFn: async ({ params, walletProvider }) =>
47
+ unwrapResult(
48
+ await sodax.swaps.approve<K, false>({
49
+ params: params as CreateIntentParams<K>,
50
+ raw: false,
51
+ walletProvider,
52
+ }),
53
+ ),
54
+ onSuccess: async (data, vars, ctx) => {
55
+ queryClient.invalidateQueries({ queryKey: ['swap', 'allowance'] });
56
+ await mutationOptions?.onSuccess?.(data, vars, ctx);
59
57
  },
60
58
  });
61
-
62
- return {
63
- approve,
64
- isLoading: isPending,
65
- error: error,
66
- resetError,
67
- };
68
59
  }
package/src/index.ts CHANGED
@@ -1,3 +1,5 @@
1
- export * from './hooks';
2
- export * from './providers';
3
- export * from './utils';
1
+ export * from './hooks/index.js';
2
+ export * from './providers/index.js';
3
+ export * from './utils/index.js';
4
+
5
+ export * from '@sodax/sdk' // export all sdk entities
@@ -1,19 +1,25 @@
1
- import type { ReactNode, ReactElement } from 'react';
2
- import React from 'react';
3
-
1
+ import { useMemo, useRef, type ReactNode, type ReactElement } from 'react';
4
2
  import { Sodax, type SodaxConfig } from '@sodax/sdk';
5
- import { SodaxContext } from '@/contexts';
6
- import type { RpcConfig } from '@sodax/types';
3
+ import { SodaxContext } from '@/contexts/index.js';
4
+ import type { DeepPartial } from '@sodax/sdk';
7
5
 
8
6
  interface SodaxProviderProps {
9
7
  children: ReactNode;
10
- testnet?: boolean;
11
- config?: SodaxConfig;
12
- rpcConfig: RpcConfig;
8
+ /**
9
+ * Sodax config (overrides defaults including rpcUrls). **Read-once at mount** —
10
+ * changes after first render are ignored to prevent re-instantiating the SDK
11
+ * from unstable parent references. To switch config (e.g. testnet ↔ mainnet),
12
+ * unmount/remount the provider.
13
+ */
14
+ config?: DeepPartial<SodaxConfig>;
13
15
  }
14
16
 
15
- export const SodaxProvider = ({ children, testnet = false, config, rpcConfig }: SodaxProviderProps): ReactElement => {
16
- const sodax = new Sodax(config);
17
+ export const SodaxProvider = ({ children, config }: SodaxProviderProps): ReactElement => {
18
+ // Freeze config on first render so the SDK instance and consumers share one
19
+ // snapshot (matches SodaxWalletProvider semantic).
20
+ const configRef = useRef<DeepPartial<SodaxConfig> | undefined>(config);
21
+ const frozen = configRef.current;
22
+ const sodax = useMemo(() => new Sodax(frozen), [frozen]);
17
23
 
18
- return <SodaxContext.Provider value={{ sodax, testnet, rpcConfig }}>{children}</SodaxContext.Provider>;
24
+ return <SodaxContext.Provider value={{ sodax }}>{children}</SodaxContext.Provider>;
19
25
  };
@@ -0,0 +1,96 @@
1
+ // packages/dapp-kit/src/providers/createSodaxQueryClient.ts
2
+ import { MutationCache, QueryClient, type QueryClientConfig } from '@tanstack/react-query';
3
+
4
+ export type CreateSodaxQueryClientOptions = {
5
+ /**
6
+ * Called for every mutation failure (i.e. every time `mutationFn` throws) that does NOT opt out
7
+ * via `meta: { silent: true }` on the mutation. Defaults to `console.error`.
8
+ *
9
+ * This is **global observability** — it runs alongside any per-hook `onError` handler the
10
+ * consumer set, NOT instead of them. In particular, it fires:
11
+ * - whether or not `mutateAsync` was awaited inside `try/catch`
12
+ * - whether or not `mutateAsyncSafe` was used (the wrapper packs into `Result<T>` AFTER React
13
+ * Query has already entered the error state and dispatched its callbacks)
14
+ * - whether or not the consumer's `mutationOptions.onError` already toasted/logged
15
+ *
16
+ * It is NOT a "did this rejection escape to the global handler?" detector — accurate
17
+ * unhandled-rejection detection lives at `window.onunhandledrejection`, not here.
18
+ *
19
+ * To silence the default for a specific mutation that the consumer is handling locally, set
20
+ * `meta: { silent: true }`:
21
+ *
22
+ * ```ts
23
+ * useSwap({ mutationOptions: { meta: { silent: true }, onError: (e) => toast(e.message) } });
24
+ * ```
25
+ *
26
+ * To disable the default globally (e.g. wire your own error boundary), pass a no-op:
27
+ *
28
+ * ```ts
29
+ * createSodaxQueryClient({ onMutationError: () => {} });
30
+ * ```
31
+ */
32
+ onMutationError?: (error: unknown) => void;
33
+ /** Pass-through for any other QueryClient config (default queries options, etc.) */
34
+ config?: QueryClientConfig;
35
+ };
36
+
37
+ const defaultOnMutationError = (error: unknown): void => {
38
+ console.error('[sodax] Mutation error:', error);
39
+ };
40
+
41
+ /**
42
+ * Creates a `QueryClient` pre-wired with a mutation-error observability hook that gives dapp-kit
43
+ * consumers a single seam for every mutation failure across the app — wire to Sentry/Datadog/console
44
+ * as you like. Optional opt-in: consumers who construct their own `QueryClient` are unaffected.
45
+ *
46
+ * **Composition with a custom `MutationCache`.** If you pass `config.mutationCache`, we keep your
47
+ * cache instance (preserving its own `onError` / `onSuccess` / etc.) and *additionally* subscribe
48
+ * to its event stream to dispatch `onMutationError`. Both your handler and the dapp-kit handler
49
+ * fire — neither replaces the other. If you don't pass one, we install our own.
50
+ *
51
+ * @example default — logs to console
52
+ * const queryClient = createSodaxQueryClient();
53
+ *
54
+ * @example custom — Sentry
55
+ * const queryClient = createSodaxQueryClient({
56
+ * onMutationError: (e) => Sentry.captureException(e),
57
+ * });
58
+ *
59
+ * @example silent — disable the default
60
+ * const queryClient = createSodaxQueryClient({ onMutationError: () => {} });
61
+ *
62
+ * @example bring-your-own MutationCache
63
+ * const myCache = new MutationCache({ onError: myOwnErrorHandler });
64
+ * const queryClient = createSodaxQueryClient({ config: { mutationCache: myCache } });
65
+ * // myOwnErrorHandler still runs; onMutationError ALSO runs (unless meta.silent).
66
+ */
67
+ export function createSodaxQueryClient({
68
+ onMutationError = defaultOnMutationError,
69
+ config,
70
+ }: CreateSodaxQueryClientOptions = {}): QueryClient {
71
+ if (config?.mutationCache) {
72
+ // Compose: keep the consumer's cache, attach our observability as an extra subscriber.
73
+ // The consumer's own `MutationCache.onError` (if any) still fires — subscribe is additive.
74
+ config.mutationCache.subscribe(event => {
75
+ if (
76
+ event.type === 'updated' &&
77
+ event.action.type === 'error' &&
78
+ event.mutation.options.meta?.silent !== true
79
+ ) {
80
+ onMutationError(event.action.error);
81
+ }
82
+ });
83
+ return new QueryClient(config);
84
+ }
85
+
86
+ // No consumer cache: install our own with `onError` directly (cheaper than a subscribe loop).
87
+ return new QueryClient({
88
+ ...config,
89
+ mutationCache: new MutationCache({
90
+ onError: (error, _vars, _ctx, mutation) => {
91
+ if (mutation.options.meta?.silent === true) return;
92
+ onMutationError(error);
93
+ },
94
+ }),
95
+ });
96
+ }
@@ -1 +1,2 @@
1
- export { SodaxProvider } from './SodaxProvider';
1
+ export { SodaxProvider } from './SodaxProvider.js';
2
+ export { createSodaxQueryClient, type CreateSodaxQueryClientOptions } from './createSodaxQueryClient.js';