@sodax/dapp-kit 2.0.0-rc.3 → 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 (61) hide show
  1. package/README.md +9 -63
  2. package/dist/index.mjs +0 -2
  3. package/package.json +31 -20
  4. package/ai-exported/AGENTS.md +0 -134
  5. package/ai-exported/integration/README.md +0 -49
  6. package/ai-exported/integration/ai-rules.md +0 -80
  7. package/ai-exported/integration/architecture.md +0 -276
  8. package/ai-exported/integration/features/README.md +0 -29
  9. package/ai-exported/integration/features/auxiliary-services.md +0 -169
  10. package/ai-exported/integration/features/bitcoin.md +0 -87
  11. package/ai-exported/integration/features/bridge.md +0 -91
  12. package/ai-exported/integration/features/dex.md +0 -152
  13. package/ai-exported/integration/features/migration.md +0 -118
  14. package/ai-exported/integration/features/money-market.md +0 -144
  15. package/ai-exported/integration/features/staking.md +0 -123
  16. package/ai-exported/integration/features/swap.md +0 -101
  17. package/ai-exported/integration/quickstart.md +0 -187
  18. package/ai-exported/integration/recipes/README.md +0 -136
  19. package/ai-exported/integration/recipes/backend-queries.md +0 -157
  20. package/ai-exported/integration/recipes/bitcoin.md +0 -193
  21. package/ai-exported/integration/recipes/bridge.md +0 -174
  22. package/ai-exported/integration/recipes/dex.md +0 -204
  23. package/ai-exported/integration/recipes/invalidations.md +0 -115
  24. package/ai-exported/integration/recipes/migration.md +0 -212
  25. package/ai-exported/integration/recipes/money-market.md +0 -207
  26. package/ai-exported/integration/recipes/mutation-error-handling.md +0 -118
  27. package/ai-exported/integration/recipes/observability.md +0 -93
  28. package/ai-exported/integration/recipes/setup.md +0 -168
  29. package/ai-exported/integration/recipes/staking.md +0 -202
  30. package/ai-exported/integration/recipes/swap.md +0 -272
  31. package/ai-exported/integration/recipes/wallet-connectivity.md +0 -128
  32. package/ai-exported/integration/reference/README.md +0 -12
  33. package/ai-exported/integration/reference/glossary.md +0 -190
  34. package/ai-exported/integration/reference/hooks-index.md +0 -190
  35. package/ai-exported/integration/reference/public-api.md +0 -110
  36. package/ai-exported/integration/reference/querykey-conventions.md +0 -179
  37. package/ai-exported/migration/README.md +0 -60
  38. package/ai-exported/migration/ai-rules.md +0 -81
  39. package/ai-exported/migration/breaking-changes/hook-signatures.md +0 -233
  40. package/ai-exported/migration/breaking-changes/querykey-conventions.md +0 -108
  41. package/ai-exported/migration/breaking-changes/result-handling.md +0 -211
  42. package/ai-exported/migration/breaking-changes/sdk-leakage.md +0 -167
  43. package/ai-exported/migration/checklist.md +0 -89
  44. package/ai-exported/migration/features/README.md +0 -34
  45. package/ai-exported/migration/features/auxiliary-services.md +0 -114
  46. package/ai-exported/migration/features/bitcoin.md +0 -88
  47. package/ai-exported/migration/features/bridge.md +0 -160
  48. package/ai-exported/migration/features/dex.md +0 -101
  49. package/ai-exported/migration/features/migration.md +0 -120
  50. package/ai-exported/migration/features/money-market.md +0 -139
  51. package/ai-exported/migration/features/staking.md +0 -109
  52. package/ai-exported/migration/features/swap.md +0 -133
  53. package/ai-exported/migration/recipes.md +0 -185
  54. package/ai-exported/migration/reference/README.md +0 -15
  55. package/ai-exported/migration/reference/deleted-hooks.md +0 -110
  56. package/ai-exported/migration/reference/error-shape-crosswalk.md +0 -144
  57. package/ai-exported/migration/reference/renamed-hooks.md +0 -68
  58. package/dist/index.cjs +0 -2641
  59. package/dist/index.cjs.map +0 -1
  60. package/dist/index.d.cts +0 -1557
  61. package/dist/index.mjs.map +0 -1
@@ -1,187 +0,0 @@
1
- # Quickstart — `@sodax/dapp-kit` v2
2
-
3
- Get a React app running with dapp-kit in five minutes. For copy-paste recipes per feature, see [`recipes/`](recipes/). For design rationale, see [`architecture.md`](architecture.md).
4
-
5
- ## 1. Install
6
-
7
- ```bash
8
- # Required
9
- pnpm add @sodax/dapp-kit @tanstack/react-query
10
-
11
- # Wallet connectivity (the canonical companion)
12
- pnpm add @sodax/wallet-sdk-react
13
- ```
14
-
15
- `@sodax/dapp-kit` peers on `react`, `react-dom` (>=18), and `@tanstack/react-query`. It re-exports `@sodax/sdk` — don't add `@sodax/types` separately.
16
-
17
- ## 2. Wire providers
18
-
19
- ```tsx
20
- // providers.tsx
21
- import { QueryClientProvider } from '@tanstack/react-query';
22
- import { SodaxProvider, createSodaxQueryClient } from '@sodax/dapp-kit';
23
- import { SodaxWalletProvider, type SodaxWalletConfig } from '@sodax/wallet-sdk-react';
24
- import { ChainKeys, type DeepPartial, type SodaxConfig } from '@sodax/sdk';
25
-
26
- const queryClient = createSodaxQueryClient();
27
-
28
- const sodaxConfig: DeepPartial<SodaxConfig> = {
29
- chains: {
30
- [ChainKeys.SONIC_MAINNET]: { rpcUrl: 'https://sonic-rpc.publicnode.com' },
31
- [ChainKeys.BSC_MAINNET]: { rpcUrl: 'https://bsc-dataseed.binance.org' },
32
- [ChainKeys.BASE_MAINNET]: { rpcUrl: 'https://mainnet.base.org' },
33
- [ChainKeys.ARBITRUM_MAINNET]: { rpcUrl: 'https://arb1.arbitrum.io/rpc' },
34
- // Add chains your dApp needs
35
- },
36
- };
37
-
38
- const walletConfig: SodaxWalletConfig = {
39
- EVM: {
40
- chains: {
41
- [ChainKeys.BSC_MAINNET]: { rpcUrl: 'https://bsc-dataseed.binance.org' },
42
- [ChainKeys.BASE_MAINNET]: { rpcUrl: 'https://mainnet.base.org' },
43
- },
44
- },
45
- };
46
-
47
- export function Providers({ children }: { children: React.ReactNode }) {
48
- return (
49
- <SodaxProvider config={sodaxConfig}>
50
- <QueryClientProvider client={queryClient}>
51
- <SodaxWalletProvider config={walletConfig}>
52
- {children}
53
- </SodaxWalletProvider>
54
- </QueryClientProvider>
55
- </SodaxProvider>
56
- );
57
- }
58
- ```
59
-
60
- `createSodaxQueryClient` gives you a `QueryClient` pre-wired with global mutation observability. Optional — if you construct your own `QueryClient`, nothing changes. See [`recipes/observability.md`](recipes/observability.md).
61
-
62
- ## 3. Initialize the SDK (optional)
63
-
64
- For the latest tokens / chains / fee parameters from the backend:
65
-
66
- ```tsx
67
- import { useEffect } from 'react';
68
- import { useSodaxContext } from '@sodax/dapp-kit';
69
-
70
- export function useInitializeSodax() {
71
- const { sodax } = useSodaxContext();
72
- useEffect(() => {
73
- sodax.config.initialize().then((result) => {
74
- if (!result.ok) console.error('Failed to initialize Sodax:', result.error);
75
- });
76
- }, [sodax]);
77
- }
78
- ```
79
-
80
- Skipping this works — feature services fall back to packaged defaults — but you'll miss tokens / chains added after the SDK release.
81
-
82
- ## 4. Get a wallet provider
83
-
84
- ```tsx
85
- import { useWalletProvider } from '@sodax/wallet-sdk-react';
86
- import { ChainKeys } from '@sodax/sdk';
87
-
88
- function MyFeature() {
89
- const walletProvider = useWalletProvider({ xChainId: ChainKeys.BSC_MAINNET });
90
- // undefined until the user connects a BSC wallet
91
- return <button disabled={!walletProvider}>...</button>;
92
- }
93
- ```
94
-
95
- `useWalletProvider` returns a chain-specific wallet provider object that satisfies `IEvmWalletProvider` (or `IIconWalletProvider`, `ISolanaWalletProvider`, etc., depending on the chain).
96
-
97
- ## 5. Run a mutation
98
-
99
- The canonical pattern: hook takes `{ mutationOptions }` (optional); domain inputs flow through `mutate(vars)`. Use `mutateAsyncSafe` for explicit `Result<T>` branching:
100
-
101
- ```tsx
102
- import { useSwap } from '@sodax/dapp-kit';
103
- import { useWalletProvider } from '@sodax/wallet-sdk-react';
104
- import { ChainKeys } from '@sodax/sdk';
105
- import type { CreateIntentParams } from '@sodax/sdk';
106
-
107
- function SwapButton({ intentParams }: { intentParams: CreateIntentParams }) {
108
- const walletProvider = useWalletProvider({ xChainId: ChainKeys.BSC_MAINNET });
109
- const { mutateAsyncSafe: swap, isPending } = useSwap();
110
-
111
- const handleSwap = async () => {
112
- if (!walletProvider) return;
113
- const result = await swap({ params: intentParams, walletProvider });
114
- if (!result.ok) {
115
- alert(result.error instanceof Error ? result.error.message : 'Swap failed');
116
- return;
117
- }
118
- console.log('Swap submitted!', result.value);
119
- };
120
-
121
- return (
122
- <button onClick={handleSwap} disabled={isPending || !walletProvider}>
123
- {isPending ? 'Swapping...' : 'Swap'}
124
- </button>
125
- );
126
- }
127
- ```
128
-
129
- The exact same pattern works for every mutation: `useBridge`, `useSupply`, `useStake`, `useDexDeposit`, etc. The only difference is the `params` shape (each feature's reference doc has the type signature).
130
-
131
- ## 6. Run a query
132
-
133
- Queries take `{ params, queryOptions }`. The hook owns `queryKey`, `queryFn`, and `enabled`:
134
-
135
- ```tsx
136
- import { useQuote } from '@sodax/dapp-kit';
137
- import { ChainKeys } from '@sodax/sdk';
138
-
139
- function SwapQuote({ amount }: { amount: bigint }) {
140
- const { data: quoteResult, isLoading } = useQuote({
141
- params: {
142
- payload: amount > 0n
143
- ? {
144
- token_src: '0x0000000000000000000000000000000000000000',
145
- token_dst: '0x0000000000000000000000000000000000000000',
146
- token_src_blockchain_id: ChainKeys.BSC_MAINNET,
147
- token_dst_blockchain_id: ChainKeys.ARBITRUM_MAINNET,
148
- amount,
149
- quote_type: 'exact_input',
150
- }
151
- : undefined,
152
- },
153
- queryOptions: { staleTime: 3000 },
154
- });
155
-
156
- if (isLoading) return <p>Loading...</p>;
157
- if (quoteResult?.ok) return <p>Output: {quoteResult.value.quoted_amount}</p>;
158
- return null;
159
- }
160
- ```
161
-
162
- Some query hooks return `Result<T>` as their `data` (the underlying SDK method returns Result and we surface it directly for query hooks; only mutations unwrap). Always check `.ok` before reading `.value`.
163
-
164
- ## First-time troubleshooting
165
-
166
- | Error | Why | Fix |
167
- |---|---|---|
168
- | `Module '"@sodax/dapp-kit"' has no exported member 'useSpokeProvider'` | v1 hook deleted in v2. | Drop the import. Pass `walletProvider` (from `useWalletProvider`) into `mutate(vars)` instead. |
169
- | `Property 'approve' does not exist on type 'SafeUseMutationResult'` | v1 approve hooks returned `{ approve, isLoading, error }`; v2 returns `SafeUseMutationResult`. | Use `mutateAsync` / `mutateAsyncSafe`. `isLoading` → `isPending`. |
170
- | `Type 'CreateIntentParams' is missing the following properties from ...: walletProvider` | v1 hooks took params at hook-init; v2 takes them via `mutate(vars)`. | Move `params` and `walletProvider` from hook init to `mutate({ params, walletProvider })`. |
171
- | `Property 'xChainId' does not exist on type 'XToken'` | SDK-leakage rename: `xChainId` → `chainKey` on `XToken` in v2. | Use `xToken.chainKey`. (Note: `useXBalances` params still use `xChainId` for the request shape — that's distinct from the read shape.) |
172
- | `Type ... is missing the following properties from type 'MoneyMarketSupplyParams': srcChainKey, srcAddress` | SDK-leakage: v2 added required `srcChainKey` + `srcAddress` to action params. | Add both to your `params` payload. See [`features/money-market.md`](features/money-market.md). |
173
- | `Cannot read properties of undefined (reading 'sodax')` | `useSodaxContext` (or any dapp-kit hook) called outside `<SodaxProvider>`. | Wrap your component tree in `<SodaxProvider>` from this package. |
174
-
175
- For broader v1 → v2 migration, see [`../migration/README.md`](../migration/README.md).
176
-
177
- ## What to read next
178
-
179
- - [`recipes/`](recipes/) — copy-paste patterns for each feature and cross-cutting concerns.
180
- - [`architecture.md`](architecture.md) — full design rationale for `useSafeMutation`, `unwrapResult`, queryKey conventions, etc.
181
- - [`features/<x>.md`](features/) — per-feature reference (hook tables, types, gotchas).
182
- - [`reference/hooks-index.md`](reference/hooks-index.md) — full hook table.
183
- - [`ai-rules.md`](ai-rules.md) — DO / DO NOT for AI agents writing dapp-kit code.
184
-
185
- ## Cross-references
186
-
187
- - [`../../../sdk/ai-exported/AGENTS.md`](../../../sdk/ai-exported/AGENTS.md) — the underlying Core SDK's tree (resolves correctly in `node_modules/@sodax/`-layout). Useful when you hit SDK-level types or behaviors leaking through hook signatures.
@@ -1,136 +0,0 @@
1
- # Recipes — `@sodax/dapp-kit`
2
-
3
- Copy-paste patterns for adding SODAX features to a React app. Each recipe is self-contained and shows the canonical v2 hook shape (single-object params, `mutateAsyncSafe` for imperative flows, `Result<T>` handling).
4
-
5
- ## Reading order
6
-
7
- 1. **[`setup.md`](setup.md)** — Install packages, wire `SodaxProvider` + `QueryClientProvider`, optional `createSodaxQueryClient` for global mutation observability.
8
- 2. **[`wallet-connectivity.md`](wallet-connectivity.md)** — Connect wallets via `@sodax/wallet-sdk-react`, get a typed `walletProvider` per chain, fetch token balances.
9
- 3. Pick a feature recipe — each one is independent of the others.
10
-
11
- ## Index
12
-
13
- ### Foundation
14
-
15
- | Recipe | Purpose |
16
- |---|---|
17
- | [`setup.md`](setup.md) | Install packages, wire providers, optional `createSodaxQueryClient` |
18
- | [`wallet-connectivity.md`](wallet-connectivity.md) | Wallet connection, `useWalletProvider`, balance hooks |
19
-
20
- ### Cross-cutting patterns
21
-
22
- | Recipe | Purpose |
23
- |---|---|
24
- | [`mutation-error-handling.md`](mutation-error-handling.md) | Pick between `mutate` / `mutateAsync` / `mutateAsyncSafe` |
25
- | [`observability.md`](observability.md) | Global mutation logging via `createSodaxQueryClient`, per-mutation `meta.silent` |
26
- | [`invalidations.md`](invalidations.md) | Hook-owned invalidations and how to compose your own `onSuccess` |
27
-
28
- ### Per-feature
29
-
30
- | Recipe | Hooks covered |
31
- |---|---|
32
- | [`swap.md`](swap.md) | `useQuote`, `useSwap`, `useSwapAllowance`, `useSwapApprove`, limit orders |
33
- | [`bridge.md`](bridge.md) | `useBridge`, allowance/approval, bridgeable amounts/tokens |
34
- | [`money-market.md`](money-market.md) | `useSupply`, `useBorrow`, `useWithdraw`, `useRepay`, reserves data |
35
- | [`staking.md`](staking.md) | `useStake`, `useUnstake`, `useClaim`, staking info, ratios |
36
- | [`migration.md`](migration.md) | `useMigrateIcxToSoda`, `useRevertMigrateSodaToIcx`, `useMigratebnUSD`, `useMigrateBaln` |
37
- | [`dex.md`](dex.md) | `useDexDeposit`, `useSupplyLiquidity`, positions, pools |
38
- | [`bitcoin.md`](bitcoin.md) | `useRadfiSession`, `useFundTradingWallet`, `useRadfiWithdraw`, UTXO management |
39
- | [`backend-queries.md`](backend-queries.md) | Intent tracking, orderbook, money market position queries (read-only, no wallet) |
40
-
41
- ## Hook conventions (mandatory)
42
-
43
- These rules are enforced across every dapp-kit hook by [`packages/dapp-kit/src/hooks/_mutationContract.test.ts`](../../../src/hooks/_mutationContract.test.ts) and apply to everything you write against this package.
44
-
45
- ### Single-object params
46
-
47
- ```tsx
48
- import { useSwap, useSwapAllowance } from '@sodax/dapp-kit';
49
- import { ChainKeys } from '@sodax/sdk';
50
-
51
- // Query hooks — { params, queryOptions }
52
- // Note: many query hooks wrap the SDK request under `params.payload`; some nest sibling
53
- // fields like `walletProvider` or `srcChainKey` under `params` alongside `payload`.
54
- // `useSwapAllowance` is one such hook — see features/swap.md for the canonical shape.
55
- const { data: isApproved } = useSwapAllowance({
56
- params: { payload: intentParams, srcChainKey: ChainKeys.BSC_MAINNET, walletProvider },
57
- });
58
-
59
- // Mutation hooks — hook takes only mutationOptions; domain inputs flow through mutate(vars)
60
- const { mutateAsync: swap } = useSwap();
61
- async function runSwap() {
62
- if (!walletProvider) return;
63
- await swap({ params: intentParams, walletProvider });
64
- }
65
- ```
66
-
67
- No positional args. No `spokeProvider` at the hook level (that was v1; deleted in v2). All domain inputs (`params`, `walletProvider`, per-call config) flow through `mutate(vars)` for mutations and through `params` for queries.
68
-
69
- ### `mutateAsyncSafe`
70
-
71
- Every mutation hook returns three call shapes. `mutateAsyncSafe` is the recommended default for sequenced flows — it returns `Promise<Result<TData>>` and never rejects:
72
-
73
- ```tsx
74
- import { useSwap } from '@sodax/dapp-kit';
75
-
76
- const { mutateAsyncSafe: swap } = useSwap();
77
- async function runSwap() {
78
- if (!walletProvider) return;
79
- const result = await swap({ params: intentParams, walletProvider });
80
- if (!result.ok) { toast.error(result.error instanceof Error ? result.error.message : 'failed'); return; }
81
- const { intent } = result.value;
82
- console.log(intent);
83
- }
84
- ```
85
-
86
- Full comparison in [`mutation-error-handling.md`](mutation-error-handling.md).
87
-
88
- ### `queryOptions`
89
-
90
- All query hooks accept optional `queryOptions` to override React Query defaults. The hook owns `queryKey`, `queryFn`, and `enabled` — those are not consumer-overridable.
91
-
92
- ```tsx
93
- import { useQuote } from '@sodax/dapp-kit';
94
-
95
- // `useQuote` wraps the SDK request under `params.payload` — don't pass the SDK request
96
- // directly under `params`. `payload` here is a `SolverIntentQuoteRequest`.
97
- const { data } = useQuote({
98
- params: { payload },
99
- queryOptions: { staleTime: 5000, refetchInterval: 10000 },
100
- });
101
- ```
102
-
103
- ### `Result<T>` in query hooks
104
-
105
- Some query hooks return `Result<T>` as their `data` (the underlying SDK method can fail). Always check `.ok` before accessing `.value`:
106
-
107
- ```tsx
108
- import { useQuote } from '@sodax/dapp-kit';
109
-
110
- // useQuote nests the SDK request under params.payload. `payload` here is a `SolverIntentQuoteRequest`.
111
- const { data: quoteResult } = useQuote({ params: { payload } });
112
- if (quoteResult?.ok) {
113
- const quote = quoteResult.value;
114
- console.log(quote);
115
- } else {
116
- console.error(quoteResult?.error);
117
- }
118
- ```
119
-
120
- ### `bigint` for amounts
121
-
122
- Token amounts are `bigint` scaled by decimals. Use `viem`'s `parseUnits` / `formatUnits`:
123
-
124
- ```tsx
125
- import { parseUnits, formatUnits } from 'viem';
126
- const amount = parseUnits('1.5', 18); // 1500000000000000000n
127
- const display = formatUnits(amount, 18); // '1.5'
128
- ```
129
-
130
- ## Backend / non-React consumers
131
-
132
- If you're building a backend (API server, bot, script), you don't need `@sodax/dapp-kit` at all — use `@sodax/sdk` directly. The SDK has its own AI-agent docs at `node_modules/@sodax/sdk/ai-exported/AGENTS.md`.
133
-
134
- ## Migration pointer
135
-
136
- If you're porting v1 dapp-kit code to v2, start at [`../../migration/README.md`](../../migration/README.md). It covers: hook signatures (single-arg policy), `Result<T>` handling shift, deleted `useSpokeProvider`, queryKey conventions, and SDK leakage.
@@ -1,157 +0,0 @@
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 |
@@ -1,193 +0,0 @@
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.