@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
@@ -0,0 +1,134 @@
1
+ # AGENTS.md — `@sodax/dapp-kit` v2
2
+
3
+ > Tool-neutral entry point for any AI coding agent assisting a consumer of `@sodax/dapp-kit`. If you're at `node_modules/@sodax/dapp-kit/ai-exported/AGENTS.md`, you are in the right place — everything you need is reachable from here without leaving the npm tarball.
4
+
5
+ ## Project
6
+
7
+ `@sodax/dapp-kit` is a React hooks library that wraps `@sodax/sdk` with React Query. It provides ~95 hooks across 11 feature domains (swap, money market, staking, bridge, dex, migration, partner, recovery, bitcoin/Radfi, backend queries, shared) for consumer dApps. It is **React-only** — Node.js scripts and backend services use `@sodax/sdk` directly.
8
+
9
+ This package is **v2**. v2 was a deep canonicalization pass over v1's hook shapes — single-object params, mandatory `mutateAsyncSafe`, hook-owned invalidations, throw-on-`Result.!ok` inside `mutationFn`, canonical queryKey/mutationKey conventions. Plus the entire SDK underneath was reshaped (chain-key-driven routing, `Result<T>` everywhere, `WalletProviderSlot<K, Raw>`). Code written against v1 dapp-kit will not compile against v2.
10
+
11
+ ## When to read what
12
+
13
+ ```
14
+ Are you writing NEW code with v2 dapp-kit? → integration/ai-rules.md, then integration/
15
+ Are you porting EXISTING v1 code to v2? → migration/ai-rules.md, then migration/
16
+ Just need a hook table or a queryKey rule? → integration/reference/
17
+ Need to install + wire providers? → integration/recipes/setup.md
18
+ Hit a feature you don't know how to scaffold? → integration/recipes/<feature>.md
19
+ ```
20
+
21
+ If a consumer's repo has both v1 call sites and a request to extend with new code, do migration first. Stale v1 patterns leak into new code if you skip it.
22
+
23
+ **Always start with `ai-rules.md` for the tree you're working in** — it's the consolidated DO / DO NOT / workflow / stop-conditions guide that prevents the most common v2 traps. Read it once, then dive into the per-feature docs or recipes.
24
+
25
+ ## Top-level layout
26
+
27
+ ```
28
+ ai-exported/
29
+ ├── AGENTS.md # You are here
30
+ ├── integration/ # How to use v2 dapp-kit (new consumers)
31
+ │ ├── README.md # Index for this tree
32
+ │ ├── ai-rules.md # DO / DO NOT / workflow — read before per-feature docs
33
+ │ ├── quickstart.md # Install + wire providers + first feature
34
+ │ ├── architecture.md # Hook shapes, queryKey conventions, useSafeMutation, unwrapResult, Result<T>
35
+ │ ├── features/ # Per-feature reference docs (one file per major feature group)
36
+ │ │ ├── README.md
37
+ │ │ ├── swap.md, money-market.md, staking.md, bridge.md, dex.md
38
+ │ │ ├── migration.md # ICX/bnUSD/BALN migration hooks
39
+ │ │ ├── bitcoin.md # Radfi (dapp-kit-unique)
40
+ │ │ └── auxiliary-services.md # partner + recovery + backend queries + shared
41
+ │ ├── recipes/ # Copy-paste patterns
42
+ │ │ ├── README.md
43
+ │ │ ├── setup.md, wallet-connectivity.md
44
+ │ │ ├── swap.md, money-market.md, staking.md, bridge.md, dex.md
45
+ │ │ ├── migration.md, bitcoin.md, backend-queries.md
46
+ │ │ ├── mutation-error-handling.md # mutate / mutateAsync / mutateAsyncSafe
47
+ │ │ ├── observability.md # createSodaxQueryClient, meta.silent
48
+ │ │ └── invalidations.md # hook-owned vs consumer
49
+ │ └── reference/ # Lookup tables
50
+ │ ├── README.md
51
+ │ ├── hooks-index.md # Comprehensive hook table
52
+ │ ├── querykey-conventions.md # camelCase, feature-prefix, default mutationKey
53
+ │ ├── public-api.md # What @sodax/dapp-kit exports + import rules
54
+ │ └── glossary.md # ReadHookParams, MutationHookParams, SafeUseMutationResult, etc.
55
+ └── migration/ # How to port v1 → v2 dapp-kit
56
+ ├── README.md # Overview + reading order + glossary
57
+ ├── ai-rules.md # DO / DO NOT / workflow for porting agents
58
+ ├── checklist.md # Top-down cross-cutting steps
59
+ ├── breaking-changes/
60
+ │ ├── hook-signatures.md # Single-arg policy + ReadHookParams/MutationHookParams
61
+ │ ├── result-handling.md # Result<T> success-path → throws; mutateAsyncSafe
62
+ │ ├── querykey-conventions.md # camelCase + default mutationKey
63
+ │ └── sdk-leakage.md # Cross-links to SDK ai-exported migration tree
64
+ ├── features/ # Per-feature porting playbooks (mirror integration/features/)
65
+ │ ├── README.md
66
+ │ ├── swap.md, money-market.md, staking.md, bridge.md, dex.md
67
+ │ ├── migration.md, bitcoin.md, auxiliary-services.md
68
+ ├── recipes.md # Codemods + adapters for incremental migration
69
+ └── reference/
70
+ ├── README.md
71
+ ├── deleted-hooks.md # useSpokeProvider, invalidateMmQueries, legacy useMigrate
72
+ ├── renamed-hooks.md
73
+ └── error-shape-crosswalk.md
74
+ ```
75
+
76
+ ## v2 in one minute
77
+
78
+ 1. **Hooks accept a single object with one or two top-level keys.** Mutation hooks take only `{ mutationOptions }` at hook-init; query hooks take `{ params, queryOptions }`. ALL domain inputs (`params`, `walletProvider`, per-call config) flow through `mutate(vars)` for mutations.
79
+ 2. **Every mutation hook returns `SafeUseMutationResult`** — extends React Query's `UseMutationResult` with `mutateAsyncSafe(vars): Promise<Result<TData>>` (never rejects). Use `mutateAsyncSafe` for sequenced flows; `mutateAsync` for try/catch flows; `mutate` for fire-and-forget render-driven flows.
80
+ 3. **`mutationFn` throws on SDK `!ok`.** dapp-kit calls `unwrapResult` on the SDK's `Result<T>`, throwing on failure. This makes React Query's native error model engage (`isError`, `error`, `onError`, `retry`, devtools). `mutateAsyncSafe` packages the throw back into `Result<T>` for ergonomic branching.
81
+ 4. **Hook-owned invalidations.** Each mutation hook invalidates the relevant query keys in its `onSuccess`, derived from `vars`. Consumer-provided `onSuccess` runs after. v1's manual `invalidateMmQueries` utilities are gone.
82
+ 5. **Canonical queryKey shape.** `[feature, action, ...identifiers]`. First segment matches the directory name (`swap`, `mm`, `bridge`, `staking`, `dex`, `bitcoin`, `partner`, `recovery`, `backend`, `shared`, `migrate`). camelCase. Bigints stringified. Mechanically enforced by `_mutationContract.test.ts`.
83
+
84
+ ## Top 5 v1 → v2 traps
85
+
86
+ 1. **Reaching for `useSpokeProvider`.** It's deleted. Pass `walletProvider` from `useWalletProvider({ xChainId: chainKey })` (`@sodax/wallet-sdk-react`) directly into `mutate(vars)`. The chain key on the action params is what routes — there is no provider class to derive.
87
+ 2. **Treating mutation `data` as `Result<T>`.** v2's `mutationFn` unwraps before resolving — `data` is the unwrapped success value (e.g. `SwapResponse`, `TxHashPair`). For SDK failures, look at `mutation.error` or use `mutateAsyncSafe` for the `Result<T>` shape.
88
+ 3. **Forgetting `try/catch` on `mutateAsync`.** v2's `mutateAsync` rejects on SDK `!ok`. If you don't `try/catch`, you'll leak unhandled rejections on user-rejects. Prefer `mutateAsyncSafe` (never rejects).
89
+ 4. **Hook-level `spokeProvider` / `params`.** v1 hooks took these positionally or at hook-init. v2 hooks take only `{ mutationOptions }` (mutations) or `{ params, queryOptions }` (queries). All domain inputs live in `mutate(vars)` for mutations.
90
+ 5. **Reading `xToken.xChainId` or hard-coding `*_MAINNET_CHAIN_ID`.** SDK leakage — these were renamed: `XToken.chainKey`, `ChainKeys.X_MAINNET`. The legacy `*_MAINNET_CHAIN_ID` constants are gone. See [`migration/breaking-changes/sdk-leakage.md`](migration/breaking-changes/sdk-leakage.md).
91
+
92
+ See `migration/README.md` for the complete trap list and `migration/breaking-changes/` for full v1↔v2 detail.
93
+
94
+ ## Public API contract
95
+
96
+ - Import only from the package root: `import { useSwap, SodaxProvider, createSodaxQueryClient } from '@sodax/dapp-kit'`.
97
+ - The package re-exports `@sodax/sdk`'s public surface — `ChainKeys`, `SodaxConfig`, types like `CreateIntentParams`, etc., are available from `@sodax/dapp-kit` directly. You may also import them from `@sodax/sdk`.
98
+ - Do **not** add `@sodax/types` to your dependencies — it's re-exported via `@sodax/sdk`.
99
+ - Do **not** deep-import from `dist/...`. Internal paths are not stable across releases.
100
+ - The published tarball ships `dist/` and `ai-exported/`. Do not rely on any other path being present.
101
+
102
+ ## Conventions agents must follow
103
+
104
+ - **Use `useSafeMutation`-built hooks** (i.e. dapp-kit's exported hooks). Never call React Query's `useMutation` directly inside a wrapper around a dapp-kit hook — consumers depend on `mutateAsyncSafe`.
105
+ - **Branch on `mutateAsyncSafe`'s `Result.ok`** for sequenced flows. The user-reject case is modal, not exceptional.
106
+ - **Use `ChainKeys.*` over hard-coded chain strings.** The set evolves per release.
107
+ - **Drop `spokeProvider`** anywhere it appears. It's not a v2 concept. `walletProvider` flows through `mutate(vars)` for signed flows; queries take it directly when needed (e.g. allowance reads).
108
+ - **Don't recreate hook-owned invalidations** at the call site. Each mutation hook already invalidates the relevant keys; consumer `onSuccess` runs after for any extra logic.
109
+ - **Conventional commits if generating commits** (`feat:`, `fix:`, `chore:`).
110
+
111
+ ## Enforcement — what CI catches (and what it doesn't)
112
+
113
+ Seven CI guards run on every PR. They catch syntactic + structural drift but NOT prose-level accuracy:
114
+
115
+ | Guard | What it enforces |
116
+ |---|---|
117
+ | `check:ai-exported` | Every `useFoo` reference resolves to a real export. |
118
+ | `check:ai-scope` | No imports from forbidden packages (`@sodax/wallet-sdk-core`, `@sodax/types` directly). |
119
+ | `check:ai-links` | Every relative link between markdown files resolves. |
120
+ | `check:ai-imports` | Every `import … from '@sodax/dapp-kit'` example typechecks. |
121
+ | `check:ai-snippets` | **Opt-out by default** — every ts/tsx code block is typechecked unless marked `// @ai-snippets-skip`. Catches call-shape drift. |
122
+ | `check:ai-keys` | Every `queryKey: [...]` / `mutationKey: [...]` literal (declarations + backticked-array table cells) matches source. Catches `'stakingInfo'` vs `'info'`-style drift. Opt-out via `<!-- ai-keys-allow -->` or `// ai-keys-allow`. |
123
+ | `check:ai-consistency` | Polling-interval claims (`"polls 3s"`, table cells like `useQuote \| 3s`) match the actual `refetchInterval` in source. Opt-out via `<!-- ai-consistency-allow -->`. |
124
+
125
+ If you're authoring a new doc page, write code samples that include explicit imports (so `check:ai-snippets` validates them) and source-derived queryKeys (so `check:ai-keys` accepts them). When showing v1 anti-patterns or pseudocode, use the appropriate `*-allow` / `*-skip` marker — these are first-class affordances, not workarounds.
126
+
127
+ ## Pointers
128
+
129
+ - [`integration/README.md`](integration/README.md) — start here for any new v2 dapp-kit work.
130
+ - [`migration/README.md`](migration/README.md) — start here for any v1 → v2 dapp-kit port.
131
+ - [`integration/recipes/setup.md`](integration/recipes/setup.md) — install + wire providers.
132
+ - [`integration/recipes/mutation-error-handling.md`](integration/recipes/mutation-error-handling.md) — picking call shapes (`mutate` / `mutateAsync` / `mutateAsyncSafe`).
133
+ - [`integration/architecture.md`](integration/architecture.md) — full design rationale + canonical hook shapes.
134
+ - [`../../sdk/ai-exported/AGENTS.md`](../../sdk/ai-exported/AGENTS.md) — the underlying Core SDK's tree (resolves correctly in `node_modules/@sodax/`-layout). Useful for SDK-leakage migration topics.
@@ -0,0 +1,49 @@
1
+ # Integration — `@sodax/dapp-kit` v2
2
+
3
+ This tree documents v2 of the dapp-kit React hooks for **new consumers** building against it. If you're porting v1 code, start at [`../migration/README.md`](../migration/README.md) instead.
4
+
5
+ ## Files in this tree
6
+
7
+ | File | What's in it |
8
+ |---|---|
9
+ | [`ai-rules.md`](ai-rules.md) | **Read first.** DO / DO NOT / workflow / stop-conditions for AI agents writing v2 dapp-kit code. |
10
+ | [`quickstart.md`](quickstart.md) | Install, wire providers, get a wallet provider, run your first mutation. |
11
+ | [`architecture.md`](architecture.md) | Every v2 design concept the hooks rest on: ReadHookParams / MutationHookParams, useSafeMutation, unwrapResult, queryKey conventions, mutateAsyncSafe semantics, createSodaxQueryClient. |
12
+ | [`features/swap.md`](features/swap.md) | Swap hooks: `useQuote`, `useSwap`, `useSwapAllowance`, `useSwapApprove`, limit orders, status polling. |
13
+ | [`features/money-market.md`](features/money-market.md) | Money market hooks: `useSupply`, `useBorrow`, `useWithdraw`, `useRepay`, allowance/approve, reserves data. |
14
+ | [`features/staking.md`](features/staking.md) | Staking hooks: `useStake`, `useUnstake`, `useInstantUnstake`, `useClaim`, `useCancelUnstake`, info/ratio reads. |
15
+ | [`features/bridge.md`](features/bridge.md) | Bridge hooks: `useBridge`, allowance/approve, bridgeable amount/tokens. |
16
+ | [`features/dex.md`](features/dex.md) | DEX hooks: deposit/withdraw, supply/decrease liquidity, claim rewards, position info, pools. |
17
+ | [`features/migration.md`](features/migration.md) | Migration hooks: ICX/bnUSD/BALN forward + reverse, allowance/approve. |
18
+ | [`features/bitcoin.md`](features/bitcoin.md) | Radfi hooks (dapp-kit-unique): session, trading wallet, fund/withdraw, UTXOs. |
19
+ | [`features/auxiliary-services.md`](features/auxiliary-services.md) | Partner, recovery, backend queries, shared (xBalances, gas estimation, trustlines). |
20
+ | [`recipes/`](recipes/) | Copy-paste patterns: setup, wallet connectivity, per-feature flows, mutation error handling, observability, invalidations. |
21
+ | [`reference/`](reference/) | Lookup tables: full hook index, queryKey conventions, public API surface, glossary. |
22
+
23
+ ## Reading order for a new integrator
24
+
25
+ 1. **[`ai-rules.md`](ai-rules.md)** — agent rules, before any code.
26
+ 2. **[`quickstart.md`](quickstart.md)** — get providers wired and a button rendering.
27
+ 3. **[`architecture.md`](architecture.md)** — understand `useSafeMutation` / `mutateAsyncSafe` / hook shapes before writing call sites.
28
+ 4. **[`recipes/`](recipes/)** — pick the patterns you need (mutation error handling, observability, invalidations).
29
+ 5. **[`features/<x>.md`](features/)** — read the file for the feature you're integrating (reference shape; pair with the matching recipe in `recipes/<x>.md` for working examples).
30
+ 6. **[`reference/`](reference/)** — keep open while writing for table lookups.
31
+
32
+ ## Cross-references to migration
33
+
34
+ If your project also has v1 dapp-kit call sites, port them first using:
35
+
36
+ - [`../migration/README.md`](../migration/README.md) — overview, reading order, and v1↔v2 glossary.
37
+ - [`../migration/checklist.md`](../migration/checklist.md) — top-down cross-cutting checklist.
38
+ - [`../migration/breaking-changes/`](../migration/breaking-changes/) — the four cross-cutting changes (hook-signatures, result-handling, queryKey-conventions, sdk-leakage).
39
+ - [`../migration/features/`](../migration/features/) — per-feature playbooks in lockstep with `integration/features/` here.
40
+
41
+ The naming rule: **every file in `integration/features/` has a sibling in `migration/features/` with the same filename.** When you're deep in one, the other is one path-swap away.
42
+
43
+ ## Cross-references to the underlying SDK
44
+
45
+ `@sodax/dapp-kit` re-exports `@sodax/sdk` at the package root, so most types you'd reach for (`ChainKeys`, `SodaxConfig`, `CreateIntentParams`, `XToken`, `Result`, `SodaxError`) are available from `@sodax/dapp-kit` directly. The Core SDK has its own ai-exported tree at [`../../../sdk/ai-exported/`](../../../sdk/ai-exported/) (resolves correctly in `node_modules/@sodax/`-layout). Useful for:
46
+
47
+ - The full SDK migration playbook for v1→v2 (chain-key terminology, `Result<T>` semantics, ConfigService) — referenced from [`../migration/breaking-changes/sdk-leakage.md`](../migration/breaking-changes/sdk-leakage.md).
48
+ - Architectural concepts that surface through hook signatures (`SodaxError<C>` vocabulary, `WalletProviderSlot<K, Raw>` discriminator semantics).
49
+ - Backend / Node.js usage patterns (where dapp-kit isn't applicable).
@@ -0,0 +1,79 @@
1
+ # AI rules — `@sodax/dapp-kit` integration
2
+
3
+ DO / DO NOT / workflow / stop conditions for AI agents writing v2 dapp-kit code. Read this **before** the per-feature docs — these rules prevent the most common load-bearing v2 traps.
4
+
5
+ ## Workflow (do these in order)
6
+
7
+ 1. **Survey the project before touching code.**
8
+
9
+ ```bash
10
+ pnpm tsc --noEmit # baseline typecheck
11
+ grep -rE '@sodax/(dapp-kit|sdk|wallet-sdk-react)' --include='*.ts' --include='*.tsx' src/ # see what's already imported
12
+ ```
13
+
14
+ 2. **Wire providers first if not already wired.** [`recipes/setup.md`](recipes/setup.md). Provider stack: `SodaxProvider > QueryClientProvider > SodaxWalletProvider > YourApp`.
15
+ 3. **Pick `mutate` / `mutateAsync` / `mutateAsyncSafe` deliberately.** See [`recipes/mutation-error-handling.md`](recipes/mutation-error-handling.md). Default to `mutateAsyncSafe` for sequenced flows.
16
+ 4. **Branch on `result.ok` for `mutateAsyncSafe` results, or on `mutation.isError` / `mutation.error`** for fire-and-forget. Never assume mutation success.
17
+ 5. **Use `useWalletProvider({ xChainId: chainKey })` from `@sodax/wallet-sdk-react` for every signed flow.** Pass the result into `mutate(vars).walletProvider`. Don't create or import any `*SpokeProvider` class — those don't exist in v2.
18
+
19
+ ## DO
20
+
21
+ - **DO** call dapp-kit's exported hooks (which wrap `useSafeMutation`). Never call React Query's `useMutation` directly inside a wrapper around a dapp-kit hook — consumers depend on `mutateAsyncSafe`.
22
+ - **DO** branch on `mutateAsyncSafe`'s `Result.ok` for sequenced flows (`if (!hasAllowance) await approve(...); await action(...);`). User-rejects are modal, not exceptional.
23
+ - **DO** use `ChainKeys.X_MAINNET` constants for chain identifiers. Never hard-code chain key strings (`'sonic'`, `'0xa4b1.arbitrum'`).
24
+ - **DO** import everything from `@sodax/dapp-kit` (or `@sodax/sdk` for SDK-only types and constants — `@sodax/dapp-kit` re-exports them anyway).
25
+ - **DO** preserve literal chain keys in generic positions where possible (e.g. `srcChainKey: ChainKeys.ETHEREUM_MAINNET as const`) — TypeScript narrowing flows from the literal, including the `walletProvider` parameter type.
26
+ - **DO** use the canonical hook shape: queries take `{ params, queryOptions }`; mutations take only `{ mutationOptions }` and flow domain inputs through `mutate(vars)`.
27
+ - **DO** compose `onSuccess` instead of replacing it: `mutationOptions: { onSuccess: (data, vars, ctx) => myExtra(...) }` runs AFTER dapp-kit's hook-owned invalidations.
28
+
29
+ ## DO NOT
30
+
31
+ - **DO NOT** call `useSpokeProvider` — it's deleted. v1 React consumers got a `SpokeProvider` from this hook; v2 has no such concept. Pass `walletProvider` directly into `mutate(vars)`.
32
+ - **DO NOT** treat mutation `data` as `Result<T>`. `mutationFn` calls `unwrapResult` and throws on `!ok`; `data` is the unwrapped success value (e.g. `SwapResponse`, `TxHashPair`). For SDK failures, look at `mutation.error` or use `mutateAsyncSafe`.
33
+ - **DO NOT** call `mutateAsync` without `try/catch`. It rejects on SDK `!ok`; an unhandled rejection lands in the global handler. Prefer `mutateAsyncSafe` for sequenced flows.
34
+ - **DO NOT** put `params`, `walletProvider`, or per-call config at the hook-init level. They go in `mutate(vars)` for mutations and in `params` for queries.
35
+ - **DO NOT** override `queryKey`, `queryFn`, or `enabled` via `queryOptions` — the hook owns those. The `queryOptions` slot is typed `Omit<UseQueryOptions, 'queryKey' | 'queryFn' | 'enabled'>`.
36
+ - **DO NOT** override `mutationFn` via `mutationOptions` — the hook owns it. Type prevents this.
37
+ - **DO NOT** import from `@sodax/dapp-kit/dist/...`. Deep-imports unstable; only the package root barrel is the public contract.
38
+ - **DO NOT** add `@sodax/types` as a separate dependency. It's re-exported transitively via `@sodax/sdk` (which dapp-kit re-exports). Adding it independently invites version skew.
39
+ - **DO NOT** recreate the v1 `invalidateMmQueries(...)` utility or anything similar. Each mutation hook invalidates the relevant keys in its own `onSuccess`. Add cross-feature invalidations via consumer `onSuccess`.
40
+ - **DO NOT** destructure cross-chain mutation results as arrays — `[a, b] = result.value` is wrong. The shape is `TxHashPair = { srcChainTxHash, dstChainTxHash }` (object). This applies to `useBridge`, `useStake`/`useUnstake`/etc., `useDexDeposit`/`useDexWithdraw`, all four MM mutations, and all four migration mutations.
41
+ - **DO NOT** use legacy chain-id constants (`BSC_MAINNET_CHAIN_ID`, etc.). They're gone in v2 — use `ChainKeys.X_MAINNET`.
42
+
43
+ ## Stop conditions (defer to user)
44
+
45
+ | Signal | Why stop |
46
+ |---|---|
47
+ | User wants a chain not in `ChainKeys.*` | Adding a new chain requires SDK-level changes — out of scope for consumer code. |
48
+ | User wants to use React Server Components / RSC patterns | dapp-kit is client-side React (uses React Query, hooks, browser APIs). Server Components don't run hooks. Tell the user the dapp-kit code goes in client components. |
49
+ | User wants to skip the wallet-sdk-react integration | If they have their own wallet abstraction, it's possible — they need to construct objects satisfying `I*WalletProvider` interfaces from `@sodax/sdk`. Refer them to `@sodax/sdk`'s ai-exported tree for non-React patterns. |
50
+ | User wants to use v1 dapp-kit (positional args, `useSpokeProvider`, `Result<T>` in success path) | Tell them to either upgrade or stay on v1. The shapes are not compatible. |
51
+
52
+ ## Verification protocol
53
+
54
+ Before declaring a dapp-kit integration "done":
55
+
56
+ ```bash
57
+ # 1. Type-check the consumer.
58
+ pnpm -C <consumer> tsc --noEmit
59
+
60
+ # 2. Confirm no leftover v1 patterns (if porting).
61
+ grep -rE '\buseSpokeProvider\b|\binvalidateMmQueries\b|_MAINNET_CHAIN_ID\b|\bxChainId\b' src/
62
+
63
+ # 3. Confirm all SDK-level mutation results are handled (either mutateAsyncSafe + result.ok branch
64
+ # or mutateAsync wrapped in try/catch).
65
+ grep -rE 'mutateAsync\(' src/ | grep -v 'try\|catch\|mutateAsyncSafe'
66
+
67
+ # 4. Confirm all hooks are imported from @sodax/dapp-kit, not deep paths.
68
+ # (matches `'@sodax/dapp-kit/dist'` and similar deep-import patterns)
69
+ grep -rE "@sodax/dapp-kit/[a-z]" src/ # zero hits expected (only the root path is public)
70
+ ```
71
+
72
+ ## Done criteria
73
+
74
+ - [ ] `SodaxProvider` + `QueryClientProvider` (preferably `createSodaxQueryClient()`) wired at the app root.
75
+ - [ ] Every mutation invocation uses `mutateAsyncSafe` (preferred), `mutateAsync` wrapped in `try/catch`, or fire-and-forget `mutate` with state read in render.
76
+ - [ ] No `useSpokeProvider`, no `invalidateMmQueries`, no `*_MAINNET_CHAIN_ID` constants, no `xChainId` outside known v2 hooks (e.g. `useXBalances` still uses `xChainId`).
77
+ - [ ] No `import` from `@sodax/dapp-kit/dist/...` or any other deep path.
78
+ - [ ] No standalone `@sodax/types` dependency in `package.json`.
79
+ - [ ] Consumer typecheck (`pnpm tsc --noEmit`) is clean.
@@ -0,0 +1,274 @@
1
+ # Architecture — `@sodax/dapp-kit` v2
2
+
3
+ Every v2 design concept the hooks rest on, in one TOC-navigable file. Read it once before writing call sites — most of the v1→v2 breakage and most of the new-code traps come from misunderstanding one of these.
4
+
5
+ ## Five pieces hold it together
6
+
7
+ 1. **Two canonical hook shapes.**
8
+ - Read hooks accept `{ params, queryOptions }` typed via `ReadHookParams<TData, TParams>`.
9
+ - Mutation hooks accept `{ mutationOptions }` typed via `MutationHookParams<TData, TVars>` and return `SafeUseMutationResult<TData, Error, TVars>`.
10
+ - All domain inputs (params, walletProvider, apiConfig) flow through `mutate(vars)`, never the hook arg.
11
+ 2. **`useSafeMutation` foundation.** Every mutation hook calls `useSafeMutation(...)` (drop-in for React Query's `useMutation`), which augments the result with `mutateAsyncSafe(vars): Promise<Result<TData>>` — never rejects.
12
+ 3. **`unwrapResult` translation.** SDK service methods return `Result<T>`. `unwrapResult` converts to thrown errors inside `mutationFn` so React Query's native error model engages (`isError`, `error`, `onError`, `retry`, devtools) for SDK failures.
13
+ 4. **`createSodaxQueryClient`** (optional). Factory that returns a `QueryClient` with a `MutationCache.onError` hook giving consumers a single observability seam, plus a `meta.silent` per-mutation opt-out.
14
+ 5. **Mechanical enforcement.** `_mutationContract.test.ts` asserts the canonical shape on every mutation hook (`useSafeMutation` not `useMutation`, default `mutationKey` before the spread, `mutationFn` after, `unwrapResult` translation, feature-prefix queryKey rule).
15
+
16
+ ## Provider stack
17
+
18
+ `SodaxProvider` wraps the app and provides:
19
+ - The `Sodax` SDK instance
20
+ - RPC configuration for all chains
21
+ - Hub provider access
22
+
23
+ ```tsx
24
+ // @ai-snippets-skip
25
+ <SodaxProvider config={sodaxConfig}> {/* SDK instance + RPC config */}
26
+ <QueryClientProvider client={queryClient}> {/* prefer createSodaxQueryClient() */}
27
+ <SodaxWalletProvider config={walletConfig}> {/* from @sodax/wallet-sdk-react (optional) */}
28
+ <YourApp />
29
+ </SodaxWalletProvider>
30
+ </QueryClientProvider>
31
+ </SodaxProvider>
32
+ ```
33
+
34
+ `SodaxProvider` does **not** depend on `@sodax/wallet-sdk-react` — wallet state is wired side-by-side. Backend / non-React consumers (Node scripts, bots) bypass dapp-kit entirely and use `@sodax/sdk` directly with their own wallet implementation.
35
+
36
+ ### `createSodaxQueryClient`
37
+
38
+ Returns a `QueryClient` pre-wired with a `MutationCache.onError` hook for global mutation observability. Default behavior: logs every mutation failure to console as `[sodax] Mutation error: <error>`.
39
+
40
+ ```tsx
41
+ import { createSodaxQueryClient } from '@sodax/dapp-kit';
42
+
43
+ // Default
44
+ const queryClientDefault = createSodaxQueryClient();
45
+
46
+ // Wire to your own logger
47
+ const queryClientWithSentry = createSodaxQueryClient({
48
+ onMutationError: (e) => Sentry.captureException(e),
49
+ });
50
+
51
+ // Disable entirely
52
+ const queryClientSilent = createSodaxQueryClient({ onMutationError: () => {} });
53
+ ```
54
+
55
+ Per-mutation opt-out via `meta.silent`:
56
+
57
+ ```tsx
58
+ // @ai-snippets-skip
59
+ const swap = useSwap({
60
+ mutationOptions: {
61
+ meta: { silent: true },
62
+ onError: (e) => toast.error(e.message),
63
+ },
64
+ });
65
+ ```
66
+
67
+ This is **observability**, not prevention. It does NOT detect "unhandled" rejections — it fires for **every** mutation failure regardless of whether the consumer caught the rejection or registered a per-hook `onError`. To prevent unhandled rejections, use `mutateAsyncSafe`.
68
+
69
+ ## Read hook shape (mandatory)
70
+
71
+ All read-only hooks accept a single object with exactly two top-level keys: `params` (SDK-feature-domain inputs — the *what* being fetched) and `queryOptions` (React Query knobs — the *how*).
72
+
73
+ Rules:
74
+
75
+ - **Single params object with two top-level keys.** `{ params, queryOptions }`. Nothing else at the top level.
76
+ - **Use the shared types.** Params type MUST be `ReadHookParams<TData, TParams>`; the `queryOptions` slot is typed `ReadQueryOptions<TData>` (which is `Omit<UseQueryOptions<TData, Error>, 'queryKey' | 'queryFn' | 'enabled'>`). Hook owns `queryKey`, `queryFn`, `enabled` — never consumer-overridable.
77
+ - **Hierarchical query keys.** `[feature, action, ...inputs]`. Stringify bigints with `.toString()`.
78
+ - **No-input hooks.** Type as `ReadHookParams<TData>` (no `TParams` generic) and accept the whole arg as optional, defaulting to `{}` for ergonomic no-arg calls (`useStakingConfig({})`).
79
+
80
+ Canonical example:
81
+
82
+ ```ts
83
+ // @ai-snippets-skip — definition-shape illustration; function body elided with `// ...`
84
+ import type { PoolData, PoolKey } from '@sodax/sdk';
85
+ import { useQuery, type UseQueryResult } from '@tanstack/react-query';
86
+ import type { ReadHookParams } from '@sodax/dapp-kit';
87
+
88
+ export type UsePoolDataParams = ReadHookParams<PoolData, { poolKey: PoolKey | null }>;
89
+
90
+ export function usePoolData({ params, queryOptions }: UsePoolDataParams = {}): UseQueryResult<PoolData, Error> {
91
+ // ... uses sodax.dex.clService.getPoolData internally
92
+ }
93
+ ```
94
+
95
+ Call site:
96
+
97
+ ```ts
98
+ // @ai-snippets-skip
99
+ const { data } = usePoolData({ params: { poolKey } });
100
+ ```
101
+
102
+ ## Mutation hook shape (mandatory)
103
+
104
+ All mutation hooks follow the **zero-domain-param** policy: the hook function takes a single optional argument with exactly one top-level key — `mutationOptions` — and ALL domain inputs (`params`, `walletProvider`, per-call config, etc.) flow through `mutate(vars)` via the typed `TVars` payload.
105
+
106
+ Three shared utilities underpin every mutation hook:
107
+
108
+ - **`useSafeMutation(options)`** — drop-in for React Query's `useMutation`. Returns `SafeUseMutationResult<TData, Error, TVars>` (extends `UseMutationResult` with `mutateAsyncSafe`).
109
+ - **`unwrapResult(result)`** — `Result<T>` → throw `error` on `!ok`, return `value` on `ok`. Use inside `mutationFn`.
110
+ - **`toResult(promise)`** — pure helper that catches `Promise<T>` rejection and packs into `Result<T>`. Used internally by `useSafeMutation`.
111
+
112
+ Rules:
113
+
114
+ - **Use `useSafeMutation`, not `useMutation`.** Every dapp-kit mutation hook MUST call `useSafeMutation`. The wrapper augments the result with `mutateAsyncSafe`, which consumers depend on.
115
+ - **One optional top-level arg.** `useFoo({ mutationOptions } = {}): SafeUseMutationResult<TData, Error, TVars>`.
116
+ - **Use the shared types.** `MutationHookParams<TData, TVars>`; return `SafeUseMutationResult<TData, Error, TVars>`; `mutationOptions` typed `MutationHookOptions<TData, TVars>` (which is `Omit<UseMutationOptions<TData, Error, TVars>, 'mutationFn'>`).
117
+ - **Hook owns `mutationFn`.** Never consumer-overridable.
118
+ - **`mutationFn` throws on SDK `!ok`.** Use `unwrapResult` from `@sodax/dapp-kit`. SDK returns `Result<T>`; the hook unwraps to `T` so React Query's native error model engages. `TData` is the unwrapped success type, NOT `Result<T>`.
119
+ - **Default `mutationKey` BEFORE the spread**, then spread `...mutationOptions`, then `mutationFn` last. Order matters: default key is overridable by consumer (spread wins), but `mutationFn` is hook-owned.
120
+ - **Compose `onSuccess` (and any other callbacks the hook itself defines).** Invalidations are correctness logic owned by the hook. Inside the hook's `onSuccess`, run invalidations first, then `await mutationOptions?.onSuccess?.(...)` so consumer hooks still fire.
121
+ - **Derive invalidation keys from `vars`, not closures.** `(data, vars, ctx) => ...` and read `vars.params.srcChainKey`.
122
+ - **All domain inputs go in `TVars`.** No `params`, `walletProvider`, `apiConfig` at the hook level. Pushing inputs into `mutate(vars)` lets a single hook serve many call shapes without remounting.
123
+
124
+ Canonical example:
125
+
126
+ ```ts
127
+ import type { SwapActionParams, SwapResponse, SpokeChainKey } from '@sodax/sdk';
128
+ import { useQueryClient } from '@tanstack/react-query';
129
+ import {
130
+ useSodaxContext,
131
+ useSafeMutation,
132
+ unwrapResult,
133
+ type MutationHookParams,
134
+ type SafeUseMutationResult,
135
+ } from '@sodax/dapp-kit';
136
+
137
+ export type UseSwapVars<K extends SpokeChainKey = SpokeChainKey> = Omit<SwapActionParams<K, false>, 'raw'>;
138
+
139
+ export function useSwap<K extends SpokeChainKey = SpokeChainKey>({
140
+ mutationOptions,
141
+ }: MutationHookParams<SwapResponse, UseSwapVars<K>> = {}): SafeUseMutationResult<SwapResponse, Error, UseSwapVars<K>> {
142
+ const { sodax } = useSodaxContext();
143
+ const queryClient = useQueryClient();
144
+
145
+ return useSafeMutation<SwapResponse, Error, UseSwapVars<K>>({
146
+ mutationKey: ['swap'],
147
+ ...mutationOptions,
148
+ mutationFn: async vars => unwrapResult(await sodax.swaps.swap({ ...vars, raw: false })),
149
+ onSuccess: async (data, vars, ctx) => {
150
+ queryClient.invalidateQueries({ queryKey: ['shared', 'xBalances', vars.params.srcChainKey] });
151
+ queryClient.invalidateQueries({ queryKey: ['shared', 'xBalances', vars.params.dstChainKey] });
152
+ await mutationOptions?.onSuccess?.(data, vars, ctx);
153
+ },
154
+ });
155
+ }
156
+ ```
157
+
158
+ ## Choosing `mutate` / `mutateAsync` / `mutateAsyncSafe`
159
+
160
+ | Method | Returns | Rejects? | When to use |
161
+ |---|---|---|---|
162
+ | `mutate(vars)` | `void` (fire-and-forget) | Never | Button-click handlers reading `isPending` / `isError` / `error` in render. Consumer-supplied `onError` fires; React Query owns state. |
163
+ | `mutateAsync(vars)` | `Promise<TData>` | **Yes** on `!ok` | Imperative chains where you want exception flow. **MUST be inside `try/catch`.** |
164
+ | `mutateAsyncSafe(vars)` | `Promise<Result<TData>>` | **Never** | Imperative chains with explicit branching, no exception flow. Same React Query state under the hood. |
165
+
166
+ `mutateAsyncSafe` is the **recommended default** for sequenced flows — the user-reject case is the modal failure mode in dApps, not exceptional, and `Result<T>`-style branching reads cleaner than exception flow control.
167
+
168
+ ```tsx
169
+ // @ai-snippets-skip
170
+ // fire-and-forget — read state in render
171
+ const m = useSwap();
172
+ <button onClick={() => m.mutate({ params, walletProvider })}>Swap</button>
173
+
174
+ // throws — for chains where you want exception flow
175
+ const { mutateAsync } = useSwap();
176
+ try { const r = await mutateAsync({ params, walletProvider }); /* … */ }
177
+ catch (e) { toast(e instanceof Error ? e.message : 'Swap failed'); }
178
+
179
+ // safe — for chains where you want explicit branching, no try/catch
180
+ const { mutateAsyncSafe } = useSwap();
181
+ const result = await mutateAsyncSafe({ params, walletProvider });
182
+ if (!result.ok) { toast(result.error.message); return; }
183
+ const { intent, intentDeliveryInfo } = result.value;
184
+ ```
185
+
186
+ ## SDK Result handling
187
+
188
+ Every public SDK service method returns `Result<T> = { ok: true; value: T } | { ok: false; error: Error | unknown }` and never throws. dapp-kit translates that contract into the React Query contract by **throwing `result.error` on `!ok` inside `mutationFn`.**
189
+
190
+ Why throw?
191
+ - React Query's `isError`, `error`, `onError`, `retry`, `throwOnError`, devtools all key off `mutationFn` throwing.
192
+ - Consumers had to remember to branch on `data.ok` inside every `onSuccess` to avoid running success logic on a failed swap. Forgetting was easy and silent.
193
+ - Hook-owned invalidations (in `onSuccess`) used to fire on SDK failure too, burning RPC traffic on every failed click.
194
+
195
+ After translating, the public hook signature is `SafeUseMutationResult<T, Error, TVars>`. `data` is the unwrapped success value (e.g. `SwapResponse`, `TxHashPair`); SDK failures arrive via `mutation.error` exactly like any other thrown error. Call sites pick from three call shapes (above).
196
+
197
+ The dual API means consumers never have to choose between React Query's error model and `Result<T>` ergonomics — both are exposed by the same hook.
198
+
199
+ ## queryKey / mutationKey conventions (mandatory)
200
+
201
+ Every `queryKey` and `mutationKey` follows the same structural rule. Enforced by `_mutationContract.test.ts` for mutation keys; reviewer-enforced for query keys.
202
+
203
+ **Rule 1 — first segment is the feature directory name.** No exceptions.
204
+
205
+ | Hook directory | First segment |
206
+ |---|---|
207
+ | `backend/` | `'backend'` |
208
+ | `bitcoin/` | `'bitcoin'` |
209
+ | `bridge/` | `'bridge'` |
210
+ | `dex/` | `'dex'` |
211
+ | `mm/` | `'mm'` |
212
+ | `partner/` | `'partner'` |
213
+ | `recovery/` | `'recovery'` |
214
+ | `shared/` | `'shared'` |
215
+ | `staking/` | `'staking'` |
216
+ | `swap/` | `'swap'` |
217
+ | `migrate/` | `'migrate'` |
218
+
219
+ **Rule 2 — camelCase for all segments.** No kebab-case (`'btc-balance'`), no ad-hoc casing. Identifiers are camelCase string literals (`'tradingWalletBalance'`, `'submitSwapTx'`).
220
+
221
+ **Rule 3 — shape is `[feature, action, ...identifiers]`** in stable order: chain → token/asset → user → amount. Example: `['mm', 'allowance', srcChainKey, token, action]`.
222
+
223
+ **Rule 4 — bigints stringify** via `.toString()` before going into a key (React Query's hash uses `JSON.stringify`, which throws on raw bigints).
224
+
225
+ **Rule 5 — invalidate the narrowest key that could change.** If the mutation knows the affected `tokenId` / user / chain, scope the invalidation to it.
226
+
227
+ Worked examples:
228
+
229
+ ```ts
230
+ // @ai-snippets-skip
231
+ queryKey: ['mm', 'userReservesData', spokeChainKey, userAddress]
232
+ queryKey: ['mm', 'allowance', srcChainKey, token, action]
233
+ queryKey: ['shared', 'xBalances', xChainId, tokens, address]
234
+ mutationKey: ['mm', 'supply']
235
+ queryClient.invalidateQueries({ queryKey: ['dex', 'positionInfo', tokenId, poolKey] });
236
+ ```
237
+
238
+ ## Hook organization
239
+
240
+ ~95 hooks (41 mutations + ~50 queries + utilities) organized by feature domain in `src/hooks/`:
241
+
242
+ ```
243
+ hooks/
244
+ ├── shared/ # useSodaxContext, useSafeMutation, unwrapResult, useEstimateGas,
245
+ │ # useDeriveUserWalletAddress, useGetUserHubWalletAddress, useXBalances,
246
+ │ # useStellarTrustlineCheck, useRequestTrustline
247
+ ├── provider/ # useHubProvider
248
+ ├── swap/ # useQuote, useSwap, useStatus, useSwapAllowance, useSwapApprove,
249
+ │ # useCancelSwap, useCreateLimitOrder, useCancelLimitOrder
250
+ ├── mm/ # useSupply, useWithdraw, useBorrow, useRepay, useMMAllowance, useMMApprove,
251
+ │ # reserves data hooks (13 hooks total)
252
+ ├── bridge/ # useBridge, useBridgeAllowance, useBridgeApprove, bridgeable amounts/tokens
253
+ ├── staking/ # useStake, useUnstake, useInstantUnstake, useClaim, staking info hooks (~18)
254
+ ├── dex/ # usePools, useDexDeposit, useDexWithdraw, liquidity hooks (~13)
255
+ ├── bitcoin/ # useRadfiSession, fund/withdraw, UTXO management (~8)
256
+ ├── backend/ # Intent tracking, swap submission, orderbook, money market position queries (~13)
257
+ ├── partner/ # Partner fee claim, auto-swap preferences, token approval (6)
258
+ ├── recovery/ # useHubAssetBalances, useWithdrawHubAsset
259
+ └── migrate/ # useMigrateIcxToSoda, useRevertMigrateSodaToIcx, useMigratebnUSD,
260
+ # useMigrateBaln, useMigrationApprove, useMigrationAllowance
261
+ ```
262
+
263
+ Every mutation hook returns `SafeUseMutationResult` and is registered in `_mutationContract.test.ts`'s manifest. Adding a non-conformant hook is a CI failure.
264
+
265
+ ## Cross-references
266
+
267
+ - [`recipes/setup.md`](recipes/setup.md) — install + wire providers (worked example).
268
+ - [`recipes/wallet-connectivity.md`](recipes/wallet-connectivity.md) — `useWalletProvider`, balances.
269
+ - [`recipes/mutation-error-handling.md`](recipes/mutation-error-handling.md) — picking call shapes (worked examples).
270
+ - [`recipes/observability.md`](recipes/observability.md) — `createSodaxQueryClient` deep-dive.
271
+ - [`recipes/invalidations.md`](recipes/invalidations.md) — composing your own `onSuccess`.
272
+ - [`reference/querykey-conventions.md`](reference/querykey-conventions.md) — full key tables.
273
+ - [`features/`](features/) — per-feature reference (hook tables, types, gotchas).
274
+ - [`../../../sdk/ai-exported/integration/architecture.md`](../../../sdk/ai-exported/integration/architecture.md) — the underlying SDK architecture (`Result<T>`, `SodaxError<C>`, `WalletProviderSlot<K, Raw>`).
@@ -0,0 +1,29 @@
1
+ # Features — `@sodax/dapp-kit` v2
2
+
3
+ Per-feature reference docs. Each file documents the hooks, params types, return types, and feature-specific gotchas — but doesn't include extended worked examples (those live in [`../recipes/`](../recipes/)).
4
+
5
+ | File | Hook count | What's covered |
6
+ |---|---|---|
7
+ | [`swap.md`](swap.md) | 8 | Cross-chain swaps via the intent solver: `useQuote`, `useSwap`, allowance/approve, status polling, limit orders. |
8
+ | [`money-market.md`](money-market.md) | 13 | Lending/borrowing on the cross-chain MM: `useSupply`, `useBorrow`, `useWithdraw`, `useRepay`, allowance/approve, reserves data hooks. |
9
+ | [`staking.md`](staking.md) | ~18 | SODA → xSODA staking: `useStake`, `useUnstake`, `useInstantUnstake`, `useClaim`, `useCancelUnstake`, allowance/approve, info/ratio reads. |
10
+ | [`bridge.md`](bridge.md) | 5 | Cross-chain token bridging: `useBridge`, allowance/approve, bridgeable amount/tokens. |
11
+ | [`dex.md`](dex.md) | ~13 | Concentrated liquidity DEX: assets in/out, liquidity supply/decrease, claim rewards, position info, pool reads, param builders. |
12
+ | [`migration.md`](migration.md) | 6 | Token migration: `useMigrateIcxToSoda`, `useRevertMigrateSodaToIcx`, `useMigratebnUSD`, `useMigrateBaln`, allowance/approve. |
13
+ | [`bitcoin.md`](bitcoin.md) | ~8 | Radfi (dapp-kit-unique): session, trading wallet, fund/withdraw, UTXOs. |
14
+ | [`auxiliary-services.md`](auxiliary-services.md) | ~30 | Partner fee claiming, recovery, backend queries (intent tracking, orderbook, MM data), shared utilities (xBalances, gas, trustlines). |
15
+
16
+ ## Reference vs recipes
17
+
18
+ - **Files in this directory (`features/`)** are reference: hook tables, type signatures, return shapes, feature-specific gotchas. Read when you need to know "what's this hook's exact shape" or "what does this method return."
19
+ - **Files in [`../recipes/`](../recipes/)** are how-to: complete worked examples, end-to-end flows, opinionated patterns. Read when you want to copy-paste working code.
20
+
21
+ ## Pair-completeness
22
+
23
+ Every file in this directory has a sibling in [`../../migration/features/`](../../migration/features/) with the same filename — the v1→v2 porting playbook for that feature. When you're deep in one, the other is one path-swap away.
24
+
25
+ ## Cross-references
26
+
27
+ - [`../architecture.md`](../architecture.md) — design concepts that span every feature (hook shapes, queryKey conventions, `useSafeMutation`, `mutateAsyncSafe`, `unwrapResult`).
28
+ - [`../recipes/`](../recipes/) — copy-paste flows.
29
+ - [`../reference/hooks-index.md`](../reference/hooks-index.md) — full hook table at one glance.