@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.
- package/README.md +9 -63
- package/dist/index.d.ts +11 -4
- package/dist/index.mjs +3 -6
- package/package.json +31 -19
- package/src/providers/SodaxProvider.tsx +15 -11
- package/ai-exported/AGENTS.md +0 -134
- package/ai-exported/integration/README.md +0 -49
- package/ai-exported/integration/ai-rules.md +0 -79
- package/ai-exported/integration/architecture.md +0 -274
- package/ai-exported/integration/features/README.md +0 -29
- package/ai-exported/integration/features/auxiliary-services.md +0 -169
- package/ai-exported/integration/features/bitcoin.md +0 -87
- package/ai-exported/integration/features/bridge.md +0 -91
- package/ai-exported/integration/features/dex.md +0 -152
- package/ai-exported/integration/features/migration.md +0 -118
- package/ai-exported/integration/features/money-market.md +0 -116
- package/ai-exported/integration/features/staking.md +0 -123
- package/ai-exported/integration/features/swap.md +0 -101
- package/ai-exported/integration/quickstart.md +0 -187
- package/ai-exported/integration/recipes/README.md +0 -136
- package/ai-exported/integration/recipes/backend-queries.md +0 -157
- package/ai-exported/integration/recipes/bitcoin.md +0 -193
- package/ai-exported/integration/recipes/bridge.md +0 -174
- package/ai-exported/integration/recipes/dex.md +0 -204
- package/ai-exported/integration/recipes/invalidations.md +0 -115
- package/ai-exported/integration/recipes/migration.md +0 -212
- package/ai-exported/integration/recipes/money-market.md +0 -206
- package/ai-exported/integration/recipes/mutation-error-handling.md +0 -118
- package/ai-exported/integration/recipes/observability.md +0 -93
- package/ai-exported/integration/recipes/setup.md +0 -144
- package/ai-exported/integration/recipes/staking.md +0 -202
- package/ai-exported/integration/recipes/swap.md +0 -272
- package/ai-exported/integration/recipes/wallet-connectivity.md +0 -101
- package/ai-exported/integration/reference/README.md +0 -12
- package/ai-exported/integration/reference/glossary.md +0 -188
- package/ai-exported/integration/reference/hooks-index.md +0 -190
- package/ai-exported/integration/reference/public-api.md +0 -110
- package/ai-exported/integration/reference/querykey-conventions.md +0 -179
- package/ai-exported/migration/README.md +0 -60
- package/ai-exported/migration/ai-rules.md +0 -81
- package/ai-exported/migration/breaking-changes/hook-signatures.md +0 -233
- package/ai-exported/migration/breaking-changes/querykey-conventions.md +0 -108
- package/ai-exported/migration/breaking-changes/result-handling.md +0 -211
- package/ai-exported/migration/breaking-changes/sdk-leakage.md +0 -165
- package/ai-exported/migration/checklist.md +0 -89
- package/ai-exported/migration/features/README.md +0 -34
- package/ai-exported/migration/features/auxiliary-services.md +0 -114
- package/ai-exported/migration/features/bitcoin.md +0 -88
- package/ai-exported/migration/features/bridge.md +0 -123
- package/ai-exported/migration/features/dex.md +0 -101
- package/ai-exported/migration/features/migration.md +0 -120
- package/ai-exported/migration/features/money-market.md +0 -97
- package/ai-exported/migration/features/staking.md +0 -109
- package/ai-exported/migration/features/swap.md +0 -118
- package/ai-exported/migration/recipes.md +0 -188
- package/ai-exported/migration/reference/README.md +0 -15
- package/ai-exported/migration/reference/deleted-hooks.md +0 -110
- package/ai-exported/migration/reference/error-shape-crosswalk.md +0 -144
- package/ai-exported/migration/reference/renamed-hooks.md +0 -66
- package/dist/index.cjs +0 -2642
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -1550
- 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.
|