@sodax/wallet-sdk-react 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.
- package/README.md +12 -5
- package/dist/{chunk-BKJB527E.mjs → chunk-3QETHO6P.mjs} +1 -3
- package/dist/{chunk-PJLEJVAU.mjs → chunk-42LTUHMZ.mjs} +1 -3
- package/dist/{chunk-NY7U7OJW.mjs → chunk-7V7O3Q7Y.mjs} +0 -2
- package/dist/{chunk-BXJLBR4G.mjs → chunk-C6M34IVL.mjs} +2 -4
- package/dist/{chunk-XZ7CHO2S.mjs → chunk-FSOGMSJH.mjs} +2 -4
- package/dist/{chunk-X2MHIWXO.mjs → chunk-IFXZQW4C.mjs} +0 -2
- package/dist/{chunk-7ULB6DW4.mjs → chunk-JQ4H4GJ5.mjs} +3 -5
- package/dist/{chunk-N5A2TMF6.mjs → chunk-LKSSME2J.mjs} +2 -4
- package/dist/{chunk-PLCA4ZDJ.mjs → chunk-LUKR7YKV.mjs} +54 -30
- package/dist/{chunk-MXZVF5HR.mjs → chunk-NAKCAL2M.mjs} +0 -2
- package/dist/chunk-QMXBY3UI.mjs +1 -0
- package/dist/{chunk-MAQ47Q52.mjs → chunk-TACW7Z4D.mjs} +0 -2
- package/dist/{chunk-2BOUGCJ7.mjs → chunk-WPZOLGVB.mjs} +4 -6
- package/dist/{chunk-66BAUK56.mjs → chunk-X7BHR7WS.mjs} +2 -4
- package/dist/{chunk-E5IAZ7E6.mjs → chunk-Z5GXDHGL.mjs} +9 -5
- package/dist/{config-OlnzyEUE.d.ts → config-GVKK8IfY.d.ts} +6 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.mjs +20 -31
- package/dist/xchains/bitcoin/index.mjs +14 -16
- package/dist/xchains/evm/index.d.ts +1 -1
- package/dist/xchains/evm/index.mjs +3 -5
- package/dist/xchains/icon/index.mjs +5 -7
- package/dist/xchains/injective/index.mjs +3 -5
- package/dist/xchains/near/index.mjs +4 -6
- package/dist/xchains/solana/index.mjs +5 -7
- package/dist/xchains/stacks/index.mjs +3 -5
- package/dist/xchains/stellar/index.mjs +4 -6
- package/dist/xchains/sui/index.mjs +5 -7
- package/docs/ADDING_A_NEW_CHAIN.md +1 -1
- package/docs/SUB_PATH_EXPORTS.md +14 -42
- package/package.json +32 -24
- package/ai-exported/AGENTS.md +0 -122
- package/ai-exported/integration/README.md +0 -102
- package/ai-exported/integration/ai-rules.md +0 -136
- package/ai-exported/integration/architecture.md +0 -181
- package/ai-exported/integration/examples/01-minimal-evm.tsx +0 -75
- package/ai-exported/integration/examples/02-multi-chain-modal.tsx +0 -169
- package/ai-exported/integration/examples/03-nextjs-app-router.tsx +0 -99
- package/ai-exported/integration/examples/04-walletconnect-setup.tsx +0 -89
- package/ai-exported/integration/examples/README.md +0 -29
- package/ai-exported/integration/recipes/batch-operations.md +0 -223
- package/ai-exported/integration/recipes/bridge-to-sdk.md +0 -164
- package/ai-exported/integration/recipes/chain-detection.md +0 -254
- package/ai-exported/integration/recipes/connect-button.md +0 -156
- package/ai-exported/integration/recipes/multi-chain-modal.md +0 -199
- package/ai-exported/integration/recipes/setup.md +0 -160
- package/ai-exported/integration/recipes/sign-message.md +0 -137
- package/ai-exported/integration/recipes/sub-path-imports.md +0 -95
- package/ai-exported/integration/recipes/switch-chain.md +0 -141
- package/ai-exported/integration/recipes/walletconnect-setup.md +0 -139
- package/ai-exported/integration/reference/api-surface.md +0 -175
- package/ai-exported/integration/reference/chain-support.md +0 -78
- package/ai-exported/integration/reference/connectors.md +0 -74
- package/ai-exported/integration/reference/hooks.md +0 -204
- package/ai-exported/integration/reference/wallet-brands.md +0 -106
- package/ai-exported/migration/README.md +0 -49
- package/ai-exported/migration/ai-rules.md +0 -144
- package/ai-exported/migration/breaking-changes.md +0 -305
- package/ai-exported/migration/checklist.md +0 -159
- package/ai-exported/migration/recipes/connect-button.md +0 -166
- package/ai-exported/migration/recipes/multi-chain-modal.md +0 -244
- package/ai-exported/migration/recipes/ssr-setup.md +0 -164
- package/ai-exported/migration/recipes/walletconnect-migration.md +0 -168
- package/ai-exported/migration/reference/components.md +0 -73
- package/ai-exported/migration/reference/config.md +0 -325
- package/ai-exported/migration/reference/hooks.md +0 -323
- package/ai-exported/migration/reference/imports.md +0 -157
- package/dist/chunk-2BOUGCJ7.mjs.map +0 -1
- package/dist/chunk-66BAUK56.mjs.map +0 -1
- package/dist/chunk-7ULB6DW4.mjs.map +0 -1
- package/dist/chunk-BKJB527E.mjs.map +0 -1
- package/dist/chunk-BXJLBR4G.mjs.map +0 -1
- package/dist/chunk-E5IAZ7E6.mjs.map +0 -1
- package/dist/chunk-MAQ47Q52.mjs.map +0 -1
- package/dist/chunk-MXZVF5HR.mjs.map +0 -1
- package/dist/chunk-N5A2TMF6.mjs.map +0 -1
- package/dist/chunk-NY7U7OJW.mjs.map +0 -1
- package/dist/chunk-PJLEJVAU.mjs.map +0 -1
- package/dist/chunk-PLCA4ZDJ.mjs.map +0 -1
- package/dist/chunk-TZMKDXFA.mjs +0 -3
- package/dist/chunk-TZMKDXFA.mjs.map +0 -1
- package/dist/chunk-X2MHIWXO.mjs.map +0 -1
- package/dist/chunk-XZ7CHO2S.mjs.map +0 -1
- package/dist/index.cjs +0 -3337
- package/dist/index.cjs.map +0 -1
- package/dist/index.mjs.map +0 -1
- package/dist/xchains/bitcoin/index.cjs +0 -1927
- package/dist/xchains/bitcoin/index.cjs.map +0 -1
- package/dist/xchains/bitcoin/index.mjs.map +0 -1
- package/dist/xchains/evm/index.cjs +0 -316
- package/dist/xchains/evm/index.cjs.map +0 -1
- package/dist/xchains/evm/index.mjs.map +0 -1
- package/dist/xchains/icon/index.cjs +0 -311
- package/dist/xchains/icon/index.cjs.map +0 -1
- package/dist/xchains/icon/index.mjs.map +0 -1
- package/dist/xchains/injective/index.cjs +0 -223
- package/dist/xchains/injective/index.cjs.map +0 -1
- package/dist/xchains/injective/index.mjs.map +0 -1
- package/dist/xchains/near/index.cjs +0 -190
- package/dist/xchains/near/index.cjs.map +0 -1
- package/dist/xchains/near/index.mjs.map +0 -1
- package/dist/xchains/solana/index.cjs +0 -186
- package/dist/xchains/solana/index.cjs.map +0 -1
- package/dist/xchains/solana/index.mjs.map +0 -1
- package/dist/xchains/stacks/index.cjs +0 -240
- package/dist/xchains/stacks/index.cjs.map +0 -1
- package/dist/xchains/stacks/index.mjs.map +0 -1
- package/dist/xchains/stellar/index.cjs +0 -322
- package/dist/xchains/stellar/index.cjs.map +0 -1
- package/dist/xchains/stellar/index.mjs.map +0 -1
- package/dist/xchains/sui/index.cjs +0 -248
- package/dist/xchains/sui/index.cjs.map +0 -1
- package/dist/xchains/sui/index.mjs.map +0 -1
- package/skills/SKILLS.md +0 -84
- package/skills/bridge-to-sdk.md +0 -148
- package/skills/connect-button.md +0 -116
- package/skills/evm-only-walletconnect.md +0 -111
- package/skills/multi-chain-modal.md +0 -178
- package/skills/setup.md +0 -107
|
@@ -1,223 +0,0 @@
|
|
|
1
|
-
# Recipe: Batch Connect / Disconnect
|
|
2
|
-
|
|
3
|
-
`useBatchConnect` and `useBatchDisconnect` orchestrate multi-chain wallet operations sequentially using **wallet brand identifiers** (e.g. `'hana'`, `'phantom'`) instead of individual connectors. Pass an identifier and the hooks discover every chain that wallet supports across the registry, then connect/disconnect them in order.
|
|
4
|
-
|
|
5
|
-
Sequential by design — many extensions share popup singletons, so parallel attempts would race a single popup. Errors are collected, not thrown — `run()` always resolves.
|
|
6
|
-
|
|
7
|
-
**Depends on:** [`setup.md`](./setup.md)
|
|
8
|
-
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
## Identifier matching
|
|
12
|
-
|
|
13
|
-
Both batch hooks (and `useIsWalletInstalled`) take a `connectors: readonly string[]` field. Each entry is a **wallet brand identifier** matched **case-insensitive substring** against `connector.id` and `connector.name`:
|
|
14
|
-
|
|
15
|
-
| Identifier | Matches connectors |
|
|
16
|
-
|------------|--------------------|
|
|
17
|
-
| `'hana'` | Hana on EVM (`io.havah.hana`), Hana on ICON (`hana`), Hana on Sui, Hana on Stellar… |
|
|
18
|
-
| `'phantom'` | Phantom on Solana (`phantom`), Phantom on EVM (`app.phantom`) |
|
|
19
|
-
| `'metamask'` | MetaMask on EVM (`io.metamask`), Injective MetaMask connector |
|
|
20
|
-
| `'xverse'` | Xverse on Bitcoin (`xverse`), Xverse on Stacks (`XverseProviders.BitcoinProvider`) |
|
|
21
|
-
|
|
22
|
-
For the full list of known wallet brands and a runtime-discovery snippet (paste-in component that lists every connector available in your app), see [`../reference/wallet-brands.md`](../reference/wallet-brands.md).
|
|
23
|
-
|
|
24
|
-
Earlier identifiers in the array are **higher-priority per chain**. The runner uses **fallback-on-failure**:
|
|
25
|
-
|
|
26
|
-
- If a chain matches `'hana'` but the Hana popup is denied / errors out, the runner tries the next identifier's connector on that same chain (e.g. Phantom).
|
|
27
|
-
- If a chain succeeds on the first identifier, later identifiers for that chain are **silently skipped** — only one popup per chain on the happy path.
|
|
28
|
-
- A chain ends up in `result.failed` only when **every** matched identifier's connector has failed.
|
|
29
|
-
|
|
30
|
-
```typescript
|
|
31
|
-
// Prefer Hana on every chain it covers; fall back to Phantom either
|
|
32
|
-
// because Hana isn't available (e.g. Solana) OR because its popup failed.
|
|
33
|
-
const { run } = useBatchConnect({ connectors: ['hana', 'phantom'] });
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
`onProgress` fires per attempt — a chain can emit a `failure` event followed by a `success` event when fallback kicks in. The final outcome lives in `result`.
|
|
37
|
-
|
|
38
|
-
To target a specific connector (not a brand), bypass this API and use `useXConnectors({ xChainType }).find(c => c.id === '...')` + `useXConnect` directly.
|
|
39
|
-
|
|
40
|
-
---
|
|
41
|
-
|
|
42
|
-
## `useBatchConnect`
|
|
43
|
-
|
|
44
|
-
Connect every chain where one of the supplied identifiers matches an installed connector. Sequential, never throws, dedupes concurrent runs.
|
|
45
|
-
|
|
46
|
-
```tsx
|
|
47
|
-
'use client';
|
|
48
|
-
|
|
49
|
-
import { useBatchConnect } from '@sodax/wallet-sdk-react';
|
|
50
|
-
|
|
51
|
-
export function ConnectAllHana() {
|
|
52
|
-
const { run, status, result, reset } = useBatchConnect({
|
|
53
|
-
connectors: ['hana'],
|
|
54
|
-
skipConnected: true, // skip chains already connected at run() time
|
|
55
|
-
onProgress: event => {
|
|
56
|
-
console.log(`[batch] ${event.chainType}: ${event.outcome}`);
|
|
57
|
-
},
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
return (
|
|
61
|
-
<div>
|
|
62
|
-
<button onClick={run} disabled={status === 'running'}>
|
|
63
|
-
{status === 'running' ? 'Connecting all Hana chains…' : 'Connect with Hana'}
|
|
64
|
-
</button>
|
|
65
|
-
{status === 'done' && result && (
|
|
66
|
-
<div>
|
|
67
|
-
<p>{result.successful.length} connected</p>
|
|
68
|
-
<p>{result.failed.length} failed</p>
|
|
69
|
-
<p>{result.skipped.length} skipped</p>
|
|
70
|
-
<button onClick={reset}>Reset</button>
|
|
71
|
-
</div>
|
|
72
|
-
)}
|
|
73
|
-
</div>
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
### Options
|
|
79
|
-
|
|
80
|
-
| Field | Type | Effect |
|
|
81
|
-
|-------|------|--------|
|
|
82
|
-
| `connectors` | `readonly string[]` | Identifiers (required, non-empty) |
|
|
83
|
-
| `skipConnected` | `boolean` | Skip chains already holding an account at `run()` time. Default `false` |
|
|
84
|
-
| `onProgress` | `(event) => void` | Per-target progress event |
|
|
85
|
-
|
|
86
|
-
### Result shape
|
|
87
|
-
|
|
88
|
-
```typescript
|
|
89
|
-
type BatchConnectResult = {
|
|
90
|
-
successful: ChainType[];
|
|
91
|
-
failed: Array<{ chainType: ChainType; error: Error }>;
|
|
92
|
-
skipped: ChainType[];
|
|
93
|
-
};
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
`run()` returns `Promise<BatchConnectResult>` — never rejects.
|
|
97
|
-
|
|
98
|
-
---
|
|
99
|
-
|
|
100
|
-
## `useBatchDisconnect`
|
|
101
|
-
|
|
102
|
-
Mirror of `useBatchConnect` for disconnect:
|
|
103
|
-
|
|
104
|
-
```tsx
|
|
105
|
-
import { useBatchDisconnect } from '@sodax/wallet-sdk-react';
|
|
106
|
-
|
|
107
|
-
function DisconnectAll() {
|
|
108
|
-
const { run, status } = useBatchDisconnect();
|
|
109
|
-
// No `connectors` → disconnect every currently-connected chain regardless of wallet
|
|
110
|
-
|
|
111
|
-
return (
|
|
112
|
-
<button onClick={run} disabled={status === 'running'}>
|
|
113
|
-
{status === 'running' ? 'Disconnecting…' : 'Disconnect all chains'}
|
|
114
|
-
</button>
|
|
115
|
-
);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
function DisconnectHanaOnly() {
|
|
119
|
-
const { run } = useBatchDisconnect({ connectors: ['hana'] });
|
|
120
|
-
// Only disconnect chains whose CURRENTLY ACTIVE connector matches 'hana'
|
|
121
|
-
return <button onClick={run}>Disconnect Hana</button>;
|
|
122
|
-
}
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
### Scope difference vs `useBatchConnect`
|
|
126
|
-
|
|
127
|
-
`useBatchConnect.connectors` is **mandatory** — it picks **which connector** to use on each chain.
|
|
128
|
-
|
|
129
|
-
`useBatchDisconnect.connectors` is **optional** — it filters **which chains** to disconnect by checking the **currently active** connector against the identifiers. Chains where the active connector doesn't match are left untouched.
|
|
130
|
-
|
|
131
|
-
```typescript
|
|
132
|
-
useBatchDisconnect(); // Disconnect every chain
|
|
133
|
-
useBatchDisconnect({ connectors: ['hana'] }); // Disconnect chains where Hana is active
|
|
134
|
-
useBatchDisconnect({ connectors: ['hana', 'xverse'] }); // Hana OR Xverse
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
---
|
|
138
|
-
|
|
139
|
-
## Wallet install detection — `useIsWalletInstalled`
|
|
140
|
-
|
|
141
|
-
Companion read hook that uses the same identifier matching to detect whether a wallet brand is installed for any (or specific) chain.
|
|
142
|
-
|
|
143
|
-
```typescript
|
|
144
|
-
import { useIsWalletInstalled } from '@sodax/wallet-sdk-react';
|
|
145
|
-
|
|
146
|
-
// True if any Hana variant is installed across any enabled chain
|
|
147
|
-
const isHanaInstalled = useIsWalletInstalled({ connectors: ['hana'] });
|
|
148
|
-
|
|
149
|
-
// True if any wallet is installed for the given chain
|
|
150
|
-
const hasBitcoinWallet = useIsWalletInstalled({ chainType: 'BITCOIN' });
|
|
151
|
-
|
|
152
|
-
// AND filter — Hana specifically on EVM
|
|
153
|
-
const hanaOnEvm = useIsWalletInstalled({ connectors: ['hana'], chainType: 'EVM' });
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
The options union enforces at the type level that **at least one** of `connectors` / `chainType` is present. Use this hook to gate an `useBatchConnect` button:
|
|
157
|
-
|
|
158
|
-
```tsx
|
|
159
|
-
const isInstalled = useIsWalletInstalled({ connectors: ['hana'] });
|
|
160
|
-
const { run } = useBatchConnect({ connectors: ['hana'] });
|
|
161
|
-
|
|
162
|
-
return isInstalled ? (
|
|
163
|
-
<button onClick={run}>Connect Hana</button>
|
|
164
|
-
) : (
|
|
165
|
-
<a href="https://hana-wallet.com">Install Hana</a>
|
|
166
|
-
);
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
---
|
|
170
|
-
|
|
171
|
-
## Status lifecycle and concurrency
|
|
172
|
-
|
|
173
|
-
| `status` | Meaning |
|
|
174
|
-
|----------|---------|
|
|
175
|
-
| `'idle'` | Initial / after `reset()` |
|
|
176
|
-
| `'running'` | A `run()` is in flight |
|
|
177
|
-
| `'done'` | The last `run()` settled — `result` is populated |
|
|
178
|
-
|
|
179
|
-
**Concurrent `run()` calls are deduped** — the second call returns the existing in-flight promise.
|
|
180
|
-
|
|
181
|
-
**`reset()` does NOT abort an in-flight batch** — there is no cancellation signal. It only clears the observable `status` / `result`. Typical usage: call `reset()` only after `status === 'done'`.
|
|
182
|
-
|
|
183
|
-
---
|
|
184
|
-
|
|
185
|
-
## Progress events
|
|
186
|
-
|
|
187
|
-
```typescript
|
|
188
|
-
const { run } = useBatchConnect({
|
|
189
|
-
connectors: ['hana'],
|
|
190
|
-
onProgress: event => {
|
|
191
|
-
switch (event.outcome) {
|
|
192
|
-
case 'success': toast(`✓ Connected ${event.chainType}`); break;
|
|
193
|
-
case 'failure': toast.error(`✗ ${event.chainType}: ${event.error.message}`); break;
|
|
194
|
-
case 'skipped': toast(`⏭ ${event.chainType} already connected`); break;
|
|
195
|
-
}
|
|
196
|
-
},
|
|
197
|
-
});
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
`onProgress` is read from a ref each time, so passing an inline arrow function is safe.
|
|
201
|
-
|
|
202
|
-
---
|
|
203
|
-
|
|
204
|
-
## When to use the lower-level hooks instead
|
|
205
|
-
|
|
206
|
-
Reach for `useXConnect` / `useXDisconnect` directly when:
|
|
207
|
-
|
|
208
|
-
- **You need a specific connector**, not a brand.
|
|
209
|
-
- **Order is user-driven**, not registry-driven.
|
|
210
|
-
- **You need cancellation** — batch hooks have no cancel signal.
|
|
211
|
-
- **You only have one chain to operate on**.
|
|
212
|
-
|
|
213
|
-
---
|
|
214
|
-
|
|
215
|
-
## Verification
|
|
216
|
-
|
|
217
|
-
```bash
|
|
218
|
-
# 1. Type check
|
|
219
|
-
pnpm checkTs
|
|
220
|
-
|
|
221
|
-
# 2. Manual — install Hana, click batch connect button, confirm sequential popups across chains
|
|
222
|
-
# 3. Manual — confirm result.failed contains chains where Hana isn't available
|
|
223
|
-
```
|
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
# Recipe: Bridge to `@sodax/sdk`
|
|
2
|
-
|
|
3
|
-
Pass the user's connected wallet to `@sodax/sdk` calls — `useWalletProvider` returns a typed `IXxxWalletProvider` ready to plug into any SDK method.
|
|
4
|
-
|
|
5
|
-
**Depends on:** [`setup.md`](./setup.md), one of [`connect-button.md`](./connect-button.md) / [`multi-chain-modal.md`](./multi-chain-modal.md)
|
|
6
|
-
|
|
7
|
-
**Requires also:** `pnpm add @sodax/sdk` — this package does not depend on `@sodax/sdk`. Install it separately in the consumer app.
|
|
8
|
-
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
## Hooks used
|
|
12
|
-
|
|
13
|
-
| Hook | Purpose |
|
|
14
|
-
|------|---------|
|
|
15
|
-
| `useWalletProvider({ xChainId })` | Typed `IXxxWalletProvider` (chain-narrowed by chain id) |
|
|
16
|
-
| `useWalletProvider({ xChainType })` | Family-level provider (same shape for every chain id in family) |
|
|
17
|
-
| `useXAccount({ xChainId })` | Read connected address |
|
|
18
|
-
| `useXService({ xChainType })` | Lower-level — chain `XService` instance for advanced reads |
|
|
19
|
-
|
|
20
|
-
Pass either `xChainId` (a `SpokeChainKey`) or `xChainType` (a `ChainType`), never both.
|
|
21
|
-
|
|
22
|
-
---
|
|
23
|
-
|
|
24
|
-
## Pattern — drive an SDK swap from a connected wallet
|
|
25
|
-
|
|
26
|
-
```tsx
|
|
27
|
-
import { useWalletProvider, useXAccount } from '@sodax/wallet-sdk-react';
|
|
28
|
-
import { Sodax, ChainKeys } from '@sodax/sdk';
|
|
29
|
-
import type { CreateIntentParams } from '@sodax/sdk';
|
|
30
|
-
|
|
31
|
-
const sodax = new Sodax(); // or hold one in context / pass via @sodax/dapp-kit
|
|
32
|
-
|
|
33
|
-
export function SwapButton({ params }: { params: CreateIntentParams<typeof ChainKeys.BSC_MAINNET> }) {
|
|
34
|
-
const walletProvider = useWalletProvider({ xChainId: ChainKeys.BSC_MAINNET });
|
|
35
|
-
const account = useXAccount({ xChainId: ChainKeys.BSC_MAINNET });
|
|
36
|
-
|
|
37
|
-
const handleSwap = async () => {
|
|
38
|
-
if (!walletProvider) return;
|
|
39
|
-
const result = await sodax.swaps.swap({
|
|
40
|
-
params,
|
|
41
|
-
walletProvider, // typed as IEvmWalletProvider — must match BSC src chain
|
|
42
|
-
});
|
|
43
|
-
if (!result.ok) {
|
|
44
|
-
console.error('swap failed:', result.error);
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
console.log('swap submitted:', result.value);
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
return (
|
|
51
|
-
<button onClick={handleSwap} disabled={!walletProvider || !account.address}>
|
|
52
|
-
Swap
|
|
53
|
-
</button>
|
|
54
|
-
);
|
|
55
|
-
}
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
The pattern works for every SDK feature service:
|
|
59
|
-
|
|
60
|
-
```typescript
|
|
61
|
-
sodax.swaps.swap({ params, walletProvider });
|
|
62
|
-
sodax.bridge.bridge({ params, walletProvider });
|
|
63
|
-
sodax.moneyMarket.supply({ params, walletProvider });
|
|
64
|
-
sodax.staking.stake({ params, walletProvider });
|
|
65
|
-
sodax.dex.deposit({ params, walletProvider });
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
---
|
|
69
|
-
|
|
70
|
-
## TypeScript narrowing
|
|
71
|
-
|
|
72
|
-
```typescript
|
|
73
|
-
import { useWalletProvider } from '@sodax/wallet-sdk-react';
|
|
74
|
-
import { ChainKeys } from '@sodax/types';
|
|
75
|
-
|
|
76
|
-
// By chain id — narrowest typing
|
|
77
|
-
const evm = useWalletProvider({ xChainId: ChainKeys.BSC_MAINNET });
|
|
78
|
-
// evm: IEvmWalletProvider | undefined
|
|
79
|
-
|
|
80
|
-
const sol = useWalletProvider({ xChainId: ChainKeys.SOLANA_MAINNET });
|
|
81
|
-
// sol: ISolanaWalletProvider | undefined
|
|
82
|
-
|
|
83
|
-
// By chain type — family-level (one wagmi connection covers all EVM chains)
|
|
84
|
-
const evmFamily = useWalletProvider({ xChainType: 'EVM' });
|
|
85
|
-
// evmFamily: IEvmWalletProvider | undefined
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
Passing the wrong wallet provider type to an SDK call (e.g. `ISolanaWalletProvider` to a `srcChainKey: BSC_MAINNET` swap) is a **compile error**.
|
|
89
|
-
|
|
90
|
-
---
|
|
91
|
-
|
|
92
|
-
## EVM — single connection across all networks
|
|
93
|
-
|
|
94
|
-
`useWalletProvider({ xChainId: ChainKeys.BSC_MAINNET })` and `useWalletProvider({ xChainId: ChainKeys.ARBITRUM_MAINNET })` return the **same** `EvmWalletProvider` instance — wagmi maintains one connection across all configured EVM networks. To switch the **active** EVM network:
|
|
95
|
-
|
|
96
|
-
```tsx
|
|
97
|
-
import { useEvmSwitchChain } from '@sodax/wallet-sdk-react';
|
|
98
|
-
import { ChainKeys } from '@sodax/types';
|
|
99
|
-
|
|
100
|
-
const { isWrongChain, handleSwitchChain } = useEvmSwitchChain({
|
|
101
|
-
xChainId: ChainKeys.BSC_MAINNET,
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
if (isWrongChain) {
|
|
105
|
-
return <button onClick={handleSwitchChain}>Switch to BSC</button>;
|
|
106
|
-
}
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
---
|
|
110
|
-
|
|
111
|
-
## Disabled chains return `undefined`
|
|
112
|
-
|
|
113
|
-
If `xChainType` resolves to a chain not enabled in `SodaxWalletProvider` config, `useWalletProvider` returns `undefined` and logs a one-time warning:
|
|
114
|
-
|
|
115
|
-
```
|
|
116
|
-
[useWalletProvider] chain "BITCOIN" is not enabled in SodaxWalletProvider config.chains — returning undefined
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
Always null-check before passing to an SDK call.
|
|
120
|
-
|
|
121
|
-
---
|
|
122
|
-
|
|
123
|
-
## Raw transactions — skip the bridge
|
|
124
|
-
|
|
125
|
-
If you only need unsigned transaction data (manual relay, gas estimation, external signing), pass `raw: true` to the SDK and skip `walletProvider`:
|
|
126
|
-
|
|
127
|
-
```typescript
|
|
128
|
-
const result = await sodax.swaps.createIntent({
|
|
129
|
-
params,
|
|
130
|
-
raw: true, // walletProvider must be absent — compile error if passed
|
|
131
|
-
});
|
|
132
|
-
// result.value.tx is an EvmRawTransaction
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
---
|
|
136
|
-
|
|
137
|
-
## Server / scripts — use `@sodax/wallet-sdk-core` directly
|
|
138
|
-
|
|
139
|
-
For Node.js scripts / bots, skip wallet-sdk-react entirely:
|
|
140
|
-
|
|
141
|
-
```typescript
|
|
142
|
-
import { Sodax } from '@sodax/sdk';
|
|
143
|
-
import { EvmWalletProvider } from '@sodax/wallet-sdk-core';
|
|
144
|
-
import { ChainKeys } from '@sodax/types';
|
|
145
|
-
|
|
146
|
-
const sodax = new Sodax();
|
|
147
|
-
const walletProvider = new EvmWalletProvider({
|
|
148
|
-
privateKey: process.env.PRIVATE_KEY!,
|
|
149
|
-
chainId: ChainKeys.BSC_MAINNET,
|
|
150
|
-
rpcUrl: 'https://bsc-dataseed.binance.org',
|
|
151
|
-
});
|
|
152
|
-
const result = await sodax.swaps.swap({ params, walletProvider });
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
---
|
|
156
|
-
|
|
157
|
-
## Verification
|
|
158
|
-
|
|
159
|
-
```bash
|
|
160
|
-
# 1. Type check
|
|
161
|
-
pnpm checkTs
|
|
162
|
-
|
|
163
|
-
# 2. Manual — connect wallet, click SDK action button, confirm transaction prompts in wallet
|
|
164
|
-
```
|
|
@@ -1,254 +0,0 @@
|
|
|
1
|
-
# Recipe: Chain & Wallet Detection
|
|
2
|
-
|
|
3
|
-
Aggregate views over the wallet store — what's enabled, what's connected, what's installed. Use these hooks to render chain pickers, "manage connections" panels, install CTAs, and hydration-safe UIs.
|
|
4
|
-
|
|
5
|
-
**Depends on:** [`setup.md`](./setup.md)
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## Hooks at a glance
|
|
10
|
-
|
|
11
|
-
| Hook | Purpose |
|
|
12
|
-
|------|---------|
|
|
13
|
-
| `useEnabledChains()` | List chain types mounted in `SodaxWalletProvider` config |
|
|
14
|
-
| `useChainGroups({ order? })` | One row per enabled chain (with display + connection metadata) — for chain pickers |
|
|
15
|
-
| `useConnectedChains({ order? })` | List of currently-connected chains + hydration `status` — for "manage connections" |
|
|
16
|
-
| `useIsWalletInstalled({ connectors?, chainType? })` | Cross-chain install check — for gating "Install X" CTA |
|
|
17
|
-
|
|
18
|
-
EVM **collapses to a single row** — wagmi maintains one connection across every configured EVM network.
|
|
19
|
-
|
|
20
|
-
---
|
|
21
|
-
|
|
22
|
-
## `useEnabledChains` — what's mounted
|
|
23
|
-
|
|
24
|
-
```tsx
|
|
25
|
-
import { useEnabledChains } from '@sodax/wallet-sdk-react';
|
|
26
|
-
|
|
27
|
-
const enabled = useEnabledChains();
|
|
28
|
-
// e.g. ['EVM', 'SOLANA', 'BITCOIN']
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
Returns the slot keys in `SodaxWalletConfig` (`config.EVM`, `config.SOLANA`, …), not which chains have a wallet connected. Use cases:
|
|
32
|
-
|
|
33
|
-
- Render only chain rows the dApp opted into.
|
|
34
|
-
- Cross-reference with `useXConnections()` to compute "of N enabled chains, M are connected".
|
|
35
|
-
- Drive `<Tabs>` / `<Select>` UIs without hard-coding a list.
|
|
36
|
-
|
|
37
|
-
`useChainGroups` and `useConnectedChains` already filter by `useEnabledChains` internally — reach for it directly only when you need the raw list.
|
|
38
|
-
|
|
39
|
-
---
|
|
40
|
-
|
|
41
|
-
## `useChainGroups` — chain picker model
|
|
42
|
-
|
|
43
|
-
One `ChainGroup` per enabled chain type, with display metadata + connection status. Designed for the "select a chain" step in modals.
|
|
44
|
-
|
|
45
|
-
```tsx
|
|
46
|
-
'use client';
|
|
47
|
-
|
|
48
|
-
import { useChainGroups } from '@sodax/wallet-sdk-react';
|
|
49
|
-
import type { ChainType } from '@sodax/types';
|
|
50
|
-
|
|
51
|
-
export function ChainPicker({ onPick }: { onPick: (c: ChainType) => void }) {
|
|
52
|
-
const groups = useChainGroups({ order: ['EVM', 'SOLANA', 'BITCOIN', 'ICON'] });
|
|
53
|
-
|
|
54
|
-
return (
|
|
55
|
-
<ul>
|
|
56
|
-
{groups.map((group) => (
|
|
57
|
-
<li key={group.chainType}>
|
|
58
|
-
<button onClick={() => onPick(group.chainType)}>
|
|
59
|
-
{group.iconUrl && <img src={group.iconUrl} alt="" width={24} height={24} />}
|
|
60
|
-
<span>{group.displayName}</span>
|
|
61
|
-
{group.isConnected && <span className="badge">Connected</span>}
|
|
62
|
-
</button>
|
|
63
|
-
</li>
|
|
64
|
-
))}
|
|
65
|
-
</ul>
|
|
66
|
-
);
|
|
67
|
-
}
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
### `ChainGroup` shape
|
|
71
|
-
|
|
72
|
-
| Field | Type | Source |
|
|
73
|
-
|-------|------|--------|
|
|
74
|
-
| `chainType` | `ChainType` | The slot key (`'EVM'`, `'SOLANA'`, …) |
|
|
75
|
-
| `chainIds` | `readonly SpokeChainKey[]` | All chain keys sharing this `chainType` (e.g. all 12 EVM `ChainKeys.*` for `'EVM'`) |
|
|
76
|
-
| `displayName` | `string` | Default per-chain display name |
|
|
77
|
-
| `iconUrl` | `string \| undefined` | `undefined` = SDK doesn't ship one — provide your own |
|
|
78
|
-
| `isConnected` | `boolean` | `true` when an account is connected for this chain |
|
|
79
|
-
| `account` | `XAccount \| undefined` | Connected account |
|
|
80
|
-
| `connectorId` | `string \| undefined` | Active connector id when connected |
|
|
81
|
-
|
|
82
|
-
EVM's `chainIds` lists every configured EVM `ChainKey`, but the group itself is **one row**. Per-network switching belongs to [`switch-chain.md`](./switch-chain.md), not a separate group.
|
|
83
|
-
|
|
84
|
-
---
|
|
85
|
-
|
|
86
|
-
## `useConnectedChains` — connected list with hydration gate
|
|
87
|
-
|
|
88
|
-
Returns one entry per **currently-connected** chain (skipping the rest), with enriched connector metadata for "manage connections" UIs and status badges.
|
|
89
|
-
|
|
90
|
-
```tsx
|
|
91
|
-
'use client';
|
|
92
|
-
|
|
93
|
-
import { useConnectedChains } from '@sodax/wallet-sdk-react';
|
|
94
|
-
|
|
95
|
-
export function ConnectionList() {
|
|
96
|
-
const { chains, total, status } = useConnectedChains();
|
|
97
|
-
|
|
98
|
-
if (status === 'loading') return <Skeleton />;
|
|
99
|
-
if (total === 0) return <ConnectCta />;
|
|
100
|
-
|
|
101
|
-
return (
|
|
102
|
-
<ul>
|
|
103
|
-
{chains.map((chain) => (
|
|
104
|
-
<li key={chain.chainType}>
|
|
105
|
-
{chain.connectorIcon && <img src={chain.connectorIcon} alt="" />}
|
|
106
|
-
<span>{chain.connectorName ?? chain.connectorId}</span>
|
|
107
|
-
<code>{chain.account.address}</code>
|
|
108
|
-
</li>
|
|
109
|
-
))}
|
|
110
|
-
</ul>
|
|
111
|
-
);
|
|
112
|
-
}
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
### `ConnectedChain` shape
|
|
116
|
-
|
|
117
|
-
| Field | Type | Notes |
|
|
118
|
-
|-------|------|-------|
|
|
119
|
-
| `chainType` | `ChainType` | |
|
|
120
|
-
| `account` | `XAccount` | Always populated (only included when address is non-empty) |
|
|
121
|
-
| `connectorId` | `string` | The persisted active connector |
|
|
122
|
-
| `connectorName` | `string \| undefined` | Looked up in `xConnectorsByChain` |
|
|
123
|
-
| `connectorIcon` | `string \| undefined` | |
|
|
124
|
-
|
|
125
|
-
### Result shape
|
|
126
|
-
|
|
127
|
-
```typescript
|
|
128
|
-
type UseConnectedChainsResult = {
|
|
129
|
-
chains: ConnectedChain[];
|
|
130
|
-
total: number;
|
|
131
|
-
status: 'loading' | 'ready';
|
|
132
|
-
};
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
---
|
|
136
|
-
|
|
137
|
-
## `useIsWalletInstalled` — install detection
|
|
138
|
-
|
|
139
|
-
Read hook for gating "Connect" buttons on actual installation. Same identifier matching as `useBatchConnect` — see [`batch-operations.md`](./batch-operations.md).
|
|
140
|
-
|
|
141
|
-
```typescript
|
|
142
|
-
import { useIsWalletInstalled } from '@sodax/wallet-sdk-react';
|
|
143
|
-
|
|
144
|
-
// True if any Hana variant is installed across all enabled chains
|
|
145
|
-
const hasHana = useIsWalletInstalled({ connectors: ['hana'] });
|
|
146
|
-
|
|
147
|
-
// True if any wallet is installed for Bitcoin specifically
|
|
148
|
-
const hasBitcoinWallet = useIsWalletInstalled({ chainType: 'BITCOIN' });
|
|
149
|
-
|
|
150
|
-
// AND filter — Hana specifically on EVM
|
|
151
|
-
const hanaOnEvm = useIsWalletInstalled({ connectors: ['hana'], chainType: 'EVM' });
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
The type union enforces at compile time that **at least one of `connectors` / `chainType`** is present — `useIsWalletInstalled({})` is a type error. `connectors: []` is explicit "match nothing" — returns `false`.
|
|
155
|
-
|
|
156
|
-
---
|
|
157
|
-
|
|
158
|
-
## Hydration status — gate reload flicker
|
|
159
|
-
|
|
160
|
-
`useConnectedChains` exposes `status: 'loading' | 'ready'`. Use it to avoid the "Connect wallet" → "Connected" flash on page reload while Zustand rehydrates from `localStorage`:
|
|
161
|
-
|
|
162
|
-
```tsx
|
|
163
|
-
const { chains, status } = useConnectedChains();
|
|
164
|
-
|
|
165
|
-
// ❌ Flicker — `chains` is empty for one render before hydration completes
|
|
166
|
-
return chains.length >= 1 ? <Connected /> : <ConnectCta />;
|
|
167
|
-
|
|
168
|
-
// ✅ No flicker — wait for hydration before deciding
|
|
169
|
-
return status === 'loading'
|
|
170
|
-
? <Skeleton />
|
|
171
|
-
: chains.length >= 1 ? <Connected /> : <ConnectCta />;
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
For first-paint correctness in SSR (Next.js), prefer `useConnectedChains.status` over an ad-hoc `useEffect(() => setMounted(true), [])` pattern — it's the official hydration signal.
|
|
175
|
-
|
|
176
|
-
`useChainGroups` does **not** expose this flag — its outputs are stable across hydration because connection-status fields (`isConnected`, `account`) start as `false` / `undefined` and gain values atomically when the persist middleware finishes.
|
|
177
|
-
|
|
178
|
-
---
|
|
179
|
-
|
|
180
|
-
## Display ordering
|
|
181
|
-
|
|
182
|
-
Both `useChainGroups` and `useConnectedChains` accept an `order?: readonly ChainType[]`:
|
|
183
|
-
|
|
184
|
-
1. Chains in the array render in array order.
|
|
185
|
-
2. Chains **not** in the array fall to the bottom, sorted alphabetically.
|
|
186
|
-
3. Without `order`:
|
|
187
|
-
- `useChainGroups` follows the order of slots in `walletConfig` (config object key order).
|
|
188
|
-
- `useConnectedChains` follows `ChainTypeArr` from `@sodax/types` — stable across reloads.
|
|
189
|
-
|
|
190
|
-
```typescript
|
|
191
|
-
const groups = useChainGroups({ order: ['EVM', 'ICON'] });
|
|
192
|
-
// EVM → ICON → (alphabetical: BITCOIN, INJECTIVE, NEAR, SOLANA, STACKS, STELLAR, SUI)
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
For UIs that must not reflow on reload (header chain list, navigation), always pass `order`.
|
|
196
|
-
|
|
197
|
-
---
|
|
198
|
-
|
|
199
|
-
## Common patterns
|
|
200
|
-
|
|
201
|
-
### Pattern 1 — header connected-account chip
|
|
202
|
-
|
|
203
|
-
```tsx
|
|
204
|
-
function HeaderAccountChip() {
|
|
205
|
-
const { chains, status } = useConnectedChains();
|
|
206
|
-
|
|
207
|
-
if (status === 'loading') return null;
|
|
208
|
-
if (chains.length === 0) return <ConnectButton />;
|
|
209
|
-
|
|
210
|
-
return <span>{chains.length} chain{chains.length !== 1 ? 's' : ''} connected</span>;
|
|
211
|
-
}
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
### Pattern 2 — "install Hana" CTA when not installed
|
|
215
|
-
|
|
216
|
-
```tsx
|
|
217
|
-
function HanaCta() {
|
|
218
|
-
const installed = useIsWalletInstalled({ connectors: ['hana'] });
|
|
219
|
-
return installed ? null : (
|
|
220
|
-
<a href="https://hana-wallet.com" target="_blank" rel="noreferrer">
|
|
221
|
-
Install Hana Wallet
|
|
222
|
-
</a>
|
|
223
|
-
);
|
|
224
|
-
}
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
### Pattern 3 — chain selector in swap form
|
|
228
|
-
|
|
229
|
-
```tsx
|
|
230
|
-
function ChainSelector({ value, onChange }: { value: ChainType; onChange: (c: ChainType) => void }) {
|
|
231
|
-
const groups = useChainGroups({ order: ['EVM', 'SOLANA', 'SUI'] });
|
|
232
|
-
return (
|
|
233
|
-
<select value={value} onChange={(e) => onChange(e.target.value as ChainType)}>
|
|
234
|
-
{groups.map((g) => (
|
|
235
|
-
<option key={g.chainType} value={g.chainType}>
|
|
236
|
-
{g.displayName} {g.isConnected ? '✓' : ''}
|
|
237
|
-
</option>
|
|
238
|
-
))}
|
|
239
|
-
</select>
|
|
240
|
-
);
|
|
241
|
-
}
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
---
|
|
245
|
-
|
|
246
|
-
## Verification
|
|
247
|
-
|
|
248
|
-
```bash
|
|
249
|
-
# 1. Type check
|
|
250
|
-
pnpm checkTs
|
|
251
|
-
|
|
252
|
-
# 2. Manual — load page on slow 3G, confirm no Connect→Connected flash on reload
|
|
253
|
-
# 3. Manual — uninstall Hana, confirm CTA appears; reinstall, confirm CTA disappears
|
|
254
|
-
```
|