@sodax/dapp-kit 2.0.0-rc.2 → 2.0.0-rc.4

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 (63) hide show
  1. package/README.md +9 -63
  2. package/dist/index.d.ts +11 -4
  3. package/dist/index.mjs +3 -6
  4. package/package.json +31 -19
  5. package/src/providers/SodaxProvider.tsx +15 -11
  6. package/ai-exported/AGENTS.md +0 -134
  7. package/ai-exported/integration/README.md +0 -49
  8. package/ai-exported/integration/ai-rules.md +0 -79
  9. package/ai-exported/integration/architecture.md +0 -274
  10. package/ai-exported/integration/features/README.md +0 -29
  11. package/ai-exported/integration/features/auxiliary-services.md +0 -169
  12. package/ai-exported/integration/features/bitcoin.md +0 -87
  13. package/ai-exported/integration/features/bridge.md +0 -91
  14. package/ai-exported/integration/features/dex.md +0 -152
  15. package/ai-exported/integration/features/migration.md +0 -118
  16. package/ai-exported/integration/features/money-market.md +0 -116
  17. package/ai-exported/integration/features/staking.md +0 -123
  18. package/ai-exported/integration/features/swap.md +0 -101
  19. package/ai-exported/integration/quickstart.md +0 -187
  20. package/ai-exported/integration/recipes/README.md +0 -136
  21. package/ai-exported/integration/recipes/backend-queries.md +0 -157
  22. package/ai-exported/integration/recipes/bitcoin.md +0 -193
  23. package/ai-exported/integration/recipes/bridge.md +0 -174
  24. package/ai-exported/integration/recipes/dex.md +0 -204
  25. package/ai-exported/integration/recipes/invalidations.md +0 -115
  26. package/ai-exported/integration/recipes/migration.md +0 -212
  27. package/ai-exported/integration/recipes/money-market.md +0 -206
  28. package/ai-exported/integration/recipes/mutation-error-handling.md +0 -118
  29. package/ai-exported/integration/recipes/observability.md +0 -93
  30. package/ai-exported/integration/recipes/setup.md +0 -144
  31. package/ai-exported/integration/recipes/staking.md +0 -202
  32. package/ai-exported/integration/recipes/swap.md +0 -272
  33. package/ai-exported/integration/recipes/wallet-connectivity.md +0 -101
  34. package/ai-exported/integration/reference/README.md +0 -12
  35. package/ai-exported/integration/reference/glossary.md +0 -188
  36. package/ai-exported/integration/reference/hooks-index.md +0 -190
  37. package/ai-exported/integration/reference/public-api.md +0 -110
  38. package/ai-exported/integration/reference/querykey-conventions.md +0 -179
  39. package/ai-exported/migration/README.md +0 -60
  40. package/ai-exported/migration/ai-rules.md +0 -81
  41. package/ai-exported/migration/breaking-changes/hook-signatures.md +0 -233
  42. package/ai-exported/migration/breaking-changes/querykey-conventions.md +0 -108
  43. package/ai-exported/migration/breaking-changes/result-handling.md +0 -211
  44. package/ai-exported/migration/breaking-changes/sdk-leakage.md +0 -165
  45. package/ai-exported/migration/checklist.md +0 -89
  46. package/ai-exported/migration/features/README.md +0 -34
  47. package/ai-exported/migration/features/auxiliary-services.md +0 -114
  48. package/ai-exported/migration/features/bitcoin.md +0 -88
  49. package/ai-exported/migration/features/bridge.md +0 -123
  50. package/ai-exported/migration/features/dex.md +0 -101
  51. package/ai-exported/migration/features/migration.md +0 -120
  52. package/ai-exported/migration/features/money-market.md +0 -97
  53. package/ai-exported/migration/features/staking.md +0 -109
  54. package/ai-exported/migration/features/swap.md +0 -118
  55. package/ai-exported/migration/recipes.md +0 -188
  56. package/ai-exported/migration/reference/README.md +0 -15
  57. package/ai-exported/migration/reference/deleted-hooks.md +0 -110
  58. package/ai-exported/migration/reference/error-shape-crosswalk.md +0 -144
  59. package/ai-exported/migration/reference/renamed-hooks.md +0 -66
  60. package/dist/index.cjs +0 -2642
  61. package/dist/index.cjs.map +0 -1
  62. package/dist/index.d.cts +0 -1550
  63. package/dist/index.mjs.map +0 -1
@@ -1,274 +0,0 @@
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
- Hooks 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
252
- ├── bridge/ # useBridge, useBridgeAllowance, useBridgeApprove, bridgeable amounts/tokens
253
- ├── staking/ # useStake, useUnstake, useInstantUnstake, useClaim, staking info hooks
254
- ├── dex/ # usePools, useDexDeposit, useDexWithdraw, liquidity hooks
255
- ├── bitcoin/ # useRadfiSession, fund/withdraw, UTXO management
256
- ├── backend/ # Intent tracking, swap submission, orderbook, money market position queries
257
- ├── partner/ # Partner fee claim, auto-swap preferences, token approval
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>`).
@@ -1,29 +0,0 @@
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 | What's covered |
6
- |---|---|
7
- | [`swap.md`](swap.md) | Cross-chain swaps via the intent solver: `useQuote`, `useSwap`, allowance/approve, status polling, limit orders. |
8
- | [`money-market.md`](money-market.md) | Lending/borrowing on the cross-chain MM: `useSupply`, `useBorrow`, `useWithdraw`, `useRepay`, allowance/approve, reserves data hooks. |
9
- | [`staking.md`](staking.md) | SODA → xSODA staking: `useStake`, `useUnstake`, `useInstantUnstake`, `useClaim`, `useCancelUnstake`, allowance/approve, info/ratio reads. |
10
- | [`bridge.md`](bridge.md) | Cross-chain token bridging: `useBridge`, allowance/approve, bridgeable amount/tokens. |
11
- | [`dex.md`](dex.md) | Concentrated liquidity DEX: assets in/out, liquidity supply/decrease, claim rewards, position info, pool reads, param builders. |
12
- | [`migration.md`](migration.md) | Token migration: `useMigrateIcxToSoda`, `useRevertMigrateSodaToIcx`, `useMigratebnUSD`, `useMigrateBaln`, allowance/approve. |
13
- | [`bitcoin.md`](bitcoin.md) | Radfi (dapp-kit-unique): session, trading wallet, fund/withdraw, UTXOs. |
14
- | [`auxiliary-services.md`](auxiliary-services.md) | 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.
@@ -1,169 +0,0 @@
1
- # Auxiliary services — `@sodax/dapp-kit`
2
-
3
- Smaller surfaces grouped together: partner fee claiming, recovery, backend queries (read-only data hooks), and shared utilities.
4
-
5
- Pair: [`../../migration/features/auxiliary-services.md`](../../migration/features/auxiliary-services.md).
6
-
7
- ## Partner
8
-
9
- Partner fee claiming and auto-swap preferences.
10
-
11
- ```ts
12
- // @ai-snippets-skip
13
- useFetchAssetsBalances({ params, queryOptions }); // Partner asset balances
14
- useGetAutoSwapPreferences({ params, queryOptions });
15
- useIsTokenApproved({ params: { payload: FeeTokenApproveParams }, queryOptions });
16
- useApproveToken({ mutationOptions });
17
- useSetSwapPreference({ mutationOptions });
18
- useFeeClaimSwap({ mutationOptions }); // Claim partner fees via swap
19
- ```
20
-
21
- `useFeeClaimSwap` returns `SafeUseMutationResult<IntentAutoSwapResult, Error, UseFeeClaimSwapVars>` — the success value is `IntentAutoSwapResult` (NOT `SwapResponse`). TVars are `Omit<PartnerFeeClaimSwapAction<HubChainKey, false>, 'raw'>`.
22
-
23
- ## Recovery
24
-
25
- Withdraw stuck hub-wallet assets back to a spoke chain.
26
-
27
- ```ts
28
- // @ai-snippets-skip
29
- useHubAssetBalances({ params, queryOptions }); // List assets stuck on hub
30
- useWithdrawHubAsset({ mutationOptions });
31
- ```
32
-
33
- ## Backend queries (read-only data)
34
-
35
- No wallet connection required.
36
-
37
- ### Intent tracking
38
-
39
- ```ts
40
- // @ai-snippets-skip
41
- useBackendIntentByTxHash({ params, queryOptions }); // Polls 1s once a txHash is supplied
42
- useBackendIntentByHash({ params, queryOptions });
43
- useBackendUserIntents({ params, queryOptions }); // Date-filtered user history; data is { items: IntentResponse[], total, offset, limit }
44
- ```
45
-
46
- ### Orderbook
47
-
48
- ```ts
49
- // @ai-snippets-skip
50
- // `pagination` MUST be nested under `params` — top-level pagination is invalid.
51
- useBackendOrderbook({ params: { pagination: { offset, limit } }, queryOptions }); // staleTime 30s; no auto-refresh
52
- ```
53
-
54
- ### Money market data
55
-
56
- ```ts
57
- // @ai-snippets-skip
58
- useBackendMoneyMarketPosition({ params, queryOptions });
59
- useBackendMoneyMarketAsset({ params, queryOptions });
60
- useBackendAllMoneyMarketAssets({ queryOptions });
61
- useBackendMoneyMarketAssetSuppliers({ params, queryOptions });
62
- useBackendMoneyMarketAssetBorrowers({ params, queryOptions });
63
- // Pagination required — without it the query is disabled.
64
- useBackendAllMoneyMarketBorrowers({ params: { pagination: { offset, limit } }, queryOptions });
65
- ```
66
-
67
- ### Swap submission
68
-
69
- ```ts
70
- // @ai-snippets-skip
71
- useBackendSubmitSwapTx({ mutationOptions }); // Mutation
72
- useBackendSubmitSwapTxStatus({ params, queryOptions }); // Query — check submitted status
73
- ```
74
-
75
- `useBackendSubmitSwapTx` is a mutation hook — per-call config (e.g. backend base URL) flows through `mutate(vars)`:
76
-
77
- ```ts
78
- // @ai-snippets-skip
79
- const { mutateAsync: submitSwapTx } = useBackendSubmitSwapTx();
80
- await submitSwapTx({ request: swapPayload, apiConfig: { baseURL: 'https://...' } });
81
- ```
82
-
83
- ## Shared utilities
84
-
85
- Cross-cutting hooks used by other features.
86
-
87
- ```ts
88
- // @ai-snippets-skip
89
- useSodaxContext(); // Access the Sodax SDK instance
90
- useHubProvider(); // Hub chain (Sonic) provider
91
- useXBalances({ params, queryOptions }); // Cross-chain token balances
92
- useDeriveUserWalletAddress({ params, queryOptions }); // Hub wallet address (CREATE3)
93
- useGetUserHubWalletAddress({ params, queryOptions }); // Hub wallet via wallet router
94
- useEstimateGas({ mutationOptions }); // Gas estimation for raw tx
95
- useStellarTrustlineCheck({ params, queryOptions });
96
- useRequestTrustline({ mutationOptions });
97
- ```
98
-
99
- ### `useXBalances` shape
100
-
101
- ```ts
102
- // @ai-snippets-skip
103
- type UseXBalancesParams = ReadHookParams<Record<string, bigint>, {
104
- xService: IXServiceBase | undefined; // From @sodax/wallet-sdk-react's useXService
105
- xChainId: SpokeChainKey | undefined;
106
- xTokens: readonly XToken[]; // Tokens to fetch balances for
107
- address: string | undefined;
108
- }>;
109
- ```
110
-
111
- Note: the **request-side** field is `xChainId` (kept for the cross-chain abstraction it overlays). This is distinct from the v2-renamed token-side `chainKey` — don't conflate them.
112
-
113
- Consumer must supply `xService` from `@sodax/wallet-sdk-react`:
114
-
115
- ```tsx
116
- // @ai-snippets-skip
117
- import { useXService, getXChainType } from '@sodax/wallet-sdk-react';
118
- const xService = useXService({ xChainType: getXChainType(xChainId) });
119
- const { data: balances } = useXBalances({ params: { xService, xChainId, xTokens, address } });
120
- ```
121
-
122
- ### Stellar trustlines
123
-
124
- Stellar accounts that have never held an asset have no trustline — receiving will fail. Pre-flight with `useStellarTrustlineCheck`; fix with `useRequestTrustline`:
125
-
126
- ```ts
127
- // @ai-snippets-skip — illustrative only; real types pulled into agents below.
128
- // useStellarTrustlineCheck takes { token, amount, chainId, walletProvider } under params.
129
- // `chainId` here is a `SpokeChainKey` (typed loosely so consumers can pass any chain key —
130
- // the hook returns `true` for non-Stellar chains, making it safe to gate on conditionally).
131
- const { data: hasTrustline } = useStellarTrustlineCheck({
132
- params: { token, amount, chainId: ChainKeys.STELLAR_MAINNET, walletProvider },
133
- });
134
-
135
- // useRequestTrustline is NOT a canonical mutation hook — it takes a single positional
136
- // `token` arg and returns { requestTrustline, isLoading, isRequested, error, data }.
137
- // The `requestTrustline` callback signature is:
138
- // ({ token, amount, srcChainKey, walletProvider }) => Promise<string>
139
- // NOTE: fields are `token` / `amount` / `srcChainKey` / `walletProvider` — NOT
140
- // `account` / `asset`. Pass a StellarChainKey for srcChainKey.
141
- const { requestTrustline, isLoading } = useRequestTrustline(token);
142
- if (hasTrustline === false) {
143
- await requestTrustline({ token, amount, srcChainKey: ChainKeys.STELLAR_MAINNET, walletProvider });
144
- }
145
- ```
146
-
147
- ## Default polling intervals
148
-
149
- | Hook | Polling | Notes |
150
- |---|---|---|
151
- | `useBackendIntentByTxHash` | 1s | once a `txHash` is supplied (refetch is unconditional, not "while pending") |
152
- | `useBackendSubmitSwapTxStatus` | varies | poll stops on `executed` / `failed` |
153
- | `useBackendOrderbook` | none | `staleTime: 30s` — fresh-window, no background refetch |
154
- | `useExpiredUtxos` (bitcoin) | 60s | refetchInterval |
155
- | `useQuote` (swap) | 3s | refetchInterval |
156
- | `useStatus` (swap) | 3s | refetchInterval |
157
- | `useSwapAllowance` (swap) | 2s | refetchInterval |
158
- | `useMMAllowance` (mm) | 5s | refetchInterval; `enabled: false` for borrow/withdraw actions |
159
- | Reserves data (mm) | 5s | `useReservesData` / `useReservesHumanized` / user position hooks |
160
- | Most others | None | |
161
-
162
- All overridable via `queryOptions.refetchInterval`.
163
-
164
- ## Cross-references
165
-
166
- - [`../recipes/backend-queries.md`](../recipes/backend-queries.md) — worked examples for intent tracking, orderbook, MM data.
167
- - [`../recipes/wallet-connectivity.md`](../recipes/wallet-connectivity.md) — `useXBalances` worked example.
168
- - [`../../migration/features/auxiliary-services.md`](../../migration/features/auxiliary-services.md) — v1 → v2 porting.
169
- - [`../../../../sdk/ai-exported/integration/features/auxiliary-services.md`](../../../../sdk/ai-exported/integration/features/auxiliary-services.md) — underlying SDK auxiliary surfaces (partner, recovery, backendApi).
@@ -1,87 +0,0 @@
1
- # Bitcoin (Radfi) — `@sodax/dapp-kit`
2
-
3
- Bitcoin trading via the Radfi protocol — authenticate, fund a trading wallet, withdraw, manage UTXOs. **Dapp-kit-unique surface** (no SDK equivalent — these flows are React-shaped).
4
-
5
- Pair: [`../../migration/features/bitcoin.md`](../../migration/features/bitcoin.md).
6
-
7
- ## Hook surface
8
-
9
- ```ts
10
- // @ai-snippets-skip
11
- // Session lifecycle
12
- useRadfiAuth({ mutationOptions }); // Authenticate via BIP322 signing
13
- useRadfiSession(walletProvider); // Manage full lifecycle (login, refresh, auto-refresh)
14
- useTradingWallet(walletAddress); // Synchronous: read persisted session
15
-
16
- // Balances
17
- useBitcoinBalance({ params: { address, rpcUrl? }, queryOptions }); // Personal wallet (default mempool.space)
18
- useTradingWalletBalance({ params: { walletProvider, tradingAddress }, queryOptions }); // Radfi API
19
-
20
- // Operations
21
- useFundTradingWallet({ mutationOptions });
22
- useRadfiWithdraw({ mutationOptions });
23
- useExpiredUtxos({ params: { walletProvider, tradingAddress }, queryOptions }); // Polls 60s
24
- useRenewUtxos({ mutationOptions });
25
- ```
26
-
27
- ## Session flow
28
-
29
- Radfi requires authentication before trading operations. `useRadfiSession` handles the full lifecycle:
30
-
31
- ```ts
32
- // @ai-snippets-skip
33
- const { walletAddress, isAuthed, tradingAddress, login, isLoginPending } = useRadfiSession(walletProvider);
34
- ```
35
-
36
- Lifecycle behavior:
37
- - On mount: refreshes token to validate existing session
38
- - Every 5 min: auto-refreshes access token
39
- - If refresh fails: clears session, sets `isAuthed = false`
40
- - Session persisted in localStorage (keyed by wallet address)
41
-
42
- ## Mutation TVars
43
-
44
- ```ts
45
- // @ai-snippets-skip
46
- // useFundTradingWallet
47
- type FundTradingWalletVars = { amount: bigint; walletProvider: IBitcoinWalletProvider };
48
-
49
- // useRadfiWithdraw
50
- type RadfiWithdrawVars = {
51
- amount: string;
52
- tokenId: string; // e.g. '0:0'
53
- withdrawTo: string; // user's personal BTC address
54
- walletProvider: IBitcoinWalletProvider;
55
- };
56
-
57
- // useRenewUtxos
58
- type RenewUtxosVars = { txIdVouts: string[]; walletProvider: IBitcoinWalletProvider };
59
- ```
60
-
61
- ## Return shapes
62
-
63
- | Hook | Returns |
64
- |---|---|
65
- | `useRadfiAuth` | `SafeUseMutationResult<RadfiAuthResult, Error, UseRadfiAuthVars>` where `RadfiAuthResult = { accessToken, refreshToken, tradingAddress }` (3 fields, NOT `RadfiSession` — the hook persists `publicKey` to localStorage internally but doesn't return it) |
66
- | `useFundTradingWallet` | `SafeUseMutationResult<TxId, Error, ...>` |
67
- | `useRadfiWithdraw` | `SafeUseMutationResult<{ txId, fee }, Error, ...>` |
68
- | `useRenewUtxos` | `SafeUseMutationResult<TxId, Error, ...>` |
69
- | `useRadfiSession` | `{ walletAddress, isAuthed, tradingAddress, login, isLoginPending }` (utility, not Query/Mutation) |
70
- | `useTradingWallet` | `{ tradingAddress: string \| undefined }` (synchronous from localStorage) |
71
- | `useBitcoinBalance` | `UseQueryResult<bigint, Error>` (BTC balance in sats; default Esplora is mempool.space, override via `rpcUrl`) |
72
- | `useTradingWalletBalance` | `UseQueryResult<RadfiWalletBalance, Error>` where `RadfiWalletBalance = { btcSatoshi: bigint; pendingSatoshi: bigint; externalPendingSatoshi: bigint; totalUtxos: number }` |
73
- | `useExpiredUtxos` | `UseQueryResult<RadfiUtxo[], Error>` where `RadfiUtxo = { _id, txid, vout, txidVout, satoshi, amount, address, isSpent, status, source, runes? }` |
74
-
75
- ## Gotchas
76
-
77
- 1. **Authentication required before any trading operation.** `useRadfiSession` manages this automatically — gate buttons on `isAuthed`.
78
- 2. **Trading wallet is created during first authentication** — not a separate step.
79
- 3. **`useTradingWallet(walletAddress)` is synchronous** (no network call). It reads persisted Radfi session from localStorage. Use it when you don't have a `walletProvider` available yet but need the trading address.
80
- 4. **PSBT signing flow for withdrawals.** Radfi server builds an unsigned PSBT, the user signs locally via the wallet provider, then submits back for co-signing and broadcast. The dapp-kit hook orchestrates this — consumers don't see PSBTs directly.
81
- 5. **Session tokens are for API rate-limiting, not asset access.** Stored in localStorage keyed by wallet address. Compromise of localStorage data doesn't affect funds.
82
- 6. **`useExpiredUtxos` polls every 60s** — set `queryOptions.refetchInterval: false` to disable while UI is hidden.
83
-
84
- ## Cross-references
85
-
86
- - [`../recipes/bitcoin.md`](../recipes/bitcoin.md) — full worked examples (session, fund, withdraw, UTXO management).
87
- - [`../../migration/features/bitcoin.md`](../../migration/features/bitcoin.md) — v1 → v2 porting (mostly hook signature shape changes; flow stays the same).
@@ -1,91 +0,0 @@
1
- # Bridge — `@sodax/dapp-kit`
2
-
3
- Cross-chain token transfers via the hub-and-spoke vault architecture.
4
-
5
- Pair: [`../../migration/features/bridge.md`](../../migration/features/bridge.md).
6
-
7
- ## Hook surface
8
-
9
- ```ts
10
- // @ai-snippets-skip
11
- // Mutation
12
- useBridge({ mutationOptions });
13
- useBridgeApprove({ mutationOptions });
14
-
15
- // Queries
16
- // useBridgeAllowance nests payload + walletProvider under params (NOT at top level)
17
- useBridgeAllowance({ params: { payload: CreateBridgeIntentParams<K>, walletProvider }, queryOptions });
18
- useGetBridgeableAmount({ params: { from: XToken, to: XToken }, queryOptions });
19
- useGetBridgeableTokens({ params: { from: SpokeChainKey, to: SpokeChainKey, token: string }, queryOptions });
20
- ```
21
-
22
- ## Mutation params
23
-
24
- ```ts
25
- // @ai-snippets-skip
26
- type CreateBridgeIntentParams<K extends SpokeChainKey = SpokeChainKey> = {
27
- srcChainKey: K;
28
- srcAddress: string;
29
- srcToken: string;
30
- amount: bigint;
31
- dstChainKey: SpokeChainKey;
32
- dstToken: string;
33
- recipient: string; // non-encoded recipient address on the destination chain
34
- };
35
-
36
- const { mutateAsyncSafe: bridge } = useBridge();
37
- const result = await bridge({ params, walletProvider });
38
- if (!result.ok) return;
39
- const { srcChainTxHash, dstChainTxHash } = result.value; // TxHashPair
40
- ```
41
-
42
- ## Query params
43
-
44
- ```ts
45
- // @ai-snippets-skip
46
- // useBridgeAllowance — payload + walletProvider nested under params
47
- type UseBridgeAllowanceParams<K extends SpokeChainKey> = ReadHookParams<
48
- boolean,
49
- {
50
- payload: CreateBridgeIntentParams<K> | undefined;
51
- walletProvider: GetWalletProviderType<K> | undefined;
52
- }
53
- >;
54
-
55
- // useGetBridgeableAmount — flat: pair of XToken objects
56
- type UseGetBridgeableAmountParams = ReadHookParams<BridgeLimit, {
57
- from: XToken | undefined;
58
- to: XToken | undefined;
59
- }>;
60
- // BridgeLimit = { amount: bigint; decimals: number; type: 'DEPOSIT_LIMIT' | 'WITHDRAWAL_LIMIT' }
61
-
62
- // useGetBridgeableTokens — flat: (from, to, token)
63
- type UseGetBridgeableTokensParams = ReadHookParams<XToken[], {
64
- from: SpokeChainKey | undefined;
65
- to: SpokeChainKey | undefined;
66
- token: string | undefined;
67
- }>;
68
- ```
69
-
70
- ## Return shapes
71
-
72
- | Hook | Returns |
73
- |---|---|
74
- | `useBridge` | `SafeUseMutationResult<TxHashPair, Error, ...>` (`{ srcChainTxHash, dstChainTxHash }`) |
75
- | `useBridgeApprove` | `SafeUseMutationResult<TxReturnType<K, false>, Error, UseBridgeApproveVars<K>>` — chain-keyed receipt union (EVM/Stellar/Sui differ) |
76
- | `useBridgeAllowance` | `UseQueryResult<boolean, Error>` — already unwrapped; on SDK `!ok` the queryFn returns `false` (does NOT throw), so `isError` stays clean |
77
- | `useGetBridgeableAmount` | `UseQueryResult<BridgeLimit, Error>` (richer than v1's bare `bigint`) |
78
- | `useGetBridgeableTokens` | `UseQueryResult<XToken[], Error>` |
79
-
80
- ## Gotchas
81
-
82
- 1. **`useGetBridgeableAmount` takes XToken objects, not addresses + chain ids.** Each `XToken` carries its own `chainKey`. v1 took 4 separate args; v2 takes 2 objects.
83
- 2. **`useGetBridgeableAmount` value is `BridgeLimit`, not a bare bigint.** Access `result.value.amount` and `result.value.decimals` for the limit + scale.
84
- 3. **Tokens are bridgeable iff they share the same vault on the hub.** Use `useGetBridgeableTokens` to enumerate compatible destinations for a given source — passing an incompatible pair to `bridge()` rejects with `VALIDATION_FAILED`.
85
- 4. **`bridge()` returns `TxHashPair`, not a tuple.** Destructure as `{ srcChainTxHash, dstChainTxHash }` — never `[a, b]`.
86
-
87
- ## Cross-references
88
-
89
- - [`../recipes/bridge.md`](../recipes/bridge.md) — full worked example.
90
- - [`../../migration/features/bridge.md`](../../migration/features/bridge.md) — v1 → v2 porting.
91
- - [`../../../../sdk/ai-exported/integration/features/bridge.md`](../../../../sdk/ai-exported/integration/features/bridge.md) — underlying SDK bridge surface.