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

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 +190 -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 +21 -11
  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
@@ -0,0 +1,157 @@
1
+ # Recipe: Backend Queries
2
+
3
+ Read-only data hooks. No wallet connection required.
4
+
5
+ **Depends on:** [setup.md](setup.md)
6
+
7
+ ## Hooks
8
+
9
+ ### Intents
10
+
11
+ | Hook | Purpose |
12
+ |------|---------|
13
+ | `useBackendIntentByTxHash` | Intent by hub chain tx hash (polls 1s) |
14
+ | `useBackendIntentByHash` | Intent by intent hash |
15
+ | `useBackendUserIntents` | All intents for a user with date filtering |
16
+
17
+ ### Orderbook
18
+
19
+ | Hook | Purpose |
20
+ |------|---------|
21
+ | `useBackendOrderbook` | Solver orderbook with pagination (cached 30s, no auto-refetch) |
22
+
23
+ ### Money Market
24
+
25
+ | Hook | Purpose |
26
+ |------|---------|
27
+ | `useBackendMoneyMarketPosition` | User's money market position |
28
+ | `useBackendMoneyMarketAsset` | Specific asset details |
29
+ | `useBackendAllMoneyMarketAssets` | All money market assets |
30
+ | `useBackendMoneyMarketAssetSuppliers` | Suppliers for an asset |
31
+ | `useBackendMoneyMarketAssetBorrowers` | Borrowers for an asset |
32
+ | `useBackendAllMoneyMarketBorrowers` | All borrowers |
33
+
34
+ ### Swap Submission
35
+
36
+ | Hook | Purpose |
37
+ |------|---------|
38
+ | `useBackendSubmitSwapTx` | Submit swap tx to backend |
39
+ | `useBackendSubmitSwapTxStatus` | Check submitted swap status |
40
+
41
+ ## Track Intent
42
+
43
+ ```tsx
44
+ import { useBackendIntentByTxHash } from '@sodax/dapp-kit';
45
+
46
+ function IntentTracker({ txHash }: { txHash: string }) {
47
+ const { data: intent, isLoading } = useBackendIntentByTxHash({
48
+ params: { txHash },
49
+ });
50
+
51
+ if (isLoading) return <div>Loading...</div>;
52
+ return <pre>{JSON.stringify(intent, null, 2)}</pre>;
53
+ }
54
+ ```
55
+
56
+ ## User Intent History
57
+
58
+ ```tsx
59
+ import { useBackendUserIntents } from '@sodax/dapp-kit';
60
+
61
+ function IntentHistory({ userAddress }: { userAddress: `0x${string}` }) {
62
+ const { data: intents } = useBackendUserIntents({
63
+ params: {
64
+ userAddress,
65
+ startDate: Date.now() - 7 * 24 * 60 * 60 * 1000,
66
+ endDate: Date.now(),
67
+ },
68
+ });
69
+
70
+ return (
71
+ <div>
72
+ {intents?.items.map((intent, i) => (
73
+ <div key={i}>
74
+ <p>{intent.intentHash} -- {intent.open ? 'open' : 'closed'}</p>
75
+ </div>
76
+ ))}
77
+ </div>
78
+ );
79
+ }
80
+ ```
81
+
82
+ ## Orderbook
83
+
84
+ ```tsx
85
+ import { useBackendOrderbook } from '@sodax/dapp-kit';
86
+
87
+ function Orderbook() {
88
+ // `pagination` is nested under `params` per the canonical query-hook shape.
89
+ const { data: orderbook } = useBackendOrderbook({
90
+ params: { pagination: { offset: '0', limit: '20' } },
91
+ });
92
+ return <pre>{JSON.stringify(orderbook, null, 2)}</pre>;
93
+ }
94
+ ```
95
+
96
+ ## Money Market Dashboard
97
+
98
+ ```tsx
99
+ // @ai-snippets-skip — uses example field name supplyAPY not in MoneyMarketAsset type
100
+ import { useBackendMoneyMarketPosition, useBackendAllMoneyMarketAssets } from '@sodax/dapp-kit';
101
+
102
+ function MMDashboard({ userAddress }: { userAddress: string }) {
103
+ const { data: position } = useBackendMoneyMarketPosition({ params: { userAddress } });
104
+ const { data: assets } = useBackendAllMoneyMarketAssets({});
105
+
106
+ return (
107
+ <div>
108
+ {position && <pre>{JSON.stringify(position, null, 2)}</pre>}
109
+ {assets?.map((a, i) => <p key={i}>{a.symbol}: Supply {a.supplyAPY}%</p>)}
110
+ </div>
111
+ );
112
+ }
113
+ ```
114
+
115
+ ## Custom Query Options
116
+
117
+ All read hooks accept `queryOptions` to override defaults:
118
+
119
+ ```tsx
120
+ // @ai-snippets-skip
121
+ const { data } = useBackendIntentByTxHash({
122
+ params: { txHash },
123
+ queryOptions: { staleTime: 5000, refetchInterval: 2000, retry: 3 },
124
+ });
125
+ ```
126
+
127
+ ## Submit a Swap Tx
128
+
129
+ `useBackendSubmitSwapTx` is a mutation hook. Per-call config (e.g. backend base URL) flows through `mutate(vars)`; TanStack Query knobs flow through the optional `mutationOptions` slot:
130
+
131
+ ```tsx
132
+ import { useBackendSubmitSwapTx } from '@sodax/dapp-kit';
133
+
134
+ function SubmitButton({ swapPayload, baseURL }) {
135
+ const { mutateAsync: submitSwapTx, isPending } = useBackendSubmitSwapTx({
136
+ mutationOptions: { retry: 5 }, // overrides default retry: 3
137
+ });
138
+
139
+ const handleSubmit = async () => {
140
+ const response = await submitSwapTx({
141
+ request: swapPayload,
142
+ apiConfig: { baseURL }, // per-call backend override
143
+ });
144
+ console.log('Submitted:', response);
145
+ };
146
+
147
+ return <button onClick={handleSubmit} disabled={isPending}>Submit</button>;
148
+ }
149
+ ```
150
+
151
+ ## Default Polling
152
+
153
+ | Hook | Interval |
154
+ |------|---------|
155
+ | `useBackendIntentByTxHash` | 1s |
156
+ | `useBackendOrderbook` | none (`staleTime: 30s`, no auto-refetch) |
157
+ | Others | No auto-refresh |
@@ -0,0 +1,193 @@
1
+ # Recipe: Bitcoin (Radfi)
2
+
3
+ Bitcoin trading via the Radfi protocol. Authenticate, fund a trading wallet, trade, withdraw, and manage UTXOs.
4
+
5
+ **Depends on:** [setup.md](setup.md), [wallet-connectivity.md](wallet-connectivity.md)
6
+
7
+ ## Hooks
8
+
9
+ ### Session
10
+
11
+ | Hook | Type | Purpose |
12
+ |------|------|---------|
13
+ | `useRadfiAuth` | Mutation | Authenticate with Radfi via BIP322 signing |
14
+ | `useRadfiSession` | Utility | Manage full session lifecycle (login, refresh, auto-refresh) |
15
+ | `useTradingWallet` | Utility | Get trading wallet address from persisted session (synchronous) |
16
+
17
+ ### Balance
18
+
19
+ | Hook | Type | Purpose |
20
+ |------|------|---------|
21
+ | `useBitcoinBalance` | Query | BTC balance for any address (sums UTXOs from mempool.space) |
22
+ | `useTradingWalletBalance` | Query | Trading wallet balance from Radfi API (confirmed + pending) |
23
+
24
+ ### Operations
25
+
26
+ | Hook | Type | Purpose |
27
+ |------|------|---------|
28
+ | `useFundTradingWallet` | Mutation | Send BTC from personal wallet to trading wallet |
29
+ | `useRadfiWithdraw` | Mutation | Withdraw BTC from trading wallet to personal wallet |
30
+ | `useExpiredUtxos` | Query | Fetch UTXOs that need renewal (polls every 60s) |
31
+ | `useRenewUtxos` | Mutation | Renew expired UTXOs in trading wallet |
32
+
33
+ ## Session Flow
34
+
35
+ Radfi requires authentication before any trading operation. The typical flow:
36
+
37
+ ```tsx
38
+ import { useRadfiSession } from '@sodax/dapp-kit';
39
+ import { useWalletProvider } from '@sodax/wallet-sdk-react';
40
+ import { ChainKeys } from '@sodax/sdk';
41
+
42
+ function BitcoinAuth() {
43
+ const walletProvider = useWalletProvider({ xChainId: ChainKeys.BITCOIN_MAINNET });
44
+ const { isAuthed, tradingAddress, login, isLoginPending } = useRadfiSession(walletProvider);
45
+
46
+ if (isAuthed) {
47
+ return <p>Authenticated. Trading wallet: {tradingAddress}</p>;
48
+ }
49
+
50
+ return (
51
+ <button onClick={login} disabled={isLoginPending}>
52
+ {isLoginPending ? 'Signing...' : 'Login to Radfi'}
53
+ </button>
54
+ );
55
+ }
56
+ ```
57
+
58
+ `useRadfiSession` handles the full lifecycle:
59
+ - On mount: refreshes token to validate existing session
60
+ - Every 5 min: auto-refreshes access token
61
+ - If refresh fails: clears session, sets `isAuthed = false`
62
+ - Session persisted in localStorage (keyed by wallet address)
63
+
64
+ ## Check Balances
65
+
66
+ ```tsx
67
+ import { useBitcoinBalance, useTradingWalletBalance, useTradingWallet } from '@sodax/dapp-kit';
68
+ import { useWalletProvider } from '@sodax/wallet-sdk-react';
69
+ import { ChainKeys } from '@sodax/sdk';
70
+
71
+ function BitcoinBalances({ walletAddress }: { walletAddress: string }) {
72
+ const walletProvider = useWalletProvider({ xChainId: ChainKeys.BITCOIN_MAINNET });
73
+ const { tradingAddress } = useTradingWallet(walletAddress);
74
+
75
+ // Personal wallet balance (from mempool.space)
76
+ const { data: personalBalance } = useBitcoinBalance({
77
+ params: { address: walletAddress },
78
+ });
79
+
80
+ // Trading wallet balance (from Radfi API). `RadfiWalletBalance` fields:
81
+ // `btcSatoshi`, `pendingSatoshi`, `externalPendingSatoshi`, `totalUtxos`.
82
+ const { data: tradingBalance } = useTradingWalletBalance({
83
+ params: { walletProvider, tradingAddress },
84
+ });
85
+
86
+ return (
87
+ <div>
88
+ <p>Personal: {personalBalance?.toString() ?? '...'} sats</p>
89
+ <p>Trading: {tradingBalance?.btcSatoshi?.toString() ?? '...'} sats</p>
90
+ </div>
91
+ );
92
+ }
93
+ ```
94
+
95
+ ## Fund Trading Wallet
96
+
97
+ ```tsx
98
+ import { useFundTradingWallet } from '@sodax/dapp-kit';
99
+ import { useWalletProvider } from '@sodax/wallet-sdk-react';
100
+ import { ChainKeys } from '@sodax/sdk';
101
+
102
+ function FundButton() {
103
+ const walletProvider = useWalletProvider({ xChainId: ChainKeys.BITCOIN_MAINNET });
104
+ const { mutateAsyncSafe: fundWallet, isPending } = useFundTradingWallet();
105
+
106
+ const handleFund = async () => {
107
+ if (!walletProvider) return;
108
+ const result = await fundWallet({ amount: 100_000n, walletProvider }); // 100,000 satoshis
109
+ if (result.ok) console.log('Funded:', result.value);
110
+ else console.error('Fund failed:', result.error);
111
+ };
112
+
113
+ return (
114
+ <button onClick={handleFund} disabled={isPending || !walletProvider}>
115
+ {isPending ? 'Funding...' : 'Fund Trading Wallet'}
116
+ </button>
117
+ );
118
+ }
119
+ ```
120
+
121
+ ## Withdraw from Trading Wallet
122
+
123
+ ```tsx
124
+ import { useRadfiWithdraw } from '@sodax/dapp-kit';
125
+ import { useWalletProvider } from '@sodax/wallet-sdk-react';
126
+ import { ChainKeys } from '@sodax/sdk';
127
+
128
+ function WithdrawButton({ withdrawTo }: { withdrawTo: string }) {
129
+ const walletProvider = useWalletProvider({ xChainId: ChainKeys.BITCOIN_MAINNET });
130
+ const { mutateAsync: withdraw, isPending } = useRadfiWithdraw();
131
+
132
+ const handleWithdraw = async () => {
133
+ if (!walletProvider) return;
134
+ const result = await withdraw({
135
+ amount: '10000',
136
+ tokenId: '0:0',
137
+ withdrawTo, // user's personal BTC address
138
+ walletProvider,
139
+ });
140
+ console.log('Withdrawn:', result.txId, 'Fee:', result.fee);
141
+ };
142
+
143
+ return (
144
+ <button onClick={handleWithdraw} disabled={isPending || !walletProvider}>
145
+ {isPending ? 'Withdrawing...' : 'Withdraw'}
146
+ </button>
147
+ );
148
+ }
149
+ ```
150
+
151
+ ## Manage Expired UTXOs
152
+
153
+ UTXOs in the trading wallet can expire. Check and renew them:
154
+
155
+ ```tsx
156
+ import { useExpiredUtxos, useRenewUtxos } from '@sodax/dapp-kit';
157
+ import { useWalletProvider } from '@sodax/wallet-sdk-react';
158
+ import { ChainKeys } from '@sodax/sdk';
159
+
160
+ function UtxoManager({ tradingAddress }: { tradingAddress: string }) {
161
+ const walletProvider = useWalletProvider({ xChainId: ChainKeys.BITCOIN_MAINNET });
162
+ const { data: expiredUtxos } = useExpiredUtxos({
163
+ params: { walletProvider, tradingAddress },
164
+ });
165
+ const { mutateAsync: renewUtxos, isPending } = useRenewUtxos();
166
+
167
+ if (!expiredUtxos?.length) return <p>No expired UTXOs</p>;
168
+
169
+ const handleRenew = async () => {
170
+ if (!walletProvider) return;
171
+ const txIdVouts = expiredUtxos.map((u) => u.txidVout);
172
+ const txId = await renewUtxos({ txIdVouts, walletProvider });
173
+ console.log('Renewed:', txId);
174
+ };
175
+
176
+ return (
177
+ <div>
178
+ <p>{expiredUtxos.length} expired UTXOs</p>
179
+ <button onClick={handleRenew} disabled={isPending || !walletProvider}>
180
+ {isPending ? 'Renewing...' : 'Renew UTXOs'}
181
+ </button>
182
+ </div>
183
+ );
184
+ }
185
+ ```
186
+
187
+ ## Notes
188
+
189
+ - **Authentication required** before any trading operation. `useRadfiSession` manages this automatically.
190
+ - **Trading wallet** is created during first authentication — not a separate step.
191
+ - **`useTradingWallet(walletAddress)`** is a synchronous utility (no network call) — it reads the persisted Radfi session from localStorage.
192
+ - **PSBT signing flow**: withdraw and renew operations build an unsigned PSBT server-side, user signs it locally, then submits back for co-signing and broadcast.
193
+ - **Session tokens** are stored in localStorage keyed by wallet address. They're for API rate-limiting, not for accessing user assets.
@@ -0,0 +1,174 @@
1
+ # Recipe: Bridge
2
+
3
+ Cross-chain token transfers via the hub-and-spoke vault architecture.
4
+
5
+ **Depends on:** [setup.md](setup.md), [wallet-connectivity.md](wallet-connectivity.md)
6
+
7
+ ## Hooks
8
+
9
+ | Hook | Type | Purpose |
10
+ |------|------|---------|
11
+ | `useBridge` | Mutation | Execute a cross-chain bridge transfer |
12
+ | `useBridgeAllowance` | Query | Check if token approval is needed |
13
+ | `useBridgeApprove` | Mutation | Approve tokens for bridge |
14
+ | `useGetBridgeableAmount` | Query | Available bridgeable amount between two tokens |
15
+ | `useGetBridgeableTokens` | Query | Tokens bridgeable to a destination chain |
16
+
17
+ ## Check Bridgeable Tokens
18
+
19
+ ```tsx
20
+ import { useGetBridgeableTokens } from '@sodax/dapp-kit';
21
+ import { ChainKeys } from '@sodax/sdk';
22
+
23
+ function BridgeableTokensList() {
24
+ // `data` is `XToken[] | undefined` — the hook throws on SDK `!ok` so we get the unwrapped value.
25
+ const { data: tokens } = useGetBridgeableTokens({
26
+ params: { from: ChainKeys.BASE_MAINNET, to: ChainKeys.POLYGON_MAINNET, token: '0x0000000000000000000000000000000000000000' },
27
+ });
28
+
29
+ if (tokens) {
30
+ return <ul>{tokens.map((t) => <li key={t.address}>{t.symbol}</li>)}</ul>;
31
+ }
32
+ return null;
33
+ }
34
+ ```
35
+
36
+ ## Check Allowance + Approve
37
+
38
+ ```tsx
39
+ import { useBridgeAllowance, useBridgeApprove } from '@sodax/dapp-kit';
40
+ import { useWalletProvider } from '@sodax/wallet-sdk-react';
41
+ import { ChainKeys } from '@sodax/sdk';
42
+ import type { CreateBridgeIntentParams } from '@sodax/sdk';
43
+
44
+ function BridgeApproval({ params }: { params: CreateBridgeIntentParams }) {
45
+ const walletProvider = useWalletProvider({ xChainId: ChainKeys.BASE_MAINNET });
46
+ // useBridgeAllowance wraps payload + walletProvider under params (not at top level).
47
+ const { data: isApproved } = useBridgeAllowance({
48
+ params: { payload: params, walletProvider },
49
+ });
50
+ const { mutateAsync: approve, isPending } = useBridgeApprove();
51
+
52
+ if (isApproved) return null;
53
+ return (
54
+ <button onClick={() => walletProvider && approve({ params, walletProvider })} disabled={isPending}>
55
+ {isPending ? 'Approving...' : 'Approve for Bridge'}
56
+ </button>
57
+ );
58
+ }
59
+ ```
60
+
61
+ ## Execute Bridge
62
+
63
+ ```tsx
64
+ import { useBridge } from '@sodax/dapp-kit';
65
+ import { useWalletProvider } from '@sodax/wallet-sdk-react';
66
+ import { ChainKeys } from '@sodax/sdk';
67
+
68
+ function BridgeButton({ srcAddress }: { srcAddress: `0x${string}` }) {
69
+ const walletProvider = useWalletProvider({ xChainId: ChainKeys.BASE_MAINNET });
70
+ const { mutateAsync: bridge, isPending } = useBridge();
71
+
72
+ const handleBridge = async () => {
73
+ if (!walletProvider) return;
74
+ try {
75
+ const { srcChainTxHash, dstChainTxHash } = await bridge({
76
+ params: {
77
+ srcChainKey: ChainKeys.BASE_MAINNET,
78
+ srcAddress,
79
+ srcToken: '0x0000000000000000000000000000000000000000',
80
+ amount: 1000000000000000000n,
81
+ dstChainKey: ChainKeys.POLYGON_MAINNET,
82
+ dstToken: '0x0000000000000000000000000000000000000000',
83
+ recipient: '0x0000000000000000000000000000000000000000',
84
+ },
85
+ walletProvider,
86
+ });
87
+ console.log('Bridge successful:', { srcChainTxHash, dstChainTxHash });
88
+ } catch (e) {
89
+ console.error(e);
90
+ }
91
+ };
92
+
93
+ return (
94
+ <button onClick={handleBridge} disabled={isPending}>
95
+ {isPending ? 'Bridging...' : 'Bridge'}
96
+ </button>
97
+ );
98
+ }
99
+ ```
100
+
101
+ ## Full Example
102
+
103
+ ```tsx
104
+ import { useState } from 'react';
105
+ import { useBridge, useBridgeAllowance, useBridgeApprove, useGetBridgeableAmount } from '@sodax/dapp-kit';
106
+ import { useWalletProvider } from '@sodax/wallet-sdk-react';
107
+ import { ChainKeys, type XToken } from '@sodax/sdk';
108
+ import type { CreateBridgeIntentParams } from '@sodax/sdk';
109
+ import { parseUnits, formatUnits } from 'viem';
110
+
111
+ export function BridgePage({ srcXToken, dstXToken, srcAddress }: { srcXToken: XToken; dstXToken: XToken; srcAddress: `0x${string}` }) {
112
+ const [amount, setAmount] = useState('');
113
+ const walletProvider = useWalletProvider({ xChainId: ChainKeys.BASE_MAINNET });
114
+ const parsedAmount = amount ? parseUnits(amount, 6) : 0n;
115
+
116
+ const bridgeParams: CreateBridgeIntentParams<typeof ChainKeys.BASE_MAINNET> | undefined = parsedAmount > 0n
117
+ ? {
118
+ srcChainKey: ChainKeys.BASE_MAINNET,
119
+ srcAddress,
120
+ srcToken: srcXToken.address,
121
+ amount: parsedAmount,
122
+ dstChainKey: ChainKeys.POLYGON_MAINNET,
123
+ dstToken: dstXToken.address,
124
+ recipient: '0x0000000000000000000000000000000000000000',
125
+ }
126
+ : undefined;
127
+
128
+ // useGetBridgeableAmount in v2 takes a pair of XToken objects (each carries chainKey).
129
+ // `data` is `BridgeLimit | undefined` (already unwrapped; hook throws on SDK !ok).
130
+ const { data: bridgeableAmount } = useGetBridgeableAmount({
131
+ params: { from: srcXToken, to: dstXToken },
132
+ });
133
+ const { data: isApproved } = useBridgeAllowance({
134
+ params: bridgeParams ? { payload: bridgeParams, walletProvider } : undefined,
135
+ });
136
+ const { mutateAsyncSafe: approve, isPending: isApproving } = useBridgeApprove();
137
+ const { mutateAsyncSafe: bridge, isPending: isBridging } = useBridge();
138
+
139
+ const handleBridge = async () => {
140
+ if (!bridgeParams || !walletProvider) return;
141
+ if (!isApproved) {
142
+ const r = await approve({ params: bridgeParams, walletProvider });
143
+ if (!r.ok) { alert(r.error instanceof Error ? r.error.message : 'Approve failed'); return; }
144
+ }
145
+ const r = await bridge({ params: bridgeParams, walletProvider });
146
+ if (r.ok) alert(`Bridge complete! src=${r.value.srcChainTxHash}`);
147
+ else alert(r.error instanceof Error ? r.error.message : 'Bridge failed');
148
+ };
149
+
150
+ return (
151
+ <div>
152
+ <input placeholder="Amount" value={amount} onChange={(e) => setAmount(e.target.value)} />
153
+ {bridgeableAmount && <p>Max: {formatUnits(bridgeableAmount.amount, bridgeableAmount.decimals)}</p>}
154
+ <button onClick={handleBridge} disabled={isBridging || isApproving || !bridgeParams || !walletProvider}>
155
+ {isApproving ? 'Approving...' : isBridging ? 'Bridging...' : 'Bridge'}
156
+ </button>
157
+ </div>
158
+ );
159
+ }
160
+ ```
161
+
162
+ ## Types
163
+
164
+ ```typescript
165
+ type CreateBridgeIntentParams<K extends SpokeChainKey = SpokeChainKey> = {
166
+ srcChainKey: K;
167
+ srcAddress: string;
168
+ srcToken: string;
169
+ amount: bigint;
170
+ dstChainKey: SpokeChainKey;
171
+ dstToken: string;
172
+ recipient: string; // non-encoded recipient address on the destination chain
173
+ };
174
+ ```